Skip to content

Commit

Permalink
feat: add isOldAsyncAPIDocument helper (#687)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Dec 13, 2022
1 parent 8abd7c0 commit ef6f1f5
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ In AsyncAPI Initiative we support below custom schema parsers. To install them,
The parser uses custom extensions to define additional information about the spec. Each has a different purpose but all of them are there to make it much easier to work with the AsyncAPI document. These extensions are prefixed with `x-parser-`. The following extensions are used:

- `x-parser-spec-parsed` is used to specify if the AsyncAPI document is already parsed by the parser. Property `x-parser-spec-parsed` is added to the root of the document with the `true` value.
- `x-parser-api-version` is used to specify which version of the [Parser-API](https://github.com/asyncapi/parser-api) the parsed AsyncAPI document uses. Property `x-parser-api-version` is added to the root of the document with the `1` value if the parsed document uses [Parser-API](https://github.com/asyncapi/parser-api) in the `v1` version or `0` if document uses old `parser-js` API.
- `x-parser-message-name` is used to specify the name of the message if it is not provided. For messages without names, the parser generates anonymous names. Property `x-parser-message-name` is added to a message object with a value that follows this pattern: `<anonymous-message-${number}>`. This value is returned by `message.id()` (`message.uid()` in the [old API](#convert-to-the-old-api)) when regular `name` property is not present.
- `x-parser-original-payload` holds the original payload of the message. You can use different formats for payloads with the AsyncAPI documents and the parser converts them to. For example, it converts payload described with Avro schema to AsyncAPI schema. The original payload is preserved in the extension.
- [`x-parser-circular`](#circular-references).
Expand Down
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;
}
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
11 changes: 10 additions & 1 deletion src/old-api/converter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { AsyncAPIDocument } from './asyncapi';
import { xParserOriginalPayload, xParserOriginalSchemaFormat, xParserOriginalTraits, xParserMessageParsed } from '../constants';
import { xParserApiVersion, xParserOriginalPayload, xParserOriginalSchemaFormat, xParserOriginalTraits, xParserMessageParsed } from '../constants';
import { createAsyncAPIDocument } from '../document';
import { copy } from '../stringify';
import { getDefaultSchemaFormat } from '../schema-parser';
import { createDetailedAsyncAPI, setExtension } from '../utils';

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

Expand All @@ -11,10 +13,17 @@ export function convertToOldAPI(newDocument: AsyncAPIDocumentInterface): AsyncAP

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

return document;
}

export function convertToNewAPI(oldDocument: AsyncAPIDocument): AsyncAPIDocumentInterface {
const data = copy(oldDocument.json());
const detailed = createDetailedAsyncAPI(data);
return createAsyncAPIDocument(detailed);
}

function handleMessages(document: AsyncAPIDocument) {
const defaultSchemaFormat = getDefaultSchemaFormat(document.version());
for (const message of document.allMessages().values()) {
Expand Down
2 changes: 1 addition & 1 deletion src/old-api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { convertToOldAPI } from './converter';
export { convertToOldAPI, convertToNewAPI } from './converter';

export { AsyncAPIDocument as OldAsyncAPIDocument } from './asyncapi';
export { Base as OldBase } from './base';
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
Loading

0 comments on commit ef6f1f5

Please sign in to comment.