Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add isOldAsyncAPIDocument helper #687

Merged
merged 3 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import specs from '@asyncapi/specs';

export const xParserSpecParsed = 'x-parser-spec-parsed';
export const xParserSpecStringified = 'x-parser-spec-stringified';
export const xParserApiVersion = 'x-parser-api-version';

export const xParserMessageName = 'x-parser-message-name';
export const xParserMessageParsed = 'x-parser-message-parsed';
Expand Down
22 changes: 21 additions & 1 deletion src/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { createDetailedAsyncAPI } from './utils';
import {
xParserSpecParsed,
xParserSpecStringified,
xParserApiVersion,
} from './constants';

import type { AsyncAPIDocumentInterface } from './models';
import type { OldAsyncAPIDocument } from './old-api';
import type { DetailedAsyncAPI, AsyncAPIObject } from './types';

export function createAsyncAPIDocument(asyncapi: DetailedAsyncAPI): AsyncAPIDocumentInterface {
Expand All @@ -32,7 +34,25 @@ export function toAsyncAPIDocument(maybeDoc: unknown): AsyncAPIDocumentInterface
}

export function isAsyncAPIDocument(maybeDoc: unknown): maybeDoc is AsyncAPIDocumentInterface {
return maybeDoc instanceof AsyncAPIDocumentV2 || maybeDoc instanceof AsyncAPIDocumentV3;
if (!maybeDoc) {
return false;
}
if (maybeDoc instanceof AsyncAPIDocumentV2 || maybeDoc instanceof AsyncAPIDocumentV3) {
return true;
}
if (maybeDoc && typeof (maybeDoc as AsyncAPIDocumentInterface).json === 'function') {
const versionOfParserAPI = (maybeDoc as AsyncAPIDocumentInterface).json()[xParserApiVersion];
return versionOfParserAPI === 1;
}
jonaslagoni marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

export function isOldAsyncAPIDocument(maybeDoc: unknown): maybeDoc is OldAsyncAPIDocument {
if (maybeDoc && typeof (maybeDoc as OldAsyncAPIDocument).json === 'function') {
const versionOfParserAPI = (maybeDoc as OldAsyncAPIDocument).json()[xParserApiVersion];
return versionOfParserAPI === undefined || versionOfParserAPI === 0;
}
return false;
}

export function isParsedDocument(maybeDoc: unknown): maybeDoc is AsyncAPIObject {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export * from './models';
export { Parser };
export { stringify, unstringify } from './stringify';
export { fromURL, fromFile } from './from';
export { createAsyncAPIDocument, toAsyncAPIDocument, isAsyncAPIDocument } from './document';
export { createAsyncAPIDocument, toAsyncAPIDocument, isAsyncAPIDocument, isOldAsyncAPIDocument } from './document';
export { DiagnosticSeverity };

export * from './old-api';
Expand Down
4 changes: 3 additions & 1 deletion src/old-api/converter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AsyncAPIDocument } from './asyncapi';
import { xParserOriginalPayload, xParserOriginalSchemaFormat, xParserOriginalTraits, xParserMessageParsed } from '../constants';
import { xParserApiVersion, xParserOriginalPayload, xParserOriginalSchemaFormat, xParserOriginalTraits, xParserMessageParsed } from '../constants';
import { copy } from '../stringify';
import { getDefaultSchemaFormat } from '../schema-parser';
import { setExtension } from '../utils';

import type { AsyncAPIDocumentInterface } from '../models/asyncapi';

Expand All @@ -11,6 +12,7 @@ export function convertToOldAPI(newDocument: AsyncAPIDocumentInterface): AsyncAP

handleMessages(document);
handleOperations(document);
setExtension(xParserApiVersion, 0, document as any);

return document;
}
Expand Down
3 changes: 2 additions & 1 deletion src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { copy } from './stringify';
import { createAsyncAPIDocument } from './document';
import { createDetailedAsyncAPI, mergePatch, setExtension, createUncaghtDiagnostic } from './utils';

