From 37cdacef83cfdc4998f1b84c235dd34b31119b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Urba=C5=84czyk?= Date: Sun, 2 Apr 2023 15:36:23 +0200 Subject: [PATCH 1/4] feat: new trait behaviour --- package-lock.json | 17 +- package.json | 2 +- src/custom-operations/anonymous-naming.ts | 2 +- src/custom-operations/apply-traits.ts | 83 ++++++-- src/custom-operations/index.ts | 20 +- .../custom-operations/apply-traits-v2.spec.ts | 189 ++++++++++++++++++ .../custom-operations/apply-traits-v3.spec.ts | 159 +++++++++++++++ 7 files changed, 443 insertions(+), 29 deletions(-) create mode 100644 test/custom-operations/apply-traits-v2.spec.ts create mode 100644 test/custom-operations/apply-traits-v3.spec.ts diff --git a/package-lock.json b/package-lock.json index 428a09cd4..07a781109 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.0", "license": "Apache-2.0", "dependencies": { - "@asyncapi/specs": "^3.1.0", + "@asyncapi/specs": "^5.0.0-next-major-spec.18", "@openapi-contrib/openapi-schema-to-json-schema": "^3.2.0", "@stoplight/json-ref-resolver": "^3.1.4", "@stoplight/spectral-core": "^1.14.1", @@ -72,8 +72,12 @@ } }, "node_modules/@asyncapi/specs": { - "version": "3.1.0", - "license": "Apache-2.0" + "version": "5.0.0-next-major-spec.18", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.0.0-next-major-spec.18.tgz", + "integrity": "sha512-WwJxQnAb734a/qe6shxe8GXhf7VgDJCIwex2ejjiYkzNU+xjM+GSpaZYkMBID7WNQtoo8+6rnSE63I9qd6gzUQ==", + "dependencies": { + "@types/json-schema": "^7.0.11" + } }, "node_modules/@babel/code-frame": { "version": "7.16.7", @@ -17694,7 +17698,12 @@ } }, "@asyncapi/specs": { - "version": "3.1.0" + "version": "5.0.0-next-major-spec.18", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.0.0-next-major-spec.18.tgz", + "integrity": "sha512-WwJxQnAb734a/qe6shxe8GXhf7VgDJCIwex2ejjiYkzNU+xjM+GSpaZYkMBID7WNQtoo8+6rnSE63I9qd6gzUQ==", + "requires": { + "@types/json-schema": "^7.0.11" + } }, "@babel/code-frame": { "version": "7.16.7", diff --git a/package.json b/package.json index 74c5ead08..fab1c59c1 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "release": "semantic-release" }, "dependencies": { - "@asyncapi/specs": "^3.1.0", + "@asyncapi/specs": "^5.0.0-next-major-spec.18", "@openapi-contrib/openapi-schema-to-json-schema": "^3.2.0", "@stoplight/json-ref-resolver": "^3.1.4", "@stoplight/spectral-core": "^1.14.1", diff --git a/src/custom-operations/anonymous-naming.ts b/src/custom-operations/anonymous-naming.ts index fa94171ab..d29b9e917 100644 --- a/src/custom-operations/anonymous-naming.ts +++ b/src/custom-operations/anonymous-naming.ts @@ -29,7 +29,7 @@ function assignNameToAnonymousMessages(document: AsyncAPIDocumentInterface) { let anonymousMessageCounter = 0; document.messages().forEach(message => { if (message.name() === undefined && message.extensions().get(xParserMessageName)?.value() === undefined) { - setExtension(xParserMessageName, ``, message); + setExtension(xParserMessageName, message.id() || ``, message); } }); } diff --git a/src/custom-operations/apply-traits.ts b/src/custom-operations/apply-traits.ts index cc6191857..990aca601 100644 --- a/src/custom-operations/apply-traits.ts +++ b/src/custom-operations/apply-traits.ts @@ -2,7 +2,7 @@ import { JSONPath } from 'jsonpath-plus'; import { mergePatch } from '../utils'; -import type { v2 } from '../spec-types'; +import type { v2, v3 } from '../spec-types'; const v2TraitPaths = [ // operations @@ -17,42 +17,87 @@ const v2TraitPaths = [ ]; export function applyTraitsV2(asyncapi: v2.AsyncAPIObject) { - applyAllTraits(asyncapi, v2TraitPaths); + applyAllTraitsV2(asyncapi, v2TraitPaths); +} + +function applyAllTraitsV2(asyncapi: Record, paths: string[]) { + const visited: Set = new Set(); + paths.forEach(path => { + JSONPath({ + path, + json: asyncapi, + resultType: 'value', + callback(value) { + if (visited.has(value)) { + return; + } + visited.add(value); + applyTraitsToObjectV2(value); + }, + }); + }); +} + +function applyTraitsToObjectV2(value: Record) { + if (Array.isArray(value.traits)) { + for (const trait of value.traits) { + for (const key in trait) { + value[String(key)] = mergePatch(value[String(key)], trait[String(key)]); + } + } + } } const v3TraitPaths = [ // operations - '$.channels.*.[publish,subscribe]', - '$.components.channels.*.[publish,subscribe]', + '$.operations.*', + '$.components.operations.*', // messages - '$.channels.*.[publish,subscribe].message', - '$.channels.*.[publish,subscribe].message.oneOf.*', - '$.components.channels.*.[publish,subscribe].message', - '$.components.channels.*.[publish,subscribe].message.oneOf.*', + '$.channels.*.messages.*', + '$.operations.*.messages.*', + '$.components.channels.*.messages.*', + '$.components.operations.*.messages.*', '$.components.messages.*', ]; -export function applyTraitsV3(asyncapi: v2.AsyncAPIObject) { // TODO: Change type when we will have implemented types for v3 - applyAllTraits(asyncapi, v3TraitPaths); +export function applyTraitsV3(asyncapi: v3.AsyncAPIObject) { + applyAllTraitsV3(asyncapi, v3TraitPaths); } -function applyAllTraits(asyncapi: Record, paths: string[]) { +function applyAllTraitsV3(asyncapi: Record, paths: string[]) { + const visited: Set = new Set(); paths.forEach(path => { JSONPath({ path, json: asyncapi, resultType: 'value', - callback(value) { applyTraits(value); }, + callback(value) { + if (visited.has(value)) { + return; + } + visited.add(value); + applyTraitsToObjectV3(value); + }, }); }); } -function applyTraits(value: Record) { - if (Array.isArray(value.traits)) { - for (const trait of value.traits) { - for (const key in trait) { - value[String(key)] = mergePatch(value[String(key)], trait[String(key)]); - } +function applyTraitsToObjectV3(value: Record) { + if (!Array.isArray(value.traits)) { + return; + } + + // shallow copy of object + const copy = { ...value }; + // reset the object but preserve the reference + for (const key in value) { + delete value[key]; + } + + // merge root object at the end + for (const trait of [...copy.traits as any[], copy]) { + for (const key in trait) { + value[String(key)] = mergePatch(value[String(key)], trait[String(key)]); } } -} +} \ No newline at end of file diff --git a/src/custom-operations/index.ts b/src/custom-operations/index.ts index 6f64035d2..058675a99 100644 --- a/src/custom-operations/index.ts +++ b/src/custom-operations/index.ts @@ -1,4 +1,4 @@ -import { applyTraitsV2 } from './apply-traits'; +import { applyTraitsV2, applyTraitsV3 } from './apply-traits'; import { checkCircularRefs } from './check-circular-refs'; import { parseSchemasV2 } from './parse-schema'; import { anonymousNaming } from './anonymous-naming'; @@ -7,18 +7,17 @@ import type { Parser } from '../parser'; import type { ParseOptions } from '../parse'; import type { AsyncAPIDocumentInterface } from '../models'; import type { DetailedAsyncAPI } from '../types'; -import type { v2 } from '../spec-types'; +import type { v2, v3 } from '../spec-types'; export async function customOperations(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, options: ParseOptions): Promise { switch (detailed.semver.major) { case 2: return operationsV2(parser, document, detailed, options); - // case 3: return operationsV3(parser, document, detailed, options); + case 3: return operationsV3(parser, document, detailed, options); } } async function operationsV2(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, options: ParseOptions): Promise { checkCircularRefs(document); - anonymousNaming(document); if (options.applyTraits) { applyTraitsV2(detailed.parsed as v2.AsyncAPIObject); @@ -26,5 +25,18 @@ async function operationsV2(parser: Parser, document: AsyncAPIDocumentInterface, if (options.parseSchemas) { await parseSchemasV2(parser, detailed); } + anonymousNaming(document); } +async function operationsV3(parser: Parser, document: AsyncAPIDocumentInterface, detailed: DetailedAsyncAPI, options: ParseOptions): Promise { + checkCircularRefs(document); + + if (options.applyTraits) { + applyTraitsV3(detailed.parsed as v3.AsyncAPIObject); + } + // TODO: Support schema parsing in v3 + // if (options.parseSchemas) { + // await parseSchemasV2(parser, detailed); + // } + anonymousNaming(document); +} diff --git a/test/custom-operations/apply-traits-v2.spec.ts b/test/custom-operations/apply-traits-v2.spec.ts new file mode 100644 index 000000000..4eb2800fc --- /dev/null +++ b/test/custom-operations/apply-traits-v2.spec.ts @@ -0,0 +1,189 @@ +import { AsyncAPIDocumentV2 } from '../../src/models'; +import { Parser } from '../../src/parser'; + +import type { v2 } from '../../src/spec-types'; + +describe('custom operations - apply traits v3', function() { + const parser = new Parser(); + + it('should apply traits to operations', async function() { + const documentRaw = { + asyncapi: '2.0.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + channels: { + channel: { + publish: { + operationId: 'publishId', + traits: [ + { + operationId: 'anotherPubId', + description: 'some description' + }, + { + description: 'another description' + } + ] + }, + subscribe: { + operationId: 'subscribeId', + traits: [ + { + operationId: 'anotherSubId', + description: 'some description' + }, + { + description: 'another description' + } + ] + } + } + } + }; + const { document, diagnostics } = await parser.parse(documentRaw); + + const v2Document = document as AsyncAPIDocumentV2; + expect(v2Document).toBeInstanceOf(AsyncAPIDocumentV2); + expect(diagnostics.length > 0).toEqual(true); + + const publish = v2Document?.json()?.channels?.channel?.publish; + delete publish?.traits; + expect(publish).toEqual({ operationId: 'anotherPubId', description: 'another description' }); + + const subscribe = v2Document?.json()?.channels?.channel?.subscribe; + delete subscribe?.traits; + expect(subscribe).toEqual({ operationId: 'anotherSubId', description: 'another description' }); + }); + + it('should apply traits to messages', async function() { + const documentRaw = { + asyncapi: '2.4.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + channels: { + channel: { + publish: { + operationId: 'operationId', + message: { + messageId: 'messageId', + traits: [ + { + messageId: 'anotherMessageId1', + description: 'some description' + }, + { + description: 'another description' + } + ] + } + }, + subscribe: { + message: { + oneOf: [ + { + messageId: 'messageId', + traits: [ + { + messageId: 'anotherMessageId2', + description: 'some description' + }, + { + description: 'another description' + } + ] + }, + { + messageId: 'messageId', + traits: [ + { + messageId: 'anotherId', + description: 'some description' + }, + { + description: 'another description' + }, + { + messageId: 'anotherMessageId3', + description: 'simple description' + } + ] + } + ], + } + } + } + } + }; + const { document, diagnostics } = await parser.parse(documentRaw); + + const v2Document = document as AsyncAPIDocumentV2; + expect(v2Document).toBeInstanceOf(AsyncAPIDocumentV2); + expect(diagnostics.length > 0).toEqual(true); + + const message = v2Document?.json()?.channels?.channel?.publish?.message; + delete (message as v2.MessageObject)?.traits; + expect(message).toEqual({ messageId: 'anotherMessageId1', description: 'another description', 'x-parser-message-name': 'anotherMessageId1' }); + + const messageOneOf1 = (v2Document?.json()?.channels?.channel?.subscribe?.message as { oneOf: Array }).oneOf[0]; + delete messageOneOf1?.traits; + expect(messageOneOf1).toEqual({ messageId: 'anotherMessageId2', description: 'another description', 'x-parser-message-name': 'anotherMessageId2' }); + + const messageOneOf2 = (v2Document?.json()?.channels?.channel?.subscribe?.message as { oneOf: Array }).oneOf[1]; + delete messageOneOf2?.traits; + expect(messageOneOf2).toEqual({ messageId: 'anotherMessageId3', description: 'simple description', 'x-parser-message-name': 'anotherMessageId3' }); + }); + + it('should preserve this same references', async function() { + const documentRaw = { + asyncapi: '2.4.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + channels: { + channel: { + publish: { + operationId: 'publishId', + message: { + $ref: '#/components/messages/message', + } + }, + } + }, + components: { + messages: { + message: { + messageId: 'messageId', + traits: [ + { + messageId: 'anotherId', + description: 'some description' + }, + { + description: 'another description' + }, + { + messageId: 'anotherMessageId3', + description: 'simple description' + } + ] + } + } + } + }; + const { document, diagnostics } = await parser.parse(documentRaw); + + const v2Document = document as AsyncAPIDocumentV2; + expect(v2Document).toBeInstanceOf(AsyncAPIDocumentV2); + expect(diagnostics.length > 0).toEqual(true); + + const message = v2Document?.json()?.channels?.channel?.publish?.message; + delete (message as v2.MessageObject)?.traits; + expect(message).toEqual({ messageId: 'anotherMessageId3', description: 'simple description', 'x-parser-message-name': 'anotherMessageId3' }); + expect(message === v2Document?.json()?.components?.messages?.message).toEqual(true); + }); +}); diff --git a/test/custom-operations/apply-traits-v3.spec.ts b/test/custom-operations/apply-traits-v3.spec.ts new file mode 100644 index 000000000..eebed6a89 --- /dev/null +++ b/test/custom-operations/apply-traits-v3.spec.ts @@ -0,0 +1,159 @@ +import { AsyncAPIDocumentV3 } from '../../src/models'; +import { Parser } from '../../src/parser'; + +import type { v3 } from '../../src/spec-types'; + +describe('custom operations - apply traits v3', function() { + const parser = new Parser(); + + it('should apply traits to operations', async function() { + const documentRaw = { + asyncapi: '3.0.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + operations: { + someOperation1: { + traits: [ + { + description: 'some description' + }, + { + description: 'another description' + } + ] + }, + someOperation2: { + description: 'root description', + traits: [ + { + description: 'some description' + }, + { + description: 'another description' + } + ] + } + } + }; + const { document } = await parser.parse(documentRaw); + + const v3Document = document as AsyncAPIDocumentV3; + expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3); + + const someOperation1 = v3Document?.json()?.operations?.someOperation1; + delete someOperation1?.traits; + expect(someOperation1).toEqual({ description: 'another description' }); + + const someOperation2 = v3Document?.json()?.operations?.someOperation2; + delete someOperation2?.traits; + expect(someOperation2).toEqual({ description: 'root description' }); + }); + + it('should apply traits to messages (channels)', async function() { + const documentRaw = { + asyncapi: '3.0.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + channels: { + someChannel1: { + messages: [ + { + traits: [ + { + messageId: 'traitMessageId', + description: 'some description' + }, + { + description: 'another description' + } + ] + } + ] + }, + someChannel2: { + messages: [ + { + messageId: 'rootMessageId', + description: 'root description', + traits: [ + { + messageId: 'traitMessageId', + description: 'some description' + }, + { + description: 'another description' + } + ] + } + ] + } + } + }; + const { document } = await parser.parse(documentRaw); + + const v3Document = document as AsyncAPIDocumentV3; + expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3); + + const message1 = v3Document?.json()?.channels?.someChannel1?.messages?.[0]; + delete (message1 as v3.MessageObject)?.traits; + expect(message1).toEqual({ messageId: 'traitMessageId', description: 'another description', 'x-parser-message-name': 'traitMessageId' }); + + const message2 = v3Document?.json()?.channels?.someChannel2?.messages?.[0]; + delete (message2 as v3.MessageObject)?.traits; + expect(message2).toEqual({ messageId: 'rootMessageId', description: 'root description', 'x-parser-message-name': 'rootMessageId' }); + }); + + it('should apply traits to messages (components)', async function() { + const documentRaw = { + asyncapi: '3.0.0', + info: { + title: 'Valid AsyncApi document', + version: '1.0', + }, + components: { + messages: { + someMessage1: { + traits: [ + { + messageId: 'traitMessageId', + description: 'some description' + }, + { + description: 'another description' + } + ] + }, + someMessage2: { + messageId: 'rootMessageId', + description: 'root description', + traits: [ + { + messageId: 'traitMessageId', + description: 'some description' + }, + { + description: 'another description' + } + ] + } + } + } + }; + const { document } = await parser.parse(documentRaw); + + const v3Document = document as AsyncAPIDocumentV3; + expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3); + + const message1 = v3Document?.json()?.components?.messages?.someMessage1; + delete (message1 as v3.MessageObject)?.traits; + expect(message1).toEqual({ messageId: 'traitMessageId', description: 'another description', 'x-parser-message-name': 'traitMessageId' }); + + const message2 = v3Document?.json()?.components?.messages?.someMessage2; + delete (message2 as v3.MessageObject)?.traits; + expect(message2).toEqual({ messageId: 'rootMessageId', description: 'root description', 'x-parser-message-name': 'rootMessageId' }); + }); +}); From 1fc5b33c1982c5b2e0aaf35a75eb8d7de09fbbca Mon Sep 17 00:00:00 2001 From: Sergio Moya <1083296+smoya@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:13:39 +0200 Subject: [PATCH 2/4] fix document creation + update specs --- package-lock.json | 35 ++++++------------- package.json | 4 +-- src/document.ts | 4 +-- .../custom-operations/apply-traits-v3.spec.ts | 4 +-- test/document.spec.ts | 13 ++++--- 5 files changed, 25 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index c20e7f141..b2253ad39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.1.0-next-major-spec.1", "license": "Apache-2.0", "dependencies": { - "@asyncapi/specs": "^v5.0.0-next-major-spec.21", + "@asyncapi/specs": "^6.0.0-next-major-spec.2", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", @@ -79,9 +79,9 @@ } }, "node_modules/@asyncapi/specs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", - "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "version": "6.0.0-next-major-spec.2", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.2.tgz", + "integrity": "sha512-kKFdVZkWnkmcNWADg3lt9AExM9jGGpAIrUcIYSbHNSSQgjZC6rr0urqctAaXXtBVrJNBI9LNG/Us70R/0MOLbw==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -10958,15 +10958,6 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -11125,9 +11116,9 @@ } }, "@asyncapi/specs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", - "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "version": "6.0.0-next-major-spec.2", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.2.tgz", + "integrity": "sha512-kKFdVZkWnkmcNWADg3lt9AExM9jGGpAIrUcIYSbHNSSQgjZC6rr0urqctAaXXtBVrJNBI9LNG/Us70R/0MOLbw==", "requires": { "@types/json-schema": "^7.0.11" } @@ -19500,15 +19491,9 @@ } }, "wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true - }, - "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index ebf421a40..5fcc5f9c2 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "prepublishOnly": "npm run generate:assets" }, "dependencies": { - "@asyncapi/specs": "^v5.0.0-next-major-spec.21", + "@asyncapi/specs": "^6.0.0-next-major-spec.2", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", @@ -95,4 +95,4 @@ "browserify-shim": { "node-fetch": "global:fetch" } -} \ No newline at end of file +} diff --git a/src/document.ts b/src/document.ts index 16f00f813..bc6003f67 100644 --- a/src/document.ts +++ b/src/document.ts @@ -17,8 +17,8 @@ export function createAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocu switch (asyncapi.semver.major) { case 2: return new AsyncAPIDocumentV2(asyncapi.parsed as v2.AsyncAPIObject, { asyncapi, pointer: '/' }); - // case 3: - // return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' }); + case 3: + return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' }); default: throw new Error(`Unsupported AsyncAPI version: ${asyncapi.semver.version}`); } diff --git a/test/custom-operations/apply-traits-v3.spec.ts b/test/custom-operations/apply-traits-v3.spec.ts index eebed6a89..e59e86b16 100644 --- a/test/custom-operations/apply-traits-v3.spec.ts +++ b/test/custom-operations/apply-traits-v3.spec.ts @@ -37,10 +37,10 @@ describe('custom operations - apply traits v3', function() { } } }; - const { document } = await parser.parse(documentRaw); - + const { document, diagnostics } = await parser.parse(documentRaw); const v3Document = document as AsyncAPIDocumentV3; expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3); + // expect(diagnostics.length > 0).toEqual(true); const someOperation1 = v3Document?.json()?.operations?.someOperation1; delete someOperation1?.traits; diff --git a/test/document.spec.ts b/test/document.spec.ts index 3fd419bcb..38d57aec9 100644 --- a/test/document.spec.ts +++ b/test/document.spec.ts @@ -1,5 +1,5 @@ import { xParserApiVersion, xParserSpecParsed, xParserSpecStringified } from '../src/constants'; -import { BaseModel, AsyncAPIDocumentV2 } from '../src/models'; +import { AsyncAPIDocumentInterface, BaseModel, AsyncAPIDocumentV2, AsyncAPIDocumentV3 } from '../src/models'; import { convertToOldAPI } from '../src/old-api'; import { Parser } from '../src/parser'; import { @@ -17,12 +17,17 @@ describe('utils', function() { class Model extends BaseModel {} describe('createAsyncAPIDocument()', function() { - it('should create a valid document from v2.0.0', function() { - const doc = { asyncapi: '2.0.0' }; + const cases = [ + [2, AsyncAPIDocumentV2], + [3, AsyncAPIDocumentV3], + ]; + + test.each(cases)('should create a valid document from a v%p.0.0 source', (majorVersion, expected) => { + const doc = { asyncapi: `${majorVersion }.0.0` }; const detailed = createDetailedAsyncAPI(doc as any, doc as any); const d = createAsyncAPIDocument(detailed); expect(d.version()).toEqual(doc.asyncapi); - expect(d).toBeInstanceOf(AsyncAPIDocumentV2); + expect(d).toBeInstanceOf(expected); }); it('should fail trying to create a document from a non supported spec version', function() { From 1fb2fa82ec6dee4e1857968a49c92e8e1f119eaf Mon Sep 17 00:00:00 2001 From: Sergio Moya <1083296+smoya@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:16:20 +0200 Subject: [PATCH 3/4] fix lint issues --- test/custom-operations/apply-traits-v3.spec.ts | 1 - test/document.spec.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/custom-operations/apply-traits-v3.spec.ts b/test/custom-operations/apply-traits-v3.spec.ts index e59e86b16..19f627c57 100644 --- a/test/custom-operations/apply-traits-v3.spec.ts +++ b/test/custom-operations/apply-traits-v3.spec.ts @@ -40,7 +40,6 @@ describe('custom operations - apply traits v3', function() { const { document, diagnostics } = await parser.parse(documentRaw); const v3Document = document as AsyncAPIDocumentV3; expect(v3Document).toBeInstanceOf(AsyncAPIDocumentV3); - // expect(diagnostics.length > 0).toEqual(true); const someOperation1 = v3Document?.json()?.operations?.someOperation1; delete someOperation1?.traits; diff --git a/test/document.spec.ts b/test/document.spec.ts index 38d57aec9..64b963218 100644 --- a/test/document.spec.ts +++ b/test/document.spec.ts @@ -23,7 +23,7 @@ describe('utils', function() { ]; test.each(cases)('should create a valid document from a v%p.0.0 source', (majorVersion, expected) => { - const doc = { asyncapi: `${majorVersion }.0.0` }; + const doc = { asyncapi: `${majorVersion}.0.0` }; const detailed = createDetailedAsyncAPI(doc as any, doc as any); const d = createAsyncAPIDocument(detailed); expect(d.version()).toEqual(doc.asyncapi); From 0232747d12118fdf25b5fbfe1cb407f1bbb14c72 Mon Sep 17 00:00:00 2001 From: Sergio Moya <1083296+smoya@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:27:35 +0200 Subject: [PATCH 4/4] fix hinting --- src/document.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/document.ts b/src/document.ts index bc6003f67..887fd02b9 100644 --- a/src/document.ts +++ b/src/document.ts @@ -11,14 +11,14 @@ import { import type { AsyncAPIDocumentInterface } from './models'; import type { OldAsyncAPIDocument } from './old-api'; import type { DetailedAsyncAPI, AsyncAPIObject } from './types'; -import { v2 } from 'spec-types'; +import { v2, v3 } from 'spec-types'; export function createAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface { switch (asyncapi.semver.major) { case 2: return new AsyncAPIDocumentV2(asyncapi.parsed as v2.AsyncAPIObject, { asyncapi, pointer: '/' }); case 3: - return new AsyncAPIDocumentV3(asyncapi.parsed, { asyncapi, pointer: '/' }); + return new AsyncAPIDocumentV3(asyncapi.parsed as v3.AsyncAPIObject, { asyncapi, pointer: '/' }); default: throw new Error(`Unsupported AsyncAPI version: ${asyncapi.semver.version}`); }