Skip to content

Commit

Permalink
feat: add x-origin property
Browse files Browse the repository at this point in the history
  • Loading branch information
aeworxet committed Mar 11, 2024
1 parent 34a523d commit c0af69d
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 117 deletions.
6 changes: 4 additions & 2 deletions example/bundle-cjs.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ const bundle = require('@asyncapi/bundler');

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: true,
referenceIntoComponents: false,
});
writeFileSync('asyncapi.yaml', document.yml());
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
}
}

main().catch(e => console.error(e));
6 changes: 4 additions & 2 deletions example/bundle-cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ const bundle = require('@asyncapi/bundler');

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: true,
referenceIntoComponents: false,
});
writeFileSync('asyncapi.yaml', document.yml());
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
}
}

main().catch(e => console.error(e));
6 changes: 4 additions & 2 deletions example/bundle-esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import bundle from '@asyncapi/bundler';

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: true,
referenceIntoComponents: false,
});
writeFileSync('asyncapi.yaml', document.yml());
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
}
}

main().catch(e => console.error(e));
6 changes: 4 additions & 2 deletions example/bundle-esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import bundle from '@asyncapi/bundler';

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: true,
referenceIntoComponents: false,
});
writeFileSync('asyncapi.yaml', document.yml());
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
}
}

main().catch(e => console.error(e));
6 changes: 4 additions & 2 deletions example/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import bundle from '@asyncapi/bundler';

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: true,
referenceIntoComponents: false,
});
writeFileSync('asyncapi.yaml', document.yml());
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
}
}

main().catch(e => console.error(e));
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"license": "Apache-2.0",
"dependencies": {
"@asyncapi/bundler": "../",
"@types/node": "^18.7.23",
"@types/node": "^20.11.30",
"ts-node": "^10.9.1",
"typescript": "^4.8.4"
"typescript": "^5.4.3"
}
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
"/lib"
],
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.0.9",
"@apidevtools/json-schema-ref-parser": "^9.1.2",
"@asyncapi/parser": "^3.0.10",
"@types/json-schema": "^7.0.11",
"axios": "^1.6.8",
"js-yaml": "^4.1.0",
"jsonpath-plus": "^6.0.1",
"lodash": "^4.17.21"
Expand Down
8 changes: 6 additions & 2 deletions src/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@ export class Document {
* @return {Object}
*/
json() {
return this._doc;
if (Object.keys(this._doc).length) {
return this._doc;
}
}

/**
* @return {string}
*/
yml() {
return yaml.dump(this._doc);
if (Object.keys(this._doc).length) {
return yaml.dump(this._doc);
}
}

/**
Expand Down
89 changes: 72 additions & 17 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import fs from 'fs';
import $RefParser from '@apidevtools/json-schema-ref-parser';
import { cloneDeep } from 'lodash';
import axios from 'axios';
import { cloneDeep, merge } from 'lodash';
import yaml from 'js-yaml';
import { parse } from './parser';
import { ParserError } from './errors';
import {JSONPath} from 'jsonpath-plus';
import { JSONPath } from 'jsonpath-plus';

import type { AsyncAPIObject } from './spec-types';
import path from 'path';
Expand Down Expand Up @@ -39,7 +41,7 @@ export const toJS = (asyncapiYAMLorJSON: string | object) => {
title: 'The provided yaml is not valid.',
});
}

return yaml.load(asyncapiYAMLorJSON);
};

Expand Down Expand Up @@ -73,6 +75,7 @@ export const resolve = async (
if (options.referenceIntoComponents) {
await parse(asyncapiDocument);
}
addXOrigins(asyncapiDocument);
const bundledAsyncAPIDocument = await $RefParser.bundle(asyncapiDocument);
docs.push(bundledAsyncAPIDocument);
}
Expand All @@ -81,7 +84,7 @@ export const resolve = async (
};

/**
*
*
* @param asyncapiDocument {AsyncAPIObject}
* @returns {boolean}
*/
Expand All @@ -96,7 +99,9 @@ export function versionCheck(asyncapiDocuments: AsyncAPIObject[]): number {
for (const asyncapiDocument of asyncapiDocuments) {
const majorVersion = getSpecVersion(asyncapiDocument);
if (majorVersion !== currentVersion) {
throw new Error('Unable to bundle specification file of different major versions');
throw new Error(
'Unable to bundle specification file of different major versions'
);
}
currentVersion = majorVersion;
}
Expand All @@ -118,31 +123,81 @@ export function notAUrl(ref: string): boolean {

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.
* 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);
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.*'
path: '$.operations.*.messages.*',
}).forEach(
({parent, parentProperty}: {parent: any, parentProperty: string}) => {
({ 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);
}
}
);
}
}

// Moved 'addXOrigins' to the beginning of the scope to avoid an ESLint's error
// `'addXOrigins' was used before it was defined`
export function addXOrigins(asyncapiDocument: AsyncAPIObject) {
// VALUE from 'asyncapiDocument' becomes KEY for the
// underlying and recursive functions
Object.values(asyncapiDocument).forEach(async (key: any) => {
if (key && typeof key === 'object' && key !== '$ref') {
if (Object.keys(key).indexOf('$ref') !== -1) {
if (isExternalReference(key['$ref'])) {
key['x-origin'] = key['$ref'];

// If an external `$ref` is found, the function goes into
// second-level recursion to see if there are more `$ref`s whose
// values need to be copied to the `x-origin` properties of the
// `$ref`ed file.
// If an external `$ref` is found again, the function goes into the
// third-level recursion, and so on, until it reaches a file that
// contains no external `$ref`s at all.
// Then it exits all the way up in the opposite direction.

const inlineAsyncapiDocumentURI = key['$ref'].split('#/');
const inlineAsyncapiDocumentPath = inlineAsyncapiDocumentURI[0];
const inlineAsyncapiDocumentPointer = inlineAsyncapiDocumentURI[1];

let inlineAsyncapiDocument = inlineAsyncapiDocumentPath.startsWith(
'http'
)
? yaml.load(await axios(inlineAsyncapiDocumentPath))
: (yaml.load(
fs.readFileSync(inlineAsyncapiDocumentPath, 'utf-8')
) as any);

inlineAsyncapiDocument =
inlineAsyncapiDocument[inlineAsyncapiDocumentPointer];

if (inlineAsyncapiDocument) {
addXOrigins(inlineAsyncapiDocument as AsyncAPIObject);
merge(key, inlineAsyncapiDocument);
}
}
} else {
addXOrigins(key);
}
}
});
return asyncapiDocument;
}
Loading

0 comments on commit c0af69d

Please sign in to comment.