Merge pull request #31563 from TobZip/fix-async-api-deref
Add support for AsyncApi v3 reference preservation
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-openapi': patch
|
||||
---
|
||||
|
||||
fix wrong dereferencing for AsyncApi 3 documents
|
||||
@@ -110,6 +110,7 @@ dependabot
|
||||
deps
|
||||
dequeue
|
||||
dequeueing
|
||||
dereferencing
|
||||
deserialization
|
||||
destructured
|
||||
destructuring
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -193,24 +193,14 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/json-schema-ref-parser@npm:^11.0.0":
|
||||
version: 11.9.3
|
||||
resolution: "@apidevtools/json-schema-ref-parser@npm:11.9.3"
|
||||
"@apidevtools/json-schema-ref-parser@npm:^14.0.3, @apidevtools/json-schema-ref-parser@npm:^14.2.1":
|
||||
version: 14.2.1
|
||||
resolution: "@apidevtools/json-schema-ref-parser@npm:14.2.1"
|
||||
dependencies:
|
||||
"@jsdevtools/ono": "npm:^7.1.3"
|
||||
"@types/json-schema": "npm:^7.0.15"
|
||||
js-yaml: "npm:^4.1.0"
|
||||
checksum: 10/3d3618dbb611d1296b99bdee4ff0dde664dad47632d30e0310c6d10de8081f6378ccb58329ea4e03103eca9347d5143671d03f0527b1c3f0916d95f8c09215e2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@apidevtools/json-schema-ref-parser@npm:^14.0.3":
|
||||
version: 14.1.1
|
||||
resolution: "@apidevtools/json-schema-ref-parser@npm:14.1.1"
|
||||
dependencies:
|
||||
"@types/json-schema": "npm:^7.0.15"
|
||||
js-yaml: "npm:^4.1.0"
|
||||
checksum: 10/c4332faf164c19764838e33cd8a7ef7c233ecaf3348e7c4470ef92d0c8c1c7ec2dbb1ce535d569bba52a4b1ef104d3f747da11f7a0f0bafdaee11b54ec826b49
|
||||
peerDependencies:
|
||||
"@types/json-schema": ^7.0.15
|
||||
checksum: 10/c3f6d97c0e885f9543b0654258ee16b2dd75463c8496499563c278089043317f89010e89eb51699c7fb38dfb83cc8592f0b0c4983b764b56789dc3329b25ebfd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5069,7 +5059,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-catalog-backend-module-openapi@workspace:plugins/catalog-backend-module-openapi"
|
||||
dependencies:
|
||||
"@apidevtools/json-schema-ref-parser": "npm:^11.0.0"
|
||||
"@apidevtools/json-schema-ref-parser": "npm:^14.2.1"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/catalog-model": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user