diff --git a/package-lock.json b/package-lock.json index 245480462..97bde9702 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@asyncapi/parser", - "version": "3.0.0-next-major-spec.9", + "version": "3.0.0-next-major-spec.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@asyncapi/parser", - "version": "3.0.0-next-major-spec.9", + "version": "3.0.0-next-major-spec.12", "license": "Apache-2.0", "dependencies": { "@asyncapi/specs": "^6.0.0-next-major-spec.9", diff --git a/package.json b/package.json index 9105a10b0..f0ccfad78 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asyncapi/parser", - "version": "3.0.0-next-major-spec.9", + "version": "3.0.0-next-major-spec.12", "description": "JavaScript AsyncAPI parser.", "bugs": { "url": "https://github.com/asyncapi/parser-js/issues" diff --git a/src/document.ts b/src/document.ts index b202b1c6a..76b0f257d 100644 --- a/src/document.ts +++ b/src/document.ts @@ -1,4 +1,4 @@ -import { AsyncAPIDocumentV2, AsyncAPIDocumentV3 } from './models'; +import { AsyncAPIDocumentV2, AsyncAPIDocumentV3, ParserAPIVersion } from './models'; import { unstringify } from './stringify'; import { createDetailedAsyncAPI } from './utils'; @@ -43,7 +43,7 @@ export function isAsyncAPIDocument(maybeDoc: unknown): maybeDoc is AsyncAPIDocum } if (maybeDoc && typeof (maybeDoc as AsyncAPIDocumentInterface).json === 'function') { const versionOfParserAPI = (maybeDoc as AsyncAPIDocumentInterface).json()[xParserApiVersion]; - return versionOfParserAPI === 2; + return versionOfParserAPI === ParserAPIVersion; } return false; } diff --git a/src/models/utils.ts b/src/models/utils.ts index 2a685d5d8..fe4cf4960 100644 --- a/src/models/utils.ts +++ b/src/models/utils.ts @@ -1,8 +1,12 @@ import type { BaseModel, ModelMetadata } from './base'; import type { DetailedAsyncAPI } from '../types'; +import { SchemaInterface } from './schema'; +import { SchemaTypesToIterate, traverseAsyncApiDocument } from '../iterator'; +import { AsyncAPIDocumentInterface } from './asyncapi'; +import { SchemasInterface } from '../models'; export interface Constructor extends Function { - new (...any: any[]): T; + new(...any: any[]): T; } export type InferModelData = T extends BaseModel ? J : never; @@ -30,3 +34,24 @@ export function objectIdFromJSONPointer(path: string, data: any): string { return data.split('/').pop().replace(/~1/, '/'); } + +export function schemasFromDocument(document: AsyncAPIDocumentInterface, SchemasModel: Constructor, includeComponents: boolean): T { + const jsonInstances: Set = new Set(); + const schemas: Set = new Set(); + + function callback(schema: SchemaInterface) { + // comparing the reference (and not just the value) to the .json() object + if (!jsonInstances.has(schema.json())) { + jsonInstances.add(schema.json()); + schemas.add(schema); // unique schemas + } + } + + let toIterate = Object.values(SchemaTypesToIterate); + if (!includeComponents) { + toIterate = toIterate.filter(s => s !== SchemaTypesToIterate.Components); + } + traverseAsyncApiDocument(document, callback, toIterate); + + return new SchemasModel(Array.from(schemas)); +} diff --git a/src/models/v2/asyncapi.ts b/src/models/v2/asyncapi.ts index c7329c1b6..5cd0e8cbd 100644 --- a/src/models/v2/asyncapi.ts +++ b/src/models/v2/asyncapi.ts @@ -12,8 +12,8 @@ import { SecurityScheme } from './security-scheme'; import { Schemas } from './schemas'; import { extensions } from './mixins'; -import { traverseAsyncApiDocument, SchemaTypesToIterate } from '../../iterator'; import { tilde } from '../../utils'; +import { schemasFromDocument } from '../utils'; import type { AsyncAPIDocumentInterface } from '../asyncapi'; import type { InfoInterface } from '../info'; @@ -27,7 +27,6 @@ import type { OperationInterface } from '../operation'; import type { MessagesInterface } from '../messages'; import type { MessageInterface } from '../message'; import type { SchemasInterface } from '../schemas'; -import type { SchemaInterface } from '../schema'; import type { SecuritySchemesInterface } from '../security-schemes'; import type { ExtensionsInterface } from '../extensions'; @@ -81,7 +80,7 @@ export class AsyncAPIDocument extends BaseModel implements As } schemas(): SchemasInterface { - return this.__schemas(false); + return schemasFromDocument(this, Schemas, false); } securitySchemes(): SecuritySchemesInterface { @@ -130,26 +129,10 @@ export class AsyncAPIDocument extends BaseModel implements As } allSchemas(): SchemasInterface { - return this.__schemas(true); + return schemasFromDocument(this, Schemas, true); } extensions(): ExtensionsInterface { return extensions(this); } - - private __schemas(withComponents: boolean) { - const schemas: Set = new Set(); - function callback(schema: SchemaInterface) { - if (!schemas.has(schema.json())) { - schemas.add(schema); - } - } - - let toIterate = Object.values(SchemaTypesToIterate); - if (!withComponents) { - toIterate = toIterate.filter(s => s !== SchemaTypesToIterate.Components); - } - traverseAsyncApiDocument(this, callback, toIterate); - return new Schemas(Array.from(schemas)); - } } diff --git a/src/models/v3/asyncapi.ts b/src/models/v3/asyncapi.ts index 671616690..756140b0b 100644 --- a/src/models/v3/asyncapi.ts +++ b/src/models/v3/asyncapi.ts @@ -14,7 +14,7 @@ import { Schemas } from './schemas'; import { extensions } from './mixins'; import { tilde } from '../../utils'; -import { SchemaTypesToIterate, traverseAsyncApiDocument } from '../../iterator'; +import { schemasFromDocument } from '../utils'; import type { AsyncAPIDocumentInterface } from '../asyncapi'; import type { InfoInterface } from '../info'; @@ -26,12 +26,12 @@ import type { MessageInterface } from '../message'; import type { ComponentsInterface } from '../components'; import type { SecuritySchemesInterface } from '../security-schemes'; import type { ExtensionsInterface } from '../extensions'; -import type { SchemaInterface } from '../schema'; import type { SchemasInterface } from '../schemas'; +import type { OperationInterface } from '../operation'; +import type { ChannelInterface } from '../channel'; +import type { ServerInterface } from '../server'; + import type { v3 } from '../../spec-types'; -import { OperationInterface } from '../operation'; -import { ChannelInterface } from '../channel'; -import { ServerInterface } from '../server'; export class AsyncAPIDocument extends BaseModel implements AsyncAPIDocumentInterface { version(): string { @@ -89,8 +89,8 @@ export class AsyncAPIDocument extends BaseModel implements As return new Messages(messages); } - schemas() { - return this.__schemas(false); + schemas(): SchemasInterface { + return schemasFromDocument(this, Schemas, false); } securitySchemes(): SecuritySchemesInterface { @@ -138,26 +138,10 @@ export class AsyncAPIDocument extends BaseModel implements As } allSchemas(): SchemasInterface { - return this.__schemas(true); + return schemasFromDocument(this, Schemas, true); } extensions(): ExtensionsInterface { return extensions(this); } - - private __schemas(withComponents: boolean) { - const schemas: Set = new Set(); - function callback(schema: SchemaInterface) { - if (!schemas.has(schema.json())) { - schemas.add(schema); - } - } - - let toIterate = Object.values(SchemaTypesToIterate); - if (!withComponents) { - toIterate = toIterate.filter(s => s !== SchemaTypesToIterate.Components); - } - traverseAsyncApiDocument(this, callback, toIterate); - return new Schemas(Array.from(schemas)); - } } diff --git a/test/document.spec.ts b/test/document.spec.ts index dfa07761d..c9fb0e5c0 100644 --- a/test/document.spec.ts +++ b/test/document.spec.ts @@ -114,8 +114,12 @@ describe('utils', function() { expect(isAsyncAPIDocument(createAsyncAPIDocument(detailed))).toEqual(true); }); - it('document with the x-parser-api-version extension set to 2 should be AsyncAPI document', async function() { - expect(isAsyncAPIDocument({ json() { return { [xParserApiVersion]: 2 }; } })).toEqual(true); + it('document with the x-parser-api-version extension set to 3 should be AsyncAPI document', async function() { + expect(isAsyncAPIDocument({ json() { return { [xParserApiVersion]: 3 }; } })).toEqual(true); + }); + + it('document with the x-parser-api-version extension set to 2 should not be AsyncAPI document', async function() { + expect(isAsyncAPIDocument({ json() { return { [xParserApiVersion]: 2 }; } })).toEqual(false); }); it('document with the x-parser-api-version extension set to 1 should not be AsyncAPI document', async function() { diff --git a/test/models/v2/asyncapi.spec.ts b/test/models/v2/asyncapi.spec.ts index 7f89795ae..3b5146ca9 100644 --- a/test/models/v2/asyncapi.spec.ts +++ b/test/models/v2/asyncapi.spec.ts @@ -308,6 +308,14 @@ describe('AsyncAPIDocument model', function() { const d = new AsyncAPIDocument(doc); expect(d.allSchemas()).toBeInstanceOf(Schemas); }); + + it('should return a collection of schemas (with schemas from components) without duplicates', function() { + const sharedMessage = { payload: {} }; + const doc = serializeInput({ channels: { 'user/signup': { publish: { message: sharedMessage }, subscribe: { message: { oneOf: [{ payload: {} }, {}] } } }, 'user/logout': { publish: { message: { payload: {} } } } }, components: { messages: { aMessage: sharedMessage } } }); + const d = new AsyncAPIDocument(doc); + expect(d.allSchemas()).toBeInstanceOf(Schemas); + expect(d.allSchemas()).toHaveLength(3); + }); }); describe('mixins', function() { diff --git a/test/models/v3/asyncapi.spec.ts b/test/models/v3/asyncapi.spec.ts index c326796cc..85e28e371 100644 --- a/test/models/v3/asyncapi.spec.ts +++ b/test/models/v3/asyncapi.spec.ts @@ -213,6 +213,13 @@ describe('AsyncAPIDocument model', function() { const d = new AsyncAPIDocument(doc); expect(d.allSchemas()).toBeInstanceOf(Schemas); }); + it('should return a collection of schemas (with schemas from components) without duplicates', function() { + const sharedMessage = { payload: {} }; + const doc = serializeInput({ channels: { userSignup: { address: 'user/signup', messages: { someMessage1: { payload: {}}, someMessage2: sharedMessage } } }, components: { messages: { aMessage: sharedMessage } } }); + const d = new AsyncAPIDocument(doc); + expect(d.allSchemas()).toBeInstanceOf(Schemas); + expect(d.allSchemas()).toHaveLength(2); + }); }); describe('.allServers()', function() {