From 34a523dbb6ca680dc7e66cf7ba5baf627f5bf430 Mon Sep 17 00:00:00 2001 From: souvik Date: Wed, 20 Mar 2024 16:56:27 +0530 Subject: [PATCH] feat: add baseDir option (#148) --- API.md | 1 + example/v3/bundle.js | 10 +++++--- example/v3/package-lock.json | 3 ++- example/v3/spec/main.yaml | 34 ++++++++++++++++++++++++++ example/v3/spec/messages.yaml | 17 +++++++++++++ src/index.ts | 7 +++++- src/util.ts | 46 +++++++++++++++++++++++++++++++++++ tests/lib/index.spec.ts | 10 ++++++++ tests/specfiles/main.yaml | 34 ++++++++++++++++++++++++++ tests/specfiles/messages.yaml | 17 +++++++++++++ 10 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 example/v3/spec/main.yaml create mode 100644 example/v3/spec/messages.yaml create mode 100644 tests/specfiles/main.yaml create mode 100644 tests/specfiles/messages.yaml diff --git a/API.md b/API.md index a4768e2..e22e0b6 100644 --- a/API.md +++ b/API.md @@ -79,6 +79,7 @@ console.log(document.string()); // get JSON string | [options] | Object | | | [options.base] | string \| object |

Base object whose properties will be retained.

| | [options.referenceIntoComponents] | boolean |

Pass true to resolve external references to components.

| +| [options.baseDir] | string |

Pass folder path to

| **Example** **TypeScript** diff --git a/example/v3/bundle.js b/example/v3/bundle.js index 9b17c9d..c0eae78 100644 --- a/example/v3/bundle.js +++ b/example/v3/bundle.js @@ -4,14 +4,18 @@ const {readFileSync, writeFileSync} = require('fs') async function main() { await singleFile() await multiFile() + await singleFileWithBaseDir() } -async function singleFile(){ - const document = await bundle([readFileSync('./main.yaml', 'utf-8')] ) +async function singleFileWithBaseDir(){ + const document = await bundle([readFileSync('./spec/main.yaml', 'utf-8')], {baseDir: './spec'}) + writeFileSync('asyncapi.yaml', document.yml()) +} +async function singleFile(){ + const document = await bundle([readFileSync('./spec/main.yaml', 'utf-8')]) writeFileSync('asyncapi.yaml', document.yml()) - } async function multiFile(){ diff --git a/example/v3/package-lock.json b/example/v3/package-lock.json index 526ead5..e7c5be8 100644 --- a/example/v3/package-lock.json +++ b/example/v3/package-lock.json @@ -13,7 +13,8 @@ } }, "../..": { - "version": "0.3.11", + "name": "@asyncapi/bundler", + "version": "0.4.0", "license": "Apache-2.0", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", diff --git a/example/v3/spec/main.yaml b/example/v3/spec/main.yaml new file mode 100644 index 0000000..bcd042e --- /dev/null +++ b/example/v3/spec/main.yaml @@ -0,0 +1,34 @@ +asyncapi: 3.0.0 +info: + title: Account Service + version: 1.0.0 + description: This service is in charge of processing user signupsA +channels: + userSignedup: + address: 'user/signedup' + messages: + userSignedUpMessage: + $ref: './messages.yaml#/messages/UserSignedUp' + test: + address: '/test' + messages: + testMessage: + $ref: '#/components/messages/TestMessage' +operations: + UserSignedUp: + action: send + channel: + $ref: '#/channels/userSignedup' + messages: + - $ref: './messages.yaml#/messages/UserSignedUp' + TestOpp: + action: send + channel: + $ref: '#/channels/test' + messages: + - $ref: '#/components/messages/TestMessage' +components: + messages: + TestMessage: + payload: + type: string \ No newline at end of file diff --git a/example/v3/spec/messages.yaml b/example/v3/spec/messages.yaml new file mode 100644 index 0000000..e9d7cbf --- /dev/null +++ b/example/v3/spec/messages.yaml @@ -0,0 +1,17 @@ +messages: + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user + UserLoggedIn: + payload: + type: object + properties: + id: string \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 539b465..50b0fd8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { toJS, resolve, versionCheck } from './util'; +import { toJS, resolve, versionCheck, resolveBaseFileDir } from './util'; import { Document } from './document'; import { parse } from './parser'; @@ -15,6 +15,7 @@ import { resolveV3Document } from './v3/parser'; * retained. * @param {boolean} [options.referenceIntoComponents] Pass `true` to resolve * external references to components. + * @param {string} [options.baseDir] Pass folder path to * * @return {Document} * @@ -80,6 +81,10 @@ export default async function bundle(files: string[], options: any = {}) { const parsedJsons = files.map(file => toJS(file)) as AsyncAPIObject[]; + if (typeof options.baseDir !== 'undefined') { + parsedJsons.forEach(parsedJson => resolveBaseFileDir(parsedJson, options.baseDir)); + } + const majorVersion = versionCheck(parsedJsons); let resolvedJsons; diff --git a/src/util.ts b/src/util.ts index 93b2cdf..f8b9741 100644 --- a/src/util.ts +++ b/src/util.ts @@ -3,8 +3,10 @@ import { cloneDeep } from 'lodash'; import yaml from 'js-yaml'; import { parse } from './parser'; import { ParserError } from './errors'; +import {JSONPath} from 'jsonpath-plus'; import type { AsyncAPIObject } from './spec-types'; +import path from 'path'; /** * @private @@ -99,4 +101,48 @@ export function versionCheck(asyncapiDocuments: AsyncAPIObject[]): number { currentVersion = majorVersion; } return currentVersion; +} + +export function isExternalReference(ref: string): boolean { + return typeof ref === 'string' && !ref.startsWith('#'); +} + +export function notAUrl(ref: string): boolean { + try { + new URL(ref); + return false; + } catch (error) { + return true; + } +} + +export function resolveBaseFileDir(file: object, baseFileDir: string) { + /** + * Update the local refences in a given file with the + * absolute file path using the baseDir passed by the + * user as an option. + */ + JSONPath({ + json: file, + resultType: 'all', + path: '$.channels.*.messages.*' + }).forEach(({parent, parentProperty}: {parent: any, parentProperty: string}) => { + const ref = parent[String(parentProperty)]['$ref']; + if (isExternalReference(ref) && notAUrl(ref)) { + parent[String(parentProperty)]['$ref'] = path.resolve(baseFileDir, ref); + } + }); + + JSONPath({ + json: file, + resultType: 'all', + path: '$.operations.*.messages.*' + }).forEach( + ({parent, parentProperty}: {parent: any, parentProperty: string}) => { + const ref = parent[String(parentProperty)]['$ref']; + if (isExternalReference(ref) && notAUrl(ref)) { + parent[String(parentProperty)]['$ref'] = path.resolve(baseFileDir, ref); + } + } + ); } \ No newline at end of file diff --git a/tests/lib/index.spec.ts b/tests/lib/index.spec.ts index d027c54..94a808c 100644 --- a/tests/lib/index.spec.ts +++ b/tests/lib/index.spec.ts @@ -89,6 +89,16 @@ describe('[integration testing] bundler should ', () => { ).resolves; }) + + test('should be able to change the baseDir folder', async () => { + const files = ['./tests/specfiles/main.yaml'] + expect( + await bundle( + files.map(file => fs.readFileSync(path.resolve(process.cwd(), file), 'utf-8')), + {baseDir: './tests/specfiles'} + ) + ).resolves + }) }); describe('[unit testing]', () => { diff --git a/tests/specfiles/main.yaml b/tests/specfiles/main.yaml new file mode 100644 index 0000000..bcd042e --- /dev/null +++ b/tests/specfiles/main.yaml @@ -0,0 +1,34 @@ +asyncapi: 3.0.0 +info: + title: Account Service + version: 1.0.0 + description: This service is in charge of processing user signupsA +channels: + userSignedup: + address: 'user/signedup' + messages: + userSignedUpMessage: + $ref: './messages.yaml#/messages/UserSignedUp' + test: + address: '/test' + messages: + testMessage: + $ref: '#/components/messages/TestMessage' +operations: + UserSignedUp: + action: send + channel: + $ref: '#/channels/userSignedup' + messages: + - $ref: './messages.yaml#/messages/UserSignedUp' + TestOpp: + action: send + channel: + $ref: '#/channels/test' + messages: + - $ref: '#/components/messages/TestMessage' +components: + messages: + TestMessage: + payload: + type: string \ No newline at end of file diff --git a/tests/specfiles/messages.yaml b/tests/specfiles/messages.yaml new file mode 100644 index 0000000..e9d7cbf --- /dev/null +++ b/tests/specfiles/messages.yaml @@ -0,0 +1,17 @@ +messages: + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user + UserLoggedIn: + payload: + type: object + properties: + id: string \ No newline at end of file