From a3d369a5ff701335463c69f67cd26c28fb083c16 Mon Sep 17 00:00:00 2001 From: Viacheslav Turovskyi Date: Thu, 9 May 2024 02:14:35 +0000 Subject: [PATCH 1/3] fix: update `$ref`s after moving components to `components` --- src/Optimizer.ts | 3 ++- src/Utils/Helpers.ts | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Optimizer.ts b/src/Optimizer.ts index c78c0f09..c13aa9b2 100644 --- a/src/Optimizer.ts +++ b/src/Optimizer.ts @@ -17,7 +17,7 @@ import YAML from 'js-yaml' import merge from 'merge-deep' import * as _ from 'lodash' import { getOptimizableComponents } from './ComponentProvider' -import { filterReportElements, hasParent, sortReportElements, toJS } from './Utils' +import { filterReportElements, hasParent, sortReportElements, toJS, updateExistingRefs } from './Utils' import Debug from 'debug' export enum Action { @@ -163,6 +163,7 @@ export class Optimizer { _.set(this.outputObject, change.path, { $ref: `#/${change.target?.replace(/\./g, '/')}`, }) + updateExistingRefs(this.outputObject, change.path, change.target as string) debug('moved %s to %s', change.path, change.target) break diff --git a/src/Utils/Helpers.ts b/src/Utils/Helpers.ts index 9752a736..e013e3dc 100644 --- a/src/Utils/Helpers.ts +++ b/src/Utils/Helpers.ts @@ -169,4 +169,42 @@ const getComponentName = (component: OptimizableComponent): string => { return componentName } -export { compareComponents, isEqual, isInComponents, isInChannels, toJS, getComponentName } +function updateExistingRefs(asyncapiComponent: any, changePath: string, changeTarget: string): any { + const changePathArray = changePath.split('.') + changePathArray.unshift('#') + changePath = changePathArray.join('/') + + const changeTargetArray = changeTarget.split('.') + changeTargetArray.unshift('#') + changeTarget = changeTargetArray.join('/') + + function iterateComponent(asyncapiComponent: any) { + // eslint-disable-next-line github/array-foreach + Object.values(asyncapiComponent).forEach((key: any) => { + if (key && typeof key === 'object' && key !== '$ref') { + if (Object.keys(key).indexOf('$ref') !== -1) { + const keyValue = key['$ref'] + if (keyValue.startsWith(`${changePath}/`)) { + key['$ref'] = keyValue.replace(changePath, changeTarget) + } + } else { + iterateComponent(key as any) + } + } + }) + } + + iterateComponent(asyncapiComponent) + + return asyncapiComponent +} + +export { + compareComponents, + isEqual, + isInComponents, + isInChannels, + toJS, + getComponentName, + updateExistingRefs, +} From 43a5a96fe99fe8691b350e48647bf8cf5a07105b Mon Sep 17 00:00:00 2001 From: Viacheslav Turovskyi Date: Thu, 9 May 2024 02:14:35 +0000 Subject: [PATCH 2/3] fix: update `$ref`s after moving components to `components` --- src/Utils/Helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/Helpers.ts b/src/Utils/Helpers.ts index e013e3dc..3f8abe34 100644 --- a/src/Utils/Helpers.ts +++ b/src/Utils/Helpers.ts @@ -188,7 +188,7 @@ function updateExistingRefs(asyncapiComponent: any, changePath: string, changeTa key['$ref'] = keyValue.replace(changePath, changeTarget) } } else { - iterateComponent(key as any) + iterateComponent(key) } } }) From cb80951c6a95571654fe34c65c791bad37206a92 Mon Sep 17 00:00:00 2001 From: Viacheslav Turovskyi Date: Wed, 22 May 2024 03:13:13 +0000 Subject: [PATCH 3/3] fix: implement pre-validation of the optimized document before output --- examples/index.js | 4 ++-- src/Optimizer.ts | 34 +++++++++++++++++++++++++++++--- src/types.ts | 1 + test/Optimizer.spec.ts | 14 +------------ test/Reporters/Reporters.spec.ts | 2 +- 5 files changed, 36 insertions(+), 19 deletions(-) diff --git a/examples/index.js b/examples/index.js index 934f3373..ebf5b402 100644 --- a/examples/index.js +++ b/examples/index.js @@ -11,9 +11,9 @@ const outputFilePath = path.join(__dirname, 'output.yaml') const input = fs.readFileSync(inputFilePath, 'utf8') const optimizer = new Optimizer(input) -optimizer.getReport().then((report) => { +optimizer.getReport().then(async (report) => { console.log(report) - const optimizedDocument = optimizer.getOptimizedDocument({ + const optimizedDocument = await optimizer.getOptimizedDocument({ output: 'YAML', rules: { reuseComponents: true, diff --git a/src/Optimizer.ts b/src/Optimizer.ts index c13aa9b2..3e79ac3c 100644 --- a/src/Optimizer.ts +++ b/src/Optimizer.ts @@ -30,6 +30,11 @@ export enum Output { JSON = 'JSON', YAML = 'YAML', } + +const parser = new Parser() + +let validationResult: any[] = []; + /** * this class is the starting point of the library. * user will only interact with this class. here we generate different kind of reports using optimizers, apply changes and return the results to the user. @@ -60,7 +65,6 @@ export class Optimizer { * */ async getReport(): Promise { - const parser = new Parser() const parsedDocument = await parser.parse(this.YAMLorJSON, { applyTraits: false }) if (!parsedDocument.document) { // eslint-disable-next-line no-undef, no-console @@ -108,7 +112,7 @@ export class Optimizer { * @returns {string } returns an stringified version of the YAML output. * */ - getOptimizedDocument(options?: Options): string { + async getOptimizedDocument(options?: Options): Promise { const defaultOptions = { rules: { reuseComponents: true, @@ -140,6 +144,30 @@ export class Optimizer { } } + // Option `noValidation: true` is used by the testing system, which + // intentionally feeds Bundler wrong AsyncAPI Documents, thus it is not + // documented. + if (!options.noValidation) { + validationResult = await parser.validate(JSON.parse(JSON.stringify(this.outputObject))) + } + + // If Parser's `validate()` function returns a non-empty array with at least + // one `severity: 0`, that means there was at least one error during + // validation, not a `warning: 1`, `info: 2`, or `hint: 3`. Thus, array's + // elements with `severity: 0` are outputted as a list of remarks, and the + // program exits without doing anything further. + if ( + validationResult.length !== 0 && + validationResult.map((element: any) => element.severity).includes(0) + ) { + // eslint-disable-next-line no-undef, no-console + console.log( + 'Validation of the optimized AsyncAPI Document failed.\nList of remarks:\n', + validationResult.filter((element: any) => element.severity === 0) + ) + throw new Error('Validation of the optimized AsyncAPI Document failed.') + } + if (options.output === Output.JSON) { return JSON.stringify(this.outputObject) } @@ -163,7 +191,7 @@ export class Optimizer { _.set(this.outputObject, change.path, { $ref: `#/${change.target?.replace(/\./g, '/')}`, }) - updateExistingRefs(this.outputObject, change.path, change.target as string) + // updateExistingRefs(this.outputObject, change.path, change.target as string) debug('moved %s to %s', change.path, change.target) break diff --git a/src/types.ts b/src/types.ts index 03fcd47f..cb0cbe06 100644 --- a/src/types.ts +++ b/src/types.ts @@ -44,6 +44,7 @@ export interface Options { rules?: Rules output?: Output disableOptimizationFor?: DisableOptimizationFor // non-approved type + noValidation?: boolean } export interface IOptimizer { diff --git a/test/Optimizer.spec.ts b/test/Optimizer.spec.ts index f9b54bbf..f94262e3 100644 --- a/test/Optimizer.spec.ts +++ b/test/Optimizer.spec.ts @@ -18,7 +18,7 @@ describe('Optimizer', () => { const optimizer = new Optimizer(inputYAML) await optimizer.getReport() expect( - optimizer + await optimizer .getOptimizedDocument({ output: Output.YAML, rules: { @@ -31,7 +31,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputYAML_mATCFalse_mDTCTrue_schemaFalse.trim()) }) @@ -52,7 +51,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputYAML_mATCFalse_mDTCTrue_schemaFalse.trim()) }) @@ -73,7 +71,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputJSON_mATCFalse_mDTCTrue_schemaFalse.trim()) }) @@ -94,7 +91,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputYAML_mATCTrue_mDTCFalse_schemaFalse.trim()) }) @@ -115,7 +111,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputYAML_mATCTrue_mDTCFalse_schemaFalse.trim()) }) @@ -136,7 +131,6 @@ describe('Optimizer', () => { schema: false, }, }) - .trim() ).toEqual(outputJSON_mATCTrue_mDTCFalse_schemaFalse.trim()) }) @@ -157,7 +151,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputYAML_mATCFalse_mDTCTrue_schemaTrue.trim()) }) @@ -178,7 +171,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputYAML_mATCFalse_mDTCTrue_schemaTrue.trim()) }) @@ -199,7 +191,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputJSON_mATCFalse_mDTCTrue_schemaTrue.trim()) }) @@ -220,7 +211,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputYAML_mATCTrue_mDTCFalse_schemaTrue.trim()) }) @@ -241,7 +231,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputYAML_mATCTrue_mDTCFalse_schemaTrue.trim()) }) @@ -262,7 +251,6 @@ describe('Optimizer', () => { schema: true, }, }) - .trim() ).toEqual(outputJSON_mATCTrue_mDTCFalse_schemaTrue.trim()) }) }) diff --git a/test/Reporters/Reporters.spec.ts b/test/Reporters/Reporters.spec.ts index f66eeecb..69fe2fee 100644 --- a/test/Reporters/Reporters.spec.ts +++ b/test/Reporters/Reporters.spec.ts @@ -7,7 +7,7 @@ import { import { inputYAML } from '../fixtures' import { Parser } from '@asyncapi/parser' import { getOptimizableComponents } from '../../src/ComponentProvider' -import { OptimizableComponentGroup } from '../../src/index.d' +import { OptimizableComponentGroup } from '../../src/types' const moveAllToComponentsExpectedResult: any[] = [ {