diff --git a/package.json b/package.json index a38484c..1c2c20e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@smoya/multi-parser", - "version": "0.1.0", + "version": "2.0.0", "description": "This tool allows to parse AsyncAPI documents and produce a desired interface based on a given Parser-API version", "bugs": { "url": "https://github.com/smoya/multi-parser-js/issues" diff --git a/src/convert.ts b/src/convert.ts new file mode 100644 index 0000000..a03d1a3 --- /dev/null +++ b/src/convert.ts @@ -0,0 +1,35 @@ +import { createAsyncAPIDocument as createAsyncAPIDocumentParserV2 } from 'parserv2'; +import { createAsyncAPIDocument as createAsyncAPIDocumentParserV3 } from 'parserv3'; + +import type { AsyncAPIDocumentInterface as AsyncAPIDocumentInterfaceParserV2 } from 'parserv2'; +import type { AsyncAPIDocumentInterface as AsyncAPIDocumentInterfaceParserV3 } from 'parserv3'; + +import type { DetailedAsyncAPI as DetailedAsyncAPIParserV2 } from 'parserv2/esm/types'; +import type { DetailedAsyncAPI as DetailedAsyncAPIParserV3 } from 'parserv3/esm/types'; + +import { majorParserAPIVersion } from './utils'; + +export type AsyncAPIDocument = AsyncAPIDocumentInterfaceParserV2 | AsyncAPIDocumentInterfaceParserV3; + +export function ConvertDocumentParserAPIVersion(doc: AsyncAPIDocument, toParserAPIVersion: string): AsyncAPIDocument { + if (!doc || !doc.json) return doc; + + const docParserAPI = doc.extensions().get('x-parser-api-version')?.value(); + const docParserAPIMajorVersion: number = docParserAPI ? Number(String(docParserAPI).split('.')[0]) : 0; + const toMajorParserAPIVersion = majorParserAPIVersion(toParserAPIVersion); + + if (docParserAPIMajorVersion === toMajorParserAPIVersion) { + return doc; // Nothing to do + } + + const detailedAsyncAPI = doc.meta().asyncapi; + switch (toMajorParserAPIVersion) { + case 1: + return createAsyncAPIDocumentParserV2(detailedAsyncAPI as DetailedAsyncAPIParserV2); + // break; + case 2: + return createAsyncAPIDocumentParserV3(detailedAsyncAPI as DetailedAsyncAPIParserV3); + default: + return doc; + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 91eca34..761932a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,35 +1,2 @@ -import { Parser as ParserV2 } from 'parserv2'; -import { Parser as ParserV3, convertToOldAPI as convertToOldAPIV3 } from 'parserv3'; - -import type { ParseOutput as ParseOutputV2 } from 'parserv2'; -import type { AsyncAPIDocumentInterface as AsyncAPIDocumentInterfaceV3, ParseOutput as ParseOutputV3Original } from 'parserv3'; -import type { AsyncAPIDocument as AsyncAPIDocumentV3 } from 'parserv3/esm/old-api/asyncapi'; - -export type ParseOutputV3 = ParseOutputV3Original & { document: AsyncAPIDocumentInterfaceV3 | AsyncAPIDocumentV3 | undefined } -export type ParseOutputPerVersion = ParseOutputV2 | ParseOutputV3 | undefined; -export type ParseOptions = { - ParserAPIMajorVersion: number; -} - -// Cache for parsers -const parsers = new Map(); -export async function Parse(doc: any, options?: ParseOptions): Promise { - if (!options || !options.ParserAPIMajorVersion) { - // Using Parser v3 (latest atm) by default - if (!parsers.has(3)) parsers.set(3, new ParserV3()); - const parsedDoc = await (parsers.get(3) as ParserV3).parse(doc) as ParseOutputV3; - if (parsedDoc?.document) parsedDoc.document = convertToOldAPIV3(parsedDoc.document) as any; - - return parsedDoc; - } - - switch (options.ParserAPIMajorVersion) { - case 1: - if (!parsers.has(2)) parsers.set(2, new ParserV2()); - return await parsers.get(2)?.parse(doc); - case 2: - if (!parsers.has(3)) parsers.set(3, new ParserV3()); - return await parsers.get(3)?.parse(doc); - } -} - +export { NewParser} from './parse'; +export { ConvertDocumentParserAPIVersion } from './convert'; diff --git a/src/parse.ts b/src/parse.ts new file mode 100644 index 0000000..56d51c6 --- /dev/null +++ b/src/parse.ts @@ -0,0 +1,36 @@ +import { Parser as ParserV2 } from 'parserv2'; +import { Parser as ParserV3 } from 'parserv3'; + +import type { ParseOptions as ParserOptionsParserV2 } from 'parserv2'; +import type { ParseOptions as ParserOptionsParserV3 } from 'parserv3'; + +import { majorParserAPIVersion } from './utils'; + +type Parser = ParserV2 | ParserV3; + +// Cache for parsers +const parsers = new Map(); + +export function NewParser(parserAPIVersion: string, options?: ParserOptionsParserV2 | ParserOptionsParserV3): Parser { + const parserAPIMajorVersion = majorParserAPIVersion(parserAPIVersion); + + switch (parserAPIMajorVersion) { + case 1: + if (options) { + return new ParserV2(options); // Can't use cached because options is not empty + } + + if (!parsers.has(2)) parsers.set(2, new ParserV2()); + return parsers.get(2) as ParserV2; + default: + case 0: // Using Parser v3 (latest atm) by default + case 2: + if (options) { + return new ParserV3(options); // Can't use cached because options is not empty + } + + if (!parsers.has(3)) parsers.set(3, new ParserV3()); + return parsers.get(3) as ParserV3; + } +} + diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..769eae5 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,5 @@ +export function majorParserAPIVersion(version: string): number { + if (!version) return 0; + + return Number(version.split('.')[0]); +} \ No newline at end of file diff --git a/test/convert.spec.ts b/test/convert.spec.ts new file mode 100644 index 0000000..a7cb531 --- /dev/null +++ b/test/convert.spec.ts @@ -0,0 +1,37 @@ + +import { Parser as ParserV2 } from 'parserv2'; +import { Parser as ParserV3 } from 'parserv3'; + +import { AsyncAPIDocument, ConvertDocumentParserAPIVersion } from '../src/convert'; + +describe('ConvertDocumentParserAPIVersion()', function() { + it('Converts from Parser-API v1 to Parser-API v2', async function() { + const doc = { asyncapi: '2.6.0', info: { title: '', description: '', version: ''}, channels: {} }; + const parsedDocParserV2 = (await new ParserV2().parse(doc)).document as AsyncAPIDocument; + + // Even though the value here is '1' for Parser V3, we force to be '2.0.0' for this test to do the equality check later. + parsedDocParserV2['_json']['x-parser-api-version'] = '2.0.0'; + + const parsedDocParserV3 = await new ParserV3().parse(doc); + const convertedDoc = ConvertDocumentParserAPIVersion(parsedDocParserV2, '2.0.0'); + expect(convertedDoc).toEqual(parsedDocParserV3.document); + }); + + it('Converts from Parser-API v2 to Parser-API v1', async function() { + const doc = { asyncapi: '2.6.0', info: { title: '', description: '', version: ''}, channels: {} }; + const parsedDocParserV3 = (await new ParserV3().parse(doc)).document as AsyncAPIDocument; + + // Even though the value here is '2.0.0' for Parser V3, we force to be 1 for this test to do the equality check later. + parsedDocParserV3['_json']['x-parser-api-version'] = 1; + + const parsedDocParserV2 = (await new ParserV2().parse(doc)).document; + const convertedDoc = ConvertDocumentParserAPIVersion(parsedDocParserV3, '1.0.0'); + expect(convertedDoc).toEqual(parsedDocParserV2); + }); + + it('Skips converting if no document is passed', async function() { + const doc = { } as AsyncAPIDocument; + const convertedDoc = ConvertDocumentParserAPIVersion(doc, '1.0.0'); + expect(convertedDoc).toEqual(doc); + }); +}); \ No newline at end of file diff --git a/test/index.spec.ts b/test/index.spec.ts deleted file mode 100644 index 2a163df..0000000 --- a/test/index.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ - -import { Parse, ParseOutputPerVersion } from '../src/index'; - -import type { ParseOutput as ParseOutputV2 } from 'parserv2'; -import type { ParseOutput as ParseOutputV3 } from 'parserv3'; -import { AsyncAPIDocument } from 'parserv3/esm/old-api/asyncapi'; - -describe('Parse()', function() { - it('Parses documents and outputs an AsyncAPIDocument compatible with Parser-API v1', async function() { - const doc = { asyncapi: '2.0.0', info: { title: '', version: '' }, channels: {} }; - const output = await Parse(doc, { ParserAPIMajorVersion: 1 }) as ParseOutputV2; - - expect(output).toBeDefined(); - expect(output.document).toBeDefined(); - expect(output.document?.extensions().get('x-parser-api-version')?.value()).toBe(1); - }); - - it('Parses documents and outputs an AsyncAPIDocument compatible with Parser-API v2', async function() { - const doc = { asyncapi: '3.0.0', info: { title: '', version: '' } }; - const output = await Parse(doc, { ParserAPIMajorVersion: 2 }) as ParseOutputV3; - - expect(output).toBeDefined(); - expect(output.document).toBeDefined(); - expect((output.document?.extensions().get('x-parser-api-version')?.value() as string).split('.')[0]).toBe('2'); - }); - - it('Parses documents and outputs an AsyncAPIDocument compatible with old API prior to parser-api existence', async function() { - const doc = { asyncapi: '3.0.0', info: { title: '', version: '' } }; - const output = await Parse(doc) as ParseOutputV3; - - expect(output).toBeDefined(); - expect(output.document).toBeDefined(); - const output2 = output as ParseOutputPerVersion; - expect((output2?.document as AsyncAPIDocument).extension('x-parser-api-version')).toEqual(0); - }); -}); \ No newline at end of file diff --git a/test/parse.spec.ts b/test/parse.spec.ts new file mode 100644 index 0000000..4e5551d --- /dev/null +++ b/test/parse.spec.ts @@ -0,0 +1,58 @@ + +import { Parser as ParserV2 } from 'parserv2'; +import { Parser as ParserV3 } from 'parserv3'; + +import { NewParser } from '../src/index'; + +describe('NewParser()', function() { + it('Creates a Parser without options compatible with Parser-API v1 and caches it', async function() { + const parser = NewParser('1.0.0'); + expect(parser).toBeInstanceOf(ParserV2); + + // Test cached parser is the same as the one we previously created. + expect(NewParser('1.0.0')).toStrictEqual(parser); + }); + + it('Creates a Parser with options compatible with Parser-API v1 and skips cache', async function() { + const options = { applyTraits: false }; + const parser = NewParser('1.0.0', options); + expect(parser).toBeInstanceOf(ParserV2); + + // Test no cached parser is returned + expect(NewParser('1.0.0', options)).not.toStrictEqual(parser); + }); + + it('Creates a Parser without options compatible with Parser-API v2 and caches it', async function() { + const parser = NewParser('2.0.0'); + expect(parser).toBeInstanceOf(ParserV3); + + // Test cached parser is the same as the one we previously created. + expect(NewParser('2.0.0')).toStrictEqual(parser); + }); + + it('Creates a Parser with options compatible with Parser-API v2 and skips cache', async function() { + const options = { applyTraits: false }; + const parser = NewParser('2.0.0', options); + expect(parser).toBeInstanceOf(ParserV3); + + // Test no cached parser is returned + expect(NewParser('2.0.0', options)).not.toStrictEqual(parser); + }); + + it('Creates a Parser without options compatible with old Parser API (AKA v0) and caches it', async function() { + const parser = NewParser(''); // could be '0.0.0' as well + expect(parser).toBeInstanceOf(ParserV3); // Using Parser v3 (latest atm) by default + + // Test cached parser is the same as the one we previously created. + expect(NewParser('')).toStrictEqual(parser); + }); + + it('Creates a Parser with options compatible with old Parser API (AKA v0) and skips cache', async function() { + const options = { applyTraits: false }; + const parser = NewParser('0.0.0', options); // could be empty string as well + expect(parser).toBeInstanceOf(ParserV3); + + // Test no cached parser is returned + expect(NewParser('0.0.0', options)).not.toStrictEqual(parser); + }); +}); \ No newline at end of file