From a3996fdd5bc5e07608ea8bf825ef1352966ef5ab Mon Sep 17 00:00:00 2001 From: Viacheslav Turovskyi Date: Sat, 17 Aug 2024 09:35:15 +0000 Subject: [PATCH] fix: add v2-specific root properties to sorting alg --- src/util.ts | 18 ++++++++- tests/gh-185.yaml | 48 ++++++++++++++++++++++++ tests/lib/index.spec.ts | 82 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 tests/gh-185.yaml diff --git a/src/util.ts b/src/util.ts index 4276312..5f77219 100644 --- a/src/util.ts +++ b/src/util.ts @@ -140,7 +140,7 @@ export async function mergeIntoBaseFile( } // Purely decorative stuff, just to bring the order of the AsyncAPI Document's -// properties into a familiar form. +// root properties into a familiar form. export function orderPropsAccToAsyncAPISpec( inputAsyncAPIObject: any ): AsyncAPIObject { @@ -148,6 +148,8 @@ export function orderPropsAccToAsyncAPISpec( 'asyncapi', 'id', 'info', + 'tags', // v2-specific root property + 'externalDocs', // v2-specific root property 'defaultContentType', 'servers', 'channels', @@ -156,7 +158,21 @@ export function orderPropsAccToAsyncAPISpec( ]; const outputAsyncAPIObject: any = {}; + let i = 0; + + // Making the best guess where root properties that are not specified in the + // AsyncAPI Specification were located in the original AsyncAPI Document + // (inserting them between known root properties.) + // DISCLAIMER: The original order is not guaranteed, it is only an + // extrapolating guess. + for (const key of Object.keys(inputAsyncAPIObject)) { + if (!orderOfPropsAccToAsyncAPISpec.includes(key)) { + orderOfPropsAccToAsyncAPISpec.splice(i, 0, key); + } + i++; + } + // Merging of known AsyncAPI Object root properties in a familiar order. for (const prop of orderOfPropsAccToAsyncAPISpec) { if (inputAsyncAPIObject[`${prop}`]) { outputAsyncAPIObject[`${prop}`] = structuredClone( diff --git a/tests/gh-185.yaml b/tests/gh-185.yaml new file mode 100644 index 0000000..74b7965 --- /dev/null +++ b/tests/gh-185.yaml @@ -0,0 +1,48 @@ +asyncapi: '2.0.0' +x-company-attr-1: attr-value-1 +x-company-attr-2: attr-value-2 +id: 'urn:rpc:example:server' +defaultContentType: application/json + +info: + title: RPC Server Example + description: This example demonstrates how to define an RPC server. + version: '1.0.0' + x-company-version: 1.2.3 + +tags: + - name: my-tag + description: tag description + +channels: + '{queue}': + parameters: + queue: + schema: + type: string + pattern: '^amq\\.gen\\-.+$' + bindings: + amqp: + is: queue + queue: + exclusive: true + subscribe: + operationId: sendSumResult + bindings: + amqp: + ack: true + message: + correlationId: + location: $message.header#/correlation_id + payload: + type: object + properties: + result: + type: number + examples: + - 7 + +servers: + production: + url: rabbitmq.example.org + protocol: amqp diff --git a/tests/lib/index.spec.ts b/tests/lib/index.spec.ts index 29deb7e..4b3ac43 100644 --- a/tests/lib/index.spec.ts +++ b/tests/lib/index.spec.ts @@ -84,7 +84,7 @@ describe('[integration testing] bundler should ', () => { }); test('should be able to bundle specification files in subdirectories and merge them into the base file', async () => { - const object = { + const resultingObject = { asyncapi: '3.0.0', info: { title: 'Streetlights MQTT API', @@ -306,7 +306,85 @@ describe('[integration testing] bundler should ', () => { noValidation: true, }); - expect(document.json()).toMatchObject(object); + expect(document.json()).toMatchObject(resultingObject); + }); + + test('should be able to bundle v2 YAML, leaving `x-` properties intact and sorting root props according to AsyncAPI Spec', async () => { + const resultingObject = { + asyncapi: '2.0.0', + 'x-company-attr-1': 'attr-value-1', + 'x-company-attr-2': 'attr-value-2', + id: 'urn:rpc:example:server', + defaultContentType: 'application/json', + info: { + title: 'RPC Server Example', + description: 'This example demonstrates how to define an RPC server.', + version: '1.0.0', + 'x-company-version': '1.2.3', + }, + tags: [ + { + name: 'my-tag', + description: 'tag description', + }, + ], + servers: { + production: { + url: 'rabbitmq.example.org', + protocol: 'amqp', + }, + }, + channels: { + '{queue}': { + parameters: { + queue: { + schema: { + type: 'string', + pattern: '^amq\\\\.gen\\\\-.+$', + }, + }, + }, + bindings: { + amqp: { + is: 'queue', + queue: { + exclusive: true, + }, + }, + }, + subscribe: { + operationId: 'sendSumResult', + bindings: { + amqp: { + ack: true, + }, + }, + message: { + correlationId: { + location: '$message.header#/correlation_id', + }, + payload: { + type: 'object', + properties: { + result: { + type: 'number', + examples: [7], + }, + }, + }, + }, + }, + }, + }, + }; + + const files = 'tests/gh-185.yaml'; + + const document = await bundle(files, { + noValidation: true, + }); + + expect(document.json()).toMatchObject(resultingObject); }); });