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 Apr 6, 2024
1 parent 682de39 commit 2d73557
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 156 deletions.
2 changes: 1 addition & 1 deletion example/bundle-cjs.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const bundle = require('@asyncapi/bundler');

async function main() {
const document = await bundle([readFileSync('./main.yaml', 'utf-8')], {
referenceIntoComponents: false,
'x-origin': true,
});
if (document.yml()) {
writeFileSync('asyncapi.yaml', document.yml());
Expand Down
6 changes: 2 additions & 4 deletions example/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import bundle from '@asyncapi/bundler';

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

main().catch(e => console.error(e));
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,9 @@
"/lib"
],
"dependencies": {
"@apidevtools/json-schema-ref-parser": "^9.1.2",
"@apidevtools/json-schema-ref-parser": "^11.5.4",
"@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
15 changes: 5 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { toJS, resolve, versionCheck, resolveBaseFileDir } from './util';
import { toJS, resolve, versionCheck } from './util';
import { Document } from './document';
import { parse } from './parser';

import type { AsyncAPIObject } from './spec-types';
import { resolveV3Document } from './v3/parser';
Expand Down Expand Up @@ -74,17 +73,13 @@ import { resolveV3Document } from './v3/parser';
*
*/
export default async function bundle(files: string[], options: any = {}) {
if (typeof options.base !== 'undefined') {
options.base = toJS(options.base);
await parse(options.base, options);
}
// if (typeof options.base !== 'undefined') {
// options.base = toJS(options.base);
// await parse(options.base, options);
// }

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;

Expand Down
32 changes: 10 additions & 22 deletions src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,30 @@
import $RefParser from '@apidevtools/json-schema-ref-parser';
import { Parser } from '@asyncapi/parser';
import { addXOrigins } from './util';

import { AsyncAPIObject } from 'spec-types';

const parser = new Parser();

/**
* Resolves external references and updates $refs.
* Fully dereferences the AsyncAPI Document.
* @param {Object[]} JSONSchema
* @private
*/
export async function parse(JSONSchema: AsyncAPIObject, options: any = {}) {
let validationResult: any[] = [];
addXOrigins(JSONSchema);

const dereferencedJSONSchema = await $RefParser.dereference(JSONSchema, {
dereference: {
circular: false,
excludedPathMatcher: (path: string): boolean => {
return (
// prettier-ignore
!!(/#\/[a-zA-Z0-9]*/).exec(path)// ||
// !!(/#\/channels\/[a-zA-Z0-9]*\/servers/).exec(path) ||
// !!(/#\/operations\/[a-zA-Z0-9]*\/channel/).exec(path) ||
// !!(/#\/operations\/[a-zA-Z0-9]*\/messages/).exec(path) ||
// !!(/#\/operations\/[a-zA-Z0-9]*\/reply\/channel/).exec(path) ||
// !!(/#\/operations\/[a-zA-Z0-9]*\/reply\/messages/).exec(path) ||
// !!(/#\/components\/channels\/[a-zA-Z0-9]*\/servers/).exec(path) ||
// !!(/#\/components\/operations\/[a-zA-Z0-9]*\/channel/).exec(path) ||
// !!(/#\/components\/operations\/[a-zA-Z0-9]*\/messages/).exec(path) ||
// !!(/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/channel/).exec(
// path
// ) ||
// !!(/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/messages/).exec(
// path
// )
);
// excludedPathMatcher: (path: string): any => {
// return (
// // prettier-ignore
// );
// },
onDereference: (path: string, value: AsyncAPIObject) => {
if (options['x-origin']) {
value['x-origin'] = path;
}
},
},
});
Expand Down
98 changes: 0 additions & 98 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import fs from 'fs';
import axios from 'axios';
import { merge } 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
Expand Down Expand Up @@ -60,12 +55,7 @@ export const resolve = async (

try {
for (const asyncapiDocument of asyncapiDocuments) {
// if (options.referenceIntoComponents) {
// await parse(asyncapiDocument);
// }
addXOrigins(asyncapiDocument); // eslint-disable-line @typescript-eslint/no-use-before-define
await parse(asyncapiDocument, options);
// const bundledAsyncAPIDocument = await $RefParser.bundle(asyncapiDocument);
docs.push(asyncapiDocument);
}
} catch (e) {}
Expand Down Expand Up @@ -101,91 +91,3 @@ export function versionCheck(asyncapiDocuments: AsyncAPIObject[]): number {
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);
}
}
);
}

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') // eslint-disable-line
) as any); // eslint-disable-line

inlineAsyncapiDocument =
inlineAsyncapiDocument[String(inlineAsyncapiDocumentPointer)];

if (inlineAsyncapiDocument) {
addXOrigins(inlineAsyncapiDocument as AsyncAPIObject);
merge(key, inlineAsyncapiDocument);
}
}
} else {
addXOrigins(key);
}
}
});
return asyncapiDocument;
}
35 changes: 16 additions & 19 deletions src/v3/parser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import $RefParser from '@apidevtools/json-schema-ref-parser';
import { Parser } from '@asyncapi/parser';
import { addXOrigins } from '../util';

import { AsyncAPIObject } from 'spec-types';

Expand All @@ -9,31 +8,29 @@ const parser = new Parser();
export async function parse(JSONSchema: AsyncAPIObject, options: any = {}) {
let validationResult: any[] = [];

addXOrigins(JSONSchema);

const dereferencedJSONSchema = await $RefParser.dereference(JSONSchema, {
dereference: {
circular: false,
excludedPathMatcher: (path: string): boolean => {
excludedPathMatcher: (path: string): any => {
return (
// prettier-ignore
!!(/#\/[a-zA-Z0-9]*/).exec(path) ||
!!(/#\/channels\/[a-zA-Z0-9]*\/servers/).exec(path) ||
!!(/#\/operations\/[a-zA-Z0-9]*\/channel/).exec(path) ||
!!(/#\/operations\/[a-zA-Z0-9]*\/messages/).exec(path) ||
!!(/#\/operations\/[a-zA-Z0-9]*\/reply\/channel/).exec(path) ||
!!(/#\/operations\/[a-zA-Z0-9]*\/reply\/messages/).exec(path) ||
!!(/#\/components\/channels\/[a-zA-Z0-9]*\/servers/).exec(path) ||
!!(/#\/components\/operations\/[a-zA-Z0-9]*\/channel/).exec(path) ||
!!(/#\/components\/operations\/[a-zA-Z0-9]*\/messages/).exec(path) ||
!!(/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/channel/).exec(
path
) ||
!!(/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/messages/).exec(
path
)
/#\/channels\/[a-zA-Z0-9]*\/servers/.test(path) ||
/#\/operations\/[a-zA-Z0-9]*\/channel/.test(path) ||
/#\/operations\/[a-zA-Z0-9]*\/messages/.test(path) ||
/#\/operations\/[a-zA-Z0-9]*\/reply\/channel/.test(path) ||
/#\/operations\/[a-zA-Z0-9]*\/reply\/messages/.test(path) ||
/#\/components\/channels\/[a-zA-Z0-9]*\/servers/.test(path) ||
/#\/components\/operations\/[a-zA-Z0-9]*\/channel/.test(path) ||
/#\/components\/operations\/[a-zA-Z0-9]*\/messages/.test(path) ||
/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/channel/.test(path) ||
/#\/components\/operations\/[a-zA-Z0-9]*\/reply\/messages/.test(path)
);
},
onDereference: (path: string, value: AsyncAPIObject) => {
if (options['x-origin']) {
value['x-origin'] = path;
}
},
},
});

Expand Down

0 comments on commit 2d73557

Please sign in to comment.