import { xParserSpecParsed } from './constants';
import { xParserSpecParsed, xParserApiVersion } from './constants';

import type { Spectral, Document, RulesetFunctionContext } from '@stoplight/spectral-core';
import type { Parser } from './parser';
Expand Down Expand Up @@ -61,6 +61,7 @@ export async function parse(parser: Parser, spectral: Spectral, asyncapi: Input,
const detailed = createDetailedAsyncAPI(validatedDoc, asyncapi as DetailedAsyncAPI['input'], options.source);
const document = createAsyncAPIDocument(detailed);
setExtension(xParserSpecParsed, true, document);
setExtension(xParserApiVersion, 1, document);
await customOperations(parser, document, detailed, inventory, options);

return {
Expand Down
53 changes: 52 additions & 1 deletion test/document.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { xParserSpecParsed, xParserSpecStringified } from '../src/constants';
import { xParserApiVersion, xParserSpecParsed, xParserSpecStringified } from '../src/constants';
import { BaseModel, AsyncAPIDocumentV2 } from '../src/models';
import { convertToOldAPI } from '../src/old-api';
import { Parser } from '../src/parser';
import {
createAsyncAPIDocument,
toAsyncAPIDocument,
isAsyncAPIDocument,
isParsedDocument,
isStringifiedDocument,
isOldAsyncAPIDocument,
} from '../src/document';
import { createDetailedAsyncAPI } from '../src/utils';

describe('utils', function() {
const parser = new Parser();
class Model extends BaseModel {}

describe('createAsyncAPIDocument()', function() {
Expand Down Expand Up @@ -98,6 +102,53 @@ describe('utils', function() {
const detailed = createDetailedAsyncAPI(doc as any, doc as any);
expect(isAsyncAPIDocument(createAsyncAPIDocument(detailed))).toEqual(true);
});

it('AsyncAPIDocument instance should be AsyncAPI document', function() {
const doc = { asyncapi: '2.0.0' };
const detailed = createDetailedAsyncAPI(doc as any, doc as any);
expect(isAsyncAPIDocument(createAsyncAPIDocument(detailed))).toEqual(true);
});

it('document with the x-parser-api-version extension set to 1 should be AsyncAPI document', async function() {
expect(isAsyncAPIDocument({ json() { return { [xParserApiVersion]: 1 }; } })).toEqual(true);
});

it('document with the x-parser-api-version extension set to 0 should not be AsyncAPI document', async function() {
expect(isAsyncAPIDocument({ json() { return { [xParserApiVersion]: 0 }; } })).toEqual(false);
});

it('document without the x-parser-api-version extension should not be AsyncAPI document', async function() {
expect(isAsyncAPIDocument({ json() { return {}; } })).toEqual(false);
});
});

describe('isOldAsyncAPIDocument()', function() {
it('OldAsyncAPIDocument instance should be old AsyncAPI document', async function() {
const documentRaw = {
asyncapi: '2.0.0',
info: {
title: 'Valid AsyncApi document',
version: '1.0',
},
channels: {}
};
const { document } = await parser.parse(documentRaw);
const oldDocument = convertToOldAPI(document!);

expect(isOldAsyncAPIDocument(oldDocument)).toEqual(true);
});

it('document with the x-parser-api-version extension set to 0 should be old AsyncAPI document', async function() {
expect(isOldAsyncAPIDocument({ json() { return { [xParserApiVersion]: 0 }; } })).toEqual(true);
});

it('document without the x-parser-api-version extension should be old AsyncAPI document', async function() {
expect(isOldAsyncAPIDocument({ json() { return {}; } })).toEqual(true);
});

it('document with the x-parser-api-version extension set to 1 should not be old AsyncAPI document', async function() {
expect(isOldAsyncAPIDocument({ json() { return { [xParserApiVersion]: 1 }; } })).toEqual(false);
});
});

describe('isParsedDocument()', function() {
Expand Down
8 changes: 4 additions & 4 deletions test/old-api/asyncapi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,7 @@ describe('AsyncAPIDocument', function() {
const nestedSchemas = fs.readFileSync(source, 'utf8');
const { document } = await parser.parse(nestedSchemas, { source });
const doc = convertToOldAPI(document!);
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': '<anonymous-schema-5>'}},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-spec-stringified': true};
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': '<anonymous-schema-5>'}},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-api-version': 0,'x-parser-spec-stringified': true};

const stringified = AsyncAPIDocument.stringify(doc);
expect(stringified).toEqual(JSON.stringify(output));
Expand All @@ -951,7 +951,7 @@ describe('AsyncAPIDocument', function() {
const nestedSchemas = fs.readFileSync(source, 'utf8');
const { document } = await parser.parse(nestedSchemas, { source });
const doc = convertToOldAPI(document!);
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},deep: {type: 'object',properties: {circular: '$ref:$.components.messages.testMessage.payload'},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-circular': true,'x-parser-spec-stringified': true};
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},deep: {type: 'object',properties: {circular: '$ref:$.components.messages.testMessage.payload'},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-api-version': 0,'x-parser-circular': true,'x-parser-spec-stringified': true};

const stringified = AsyncAPIDocument.stringify(doc);
expect(stringified).toEqual(JSON.stringify(output));
Expand All @@ -975,7 +975,7 @@ describe('AsyncAPIDocument', function() {
const nestedSchemas = fs.readFileSync(source, 'utf8');
const { document } = await parser.parse(nestedSchemas, { source });
const oldDoc = convertToOldAPI(document!);
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': '<anonymous-schema-5>'}},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-spec-stringified': true};
const output = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},test: {type: 'object',properties: {testing: {type: 'string','x-parser-schema-id': '<anonymous-schema-5>'}},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-api-version': 0,'x-parser-spec-stringified': true};
const doc = AsyncAPIDocument.parse(output) as AsyncAPIDocument;
expect(JSON.stringify(doc.json())).toEqual(JSON.stringify(oldDoc.json()));
});
Expand All @@ -997,7 +997,7 @@ describe('AsyncAPIDocument', function() {
});

it('should parse stringified document with circular references', async function() {
const circularOutput = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},deep: {type: 'object',properties: {circular: '$ref:$.components.messages.testMessage.payload'},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-circular': true,'x-parser-spec-stringified': true};
const circularOutput = {asyncapi: '2.0.0',info: {title: 'Test API',version: '1.0.0'},channels: {mychannel: {publish: {message: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-2>'}},'x-parser-schema-id': '<anonymous-schema-1>'},'x-parser-message-name': '<anonymous-message-1>','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.channels.mychannel.publish.message.payload','x-parser-message-parsed': true}}}},components: {messages: {testMessage: {payload: {type: 'object',properties: {name: {type: 'string','x-parser-schema-id': '<anonymous-schema-3>'},deep: {type: 'object',properties: {circular: '$ref:$.components.messages.testMessage.payload'},'x-parser-schema-id': '<anonymous-schema-4>'}},'x-parser-schema-id': 'testSchema'},'x-parser-message-name': 'testMessage','x-parser-original-schema-format': 'application/vnd.aai.asyncapi;version=2.0.0',schemaFormat: 'application/vnd.aai.asyncapi;version=2.0.0','x-parser-original-payload': '$ref:$.components.messages.testMessage.payload','x-parser-message-parsed': true}},schemas: {testSchema: '$ref:$.components.messages.testMessage.payload'}},'x-parser-spec-parsed': true,'x-parser-circular': true,'x-parser-spec-stringified': true,'x-parser-api-version': 0};
const result = AsyncAPIDocument.parse(circularOutput) as AsyncAPIDocument;

expect(result.hasCircular()).toEqual(true);
Expand Down
Loading