Skip to content
This repository has been archived by the owner on Aug 3, 2024. It is now read-only.

Commit

Permalink
feat!: allow converting AsyncAPIDocument to different Parser-API vers…
Browse files Browse the repository at this point in the history
…ions + NewParser
  • Loading branch information
smoya committed Aug 31, 2023
1 parent 7ddfd58 commit dd6d03b
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 72 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
35 changes: 35 additions & 0 deletions src/convert.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
37 changes: 2 additions & 35 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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<number, ParserV2 | ParserV3>();
export async function Parse(doc: any, options?: ParseOptions): Promise<ParseOutputPerVersion> {
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';
36 changes: 36 additions & 0 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -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<number, Parser>();

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;
}
}

5 changes: 5 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function majorParserAPIVersion(version: string): number {
if (!version) return 0;

return Number(version.split('.')[0]);
}
37 changes: 37 additions & 0 deletions test/convert.spec.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
36 changes: 0 additions & 36 deletions test/index.spec.ts

This file was deleted.

58 changes: 58 additions & 0 deletions test/parse.spec.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});

0 comments on commit dd6d03b

Please sign in to comment.