Merge pull request #31563 from TobZip/fix-async-api-deref
Add support for AsyncApi v3 reference preservation
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apidevtools/json-schema-ref-parser": "^11.0.0",
|
||||
"@apidevtools/json-schema-ref-parser": "^14.2.1",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('bundleFileWithRefs', () => {
|
||||
|
||||
expect(result).toEqual(expectedResult.trimStart());
|
||||
});
|
||||
it('should return the bundled asyncapi specification', async () => {
|
||||
it('should return the bundled asyncapi 2.5.0 specification', async () => {
|
||||
const spec = `
|
||||
asyncapi: 2.5.0
|
||||
info:
|
||||
@@ -179,6 +179,202 @@ channels:
|
||||
|
||||
expect(result).toEqual(expectedSchema.trimStart());
|
||||
});
|
||||
|
||||
it('should return the bundled asyncapi 3.0.0 specification with preserved references', async () => {
|
||||
const spec = `
|
||||
asyncapi: 3.0.0
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: AsyncAPI 3.0 Sample
|
||||
description: Sample AsyncAPI 3.0 with operations and replies
|
||||
servers:
|
||||
test:
|
||||
host: api.example.com:5672
|
||||
protocol: kafka
|
||||
channels:
|
||||
userSignup:
|
||||
address: user/signedup
|
||||
servers:
|
||||
- $ref: "#/servers/test"
|
||||
messages:
|
||||
UserSignedUp:
|
||||
$ref: "#/components/messages/UserSignedUp"
|
||||
ServiceUserSignup:
|
||||
$ref: "#/components/messages/ServiceUserSignup"
|
||||
userSignupReply:
|
||||
- $ref: "#/components/channels/userSignupReply"
|
||||
operations:
|
||||
sendUserSignup:
|
||||
action: send
|
||||
channel:
|
||||
$ref: "#/channels/userSignup"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignup/messages/UserSignedUp"
|
||||
reply:
|
||||
channel:
|
||||
$ref: "#/channels/userSignupReply"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignupReply/messages/UserSignedUpReply"
|
||||
sendServiceUserSignup:
|
||||
$ref: "#/components/operations/sendServiceUserSignup"
|
||||
components:
|
||||
channels:
|
||||
userSignupReply:
|
||||
servers:
|
||||
- $ref: "#/servers/test"
|
||||
address: user/signedup/reply
|
||||
messages:
|
||||
UserSignedUpReply:
|
||||
$ref: "#/components/messages/UserSignedUpReply"
|
||||
ServiceUserSignupReply:
|
||||
$ref: "#/components/messages/ServiceUserSignupReply"
|
||||
operations:
|
||||
sendServiceUserSignup:
|
||||
action: send
|
||||
channel:
|
||||
$ref: "#/channels/userSignup"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignup/messages/ServiceUserSignup"
|
||||
reply:
|
||||
channel:
|
||||
$ref: "#/channels/userSignupReply"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignupReply/messages/ServiceUserSignupReply"
|
||||
messages:
|
||||
UserSignedUp:
|
||||
$ref: "./messages/UserSignedUp.yaml"
|
||||
ServiceUserSignup:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
serviceId:
|
||||
type: string
|
||||
UserSignedUpReply:
|
||||
$ref: "./messages/UserSignedUpReply.yaml"
|
||||
ServiceUserSignupReply:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
`;
|
||||
|
||||
const userSignedUpMessage = `
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
`;
|
||||
|
||||
const userSignedUpReplyMessage = `
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
`;
|
||||
|
||||
const expectedBundledSpec = `
|
||||
asyncapi: 3.0.0
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: AsyncAPI 3.0 Sample
|
||||
description: Sample AsyncAPI 3.0 with operations and replies
|
||||
servers:
|
||||
test:
|
||||
host: api.example.com:5672
|
||||
protocol: kafka
|
||||
channels:
|
||||
userSignup:
|
||||
address: user/signedup
|
||||
servers:
|
||||
- $ref: "#/servers/test"
|
||||
messages:
|
||||
UserSignedUp:
|
||||
$ref: "#/components/messages/UserSignedUp"
|
||||
ServiceUserSignup:
|
||||
$ref: "#/components/messages/ServiceUserSignup"
|
||||
userSignupReply:
|
||||
- $ref: "#/components/channels/userSignupReply"
|
||||
operations:
|
||||
sendUserSignup:
|
||||
action: send
|
||||
channel:
|
||||
$ref: "#/channels/userSignup"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignup/messages/UserSignedUp"
|
||||
reply:
|
||||
channel:
|
||||
$ref: "#/channels/userSignupReply"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignupReply/messages/UserSignedUpReply"
|
||||
sendServiceUserSignup:
|
||||
$ref: "#/components/operations/sendServiceUserSignup"
|
||||
components:
|
||||
channels:
|
||||
userSignupReply:
|
||||
servers:
|
||||
- $ref: "#/servers/test"
|
||||
address: user/signedup/reply
|
||||
messages:
|
||||
UserSignedUpReply:
|
||||
$ref: "#/components/messages/UserSignedUpReply"
|
||||
ServiceUserSignupReply:
|
||||
$ref: "#/components/messages/ServiceUserSignupReply"
|
||||
operations:
|
||||
sendServiceUserSignup:
|
||||
action: send
|
||||
channel:
|
||||
$ref: "#/channels/userSignup"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignup/messages/ServiceUserSignup"
|
||||
reply:
|
||||
channel:
|
||||
$ref: "#/channels/userSignupReply"
|
||||
messages:
|
||||
- $ref: "#/channels/userSignupReply/messages/ServiceUserSignupReply"
|
||||
messages:
|
||||
UserSignedUp:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
ServiceUserSignup:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
serviceId:
|
||||
type: string
|
||||
UserSignedUpReply:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
ServiceUserSignupReply:
|
||||
payload:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
`;
|
||||
|
||||
read
|
||||
.mockResolvedValueOnce(userSignedUpMessage)
|
||||
.mockResolvedValueOnce(userSignedUpReplyMessage);
|
||||
|
||||
const result = await bundleFileWithRefs(
|
||||
spec,
|
||||
'https://github.com/owner/repo/blob/main/catalog-info.yaml',
|
||||
read,
|
||||
resolveUrl,
|
||||
);
|
||||
|
||||
expect(read).toHaveBeenCalledTimes(2);
|
||||
expect(result).toEqual(expectedBundledSpec.trimStart());
|
||||
});
|
||||
});
|
||||
|
||||
describe('bundleFileWithRefs - Testing getRelativePath scenarios', () => {
|
||||
|
||||
@@ -34,6 +34,20 @@ export type BundlerRead = (url: string) => Promise<Buffer>;
|
||||
|
||||
export type BundlerResolveUrl = (url: string, base: string) => string;
|
||||
|
||||
// Preserved references paths for AsyncAPI v3 documents
|
||||
const asyncApiV3PreservedPaths = [
|
||||
/#\/channels\/.*\/servers/,
|
||||
/#\/operations\/.*\/channel/,
|
||||
/#\/operations\/.*\/messages/,
|
||||
/#\/operations\/.*\/reply\/channel/,
|
||||
/#\/operations\/.*\/reply\/messages/,
|
||||
/#\/components\/channels\/.*\/servers/,
|
||||
/#\/components\/operations\/.*\/channel/,
|
||||
/#\/components\/operations\/.*\/messages/,
|
||||
/#\/components\/operations\/.*\/reply\/channel/,
|
||||
/#\/components\/operations\/.*\/reply\/messages/,
|
||||
];
|
||||
|
||||
export async function bundleFileWithRefs(
|
||||
fileWithRefs: string,
|
||||
baseUrl: string,
|
||||
@@ -46,7 +60,7 @@ export async function bundleFileWithRefs(
|
||||
return protocol === undefined || protocol === 'file';
|
||||
},
|
||||
read: async file => {
|
||||
const relativePath = path.relative('.', file.url);
|
||||
const relativePath = path.relative('.', file.url).replace(/\\/g, '/');
|
||||
const url = resolveUrl(relativePath, baseUrl);
|
||||
return await read(url);
|
||||
},
|
||||
@@ -61,14 +75,29 @@ export async function bundleFileWithRefs(
|
||||
return await read(url);
|
||||
},
|
||||
};
|
||||
|
||||
const options: ParserOptions = {
|
||||
resolve: {
|
||||
file: fileUrlReaderResolver,
|
||||
http: httpUrlReaderResolver,
|
||||
},
|
||||
};
|
||||
|
||||
const fileObject = parse(fileWithRefs);
|
||||
|
||||
if (fileObject.asyncapi) {
|
||||
const version = parseInt(fileObject.asyncapi, 10);
|
||||
|
||||
if (version === 3) {
|
||||
options.bundle = {
|
||||
excludedPathMatcher: (refPath: string): any => {
|
||||
return asyncApiV3PreservedPaths.some(pattern =>
|
||||
pattern.test(refPath),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const bundledObject = await $RefParser.bundle(fileObject, options);
|
||||
return stringify(bundledObject);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user