From 5d66dbc47f5d0d335c2b92b2ddc07f4ca3077c95 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Wed, 30 Aug 2023 17:52:00 +0100 Subject: [PATCH 01/13] support v3 --- examples/index.js | 115 ++++++++++++++------------------------- package-lock.json | 16 +++--- package.json | 3 +- src/ComponentProvider.ts | 82 ++++++++++++++++------------ src/Optimizer.ts | 6 +- 5 files changed, 103 insertions(+), 119 deletions(-) diff --git a/examples/index.js b/examples/index.js index 93c0f31c..a08dc68b 100644 --- a/examples/index.js +++ b/examples/index.js @@ -2,87 +2,52 @@ const { Optimizer } = require('../lib/Optimizer') const yaml = ` -asyncapi: 2.0.0 +asyncapi: 3.0.0 info: - title: Streetlights API - version: '1.0.0' + title: Account Service + version: 1.0.0 + description: This service is in charge of processing user signups channels: - smartylighting/event/{streetlightId}/lighting/measured: - parameters: - #this parameter is duplicated. it can be moved to components and ref-ed from here. - streetlightId: - schema: - type: string - subscribe: - operationId: receiveLightMeasurement - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: lightMeasured - title: Light measured - contentType: application/json - traits: - - headers: - type: object - properties: - my-app-header: - type: integer - minimum: 0 - maximum: 100 - payload: - type: object - properties: - lumens: - type: integer - minimum: 0 - #full form is used, we can ref it to: #/components/schemas/sentAt - sentAt: - type: string - format: date-time - smartylighting/action/{streetlightId}/turn/on: - parameters: - streetlightId: - schema: - type: string - publish: - operationId: turnOn - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: turnOnOff - title: Turn on/off - traits: - - headers: - type: object - properties: - my-app-header: - type: integer - minimum: 0 - maximum: 100 - payload: - type: object - properties: - sentAt: - $ref: "#/components/schemas/sentAt" + user/signedup: + address: user/signedup + messages: + subscribe.message: + $ref: '#/components/messages/UserSignedUp' +operations: + user/signedup.subscribe: + action: send + channel: + $ref: '#/channels/user~1signedup' + messages: + - $ref: '#/components/messages/UserSignedUp' components: messages: - #libarary should be able to find and delete this message, because it is not used anywhere. - unusedMessage: - name: unusedMessage - title: This message is not used in any channel. - - schemas: - #this schema is ref-ed in one channel and used full form in another. library should be able to identify and ref the second channel as well. - sentAt: - type: string - format: date-time` + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user + UserSignedUpV2: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user +` const optimizer = new Optimizer(yaml) optimizer.getReport().then((report) => { - //console.log(JSON.stringify(report)) + console.log(JSON.stringify(report)) const optimizedDocument = optimizer.getOptimizedDocument({ rules: { reuseComponents: true, diff --git a/package-lock.json b/package-lock.json index b5770026..f04422e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.0.tgz", "integrity": "sha512-78jjN3eW4ZmgJEa6Ap15lofzADCeItO4wHcAY2Jod3qLB1xf1zFDZQdtm3VSHYLeLhwoC1A33bAtzEf7M5P2bg==", "dependencies": { - "@asyncapi/specs": "^5.1.0", + "@asyncapi/specs": "^6.0.0-next-major-spec.6", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", @@ -96,9 +96,9 @@ } }, "node_modules/@asyncapi/specs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", - "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "version": "6.0.0-next-major-spec.6", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.6.tgz", + "integrity": "sha512-xYiXZetKiAZgZoh2q/sxt+Ceg8bLTK/WSCwtLvlH7JgnjpnOEtCDwKszijmmOJEYdGVSrj0OFXLPrg/dm4JANw==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -9102,7 +9102,7 @@ "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.0.tgz", "integrity": "sha512-78jjN3eW4ZmgJEa6Ap15lofzADCeItO4wHcAY2Jod3qLB1xf1zFDZQdtm3VSHYLeLhwoC1A33bAtzEf7M5P2bg==", "requires": { - "@asyncapi/specs": "^5.1.0", + "@asyncapi/specs": "^6.0.0-next-major-spec.6", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", @@ -9151,9 +9151,9 @@ } }, "@asyncapi/specs": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-5.1.0.tgz", - "integrity": "sha512-yffhETqehkim43luMnPKOwzY0D0YtU4bKpORIXIaid6p5Y5kDLrMGJaEPkNieQp03HMjhjFrnUPtT8kvqe0+aQ==", + "version": "6.0.0-next-major-spec.6", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.6.tgz", + "integrity": "sha512-xYiXZetKiAZgZoh2q/sxt+Ceg8bLTK/WSCwtLvlH7JgnjpnOEtCDwKszijmmOJEYdGVSrj0OFXLPrg/dm4JANw==", "requires": { "@types/json-schema": "^7.0.11" } diff --git a/package.json b/package.json index a2c80860..3a34c278 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "test": "jest --coverage", "test:watch": "jest --watch", "build": "tsc", + "build:watch": "tsc --watch", "docs": "jsdoc2md lib/Optimizer.js -f lib/**/*.js > API.md", "example": "tsc && node examples/index.js", "lint": "eslint --max-warnings 0 --config .eslintrc .", @@ -55,7 +56,7 @@ "access": "public" }, "dependencies": { - "@asyncapi/parser": "^2.1.0", + "@asyncapi/parser": "^v3.0.0-next-major-spec.1", "js-yaml": "^4.1.0", "jsonpath-plus": "^6.0.1", "lodash": "^4.17.21", diff --git a/src/ComponentProvider.ts b/src/ComponentProvider.ts index bea1ccaf..7971162e 100644 --- a/src/ComponentProvider.ts +++ b/src/ComponentProvider.ts @@ -4,38 +4,30 @@ import { OptimizableComponentGroup, OptimizableComponent } from 'index.d' import { JSONPath } from 'jsonpath-plus' import _ from 'lodash' -//the type of object should match exactly with one of the fixed fields in asyncapi components object. -const OPTIMIZABLE_PATHS = [ - { - type: 'messages', - paths: [ - '$.channels.*.*.message[?(@property !== "oneOf")]^', - '$.channels.*.*.message.oneOf.*', - '$.components.messages.*', - ], - }, - { - type: 'schemas', - paths: [ - '$.channels.*.*.message.traits[*]..[?(@.type)]', - '$.channels.*.*.message.headers', - '$.channels.*.*.message.headers..[?(@.type)]', - '$.channels.*.*.message.payload', - '$.channels.*.*.message.payload..[?(@.type)]', - '$.channels.*.parameters.*.schema[?(@.type)]', - '$.channels.*.parameters.*.schema..[?(@.type)]', - '$.components.schemas..[?(@.type)]', - ], - }, - { type: 'parameters', paths: ['$.channels.*.parameters.*', '$.components.parameters.*'] }, -] +export const toLodashPath = (jsonPointer: string): string => { + // Remove leading slash if present + if (jsonPointer.startsWith('/')) { + jsonPointer = jsonPointer.slice(1) + } + + // Split the JSON Pointer into tokens + const tokens = jsonPointer.split('/') -export const toLodashPath = (path: string): string => { - return path - .replace(/'\]\['/g, '.') - .slice(3, -2) - .replace(/'\]/g, '') - .replace(/\['/g, '.') + // Unescape the special characters and transform into Lodash path + return tokens + .map((token) => { + // Replace tilde representations + token = token.replace(/~1/g, '/') + token = token.replace(/~0/g, '~') + + // Check if token can be treated as an array index (non-negative integer) + if (/^\d+$/.test(token)) { + return `[${token}]` + } + // For nested properties, use dot notation + return token + }) + .join('.') } export const parseComponentsFromPath = ( @@ -62,10 +54,32 @@ export const getOptimizableComponents = ( asyncAPIDocument: AsyncAPIDocumentInterface ): OptimizableComponentGroup[] => { const optimizeableComponents: OptimizableComponentGroup[] = [] - for (const componentsPaths of OPTIMIZABLE_PATHS) { + + const optimizableComponents = { + // eslint-disable-next-line no-warning-comments + //TODO: remove the second all() call after https://github.com/asyncapi/parser-js/pull/854 is merged + + //ignore the error for now + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + servers: asyncAPIDocument.allServers().all().all(), + messages: asyncAPIDocument.allMessages().all(), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + channels: asyncAPIDocument.allChannels().all().all(), + schemas: asyncAPIDocument.allSchemas().all(), + operations: asyncAPIDocument.allOperations().all(), + } + for (const [type, components] of Object.entries(optimizableComponents)) { + if (components.length === 0) continue optimizeableComponents.push({ - type: componentsPaths.type, - components: parseComponentsFromPath(asyncAPIDocument, componentsPaths.paths), + type, + components: components.map((component: any) => ({ + path: toLodashPath(component.jsonPath()), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + component: component.json(), + })), }) } return optimizeableComponents diff --git a/src/Optimizer.ts b/src/Optimizer.ts index e542ad71..b5684790 100644 --- a/src/Optimizer.ts +++ b/src/Optimizer.ts @@ -51,7 +51,11 @@ export class Optimizer { async getReport(): Promise { const parser = new Parser() const parsedDocument = await parser.parse(this.YAMLorJSON, { applyTraits: false }) - if (!parsedDocument.document) throw new Error('Parsing failed.') + if (!parsedDocument.document) { + // eslint-disable-next-line no-undef, no-console + console.error(parsedDocument.diagnostics) + throw new Error('Parsing failed.') + } this.components = getOptimizableComponents(parsedDocument.document) const rawReports = this.reporters.map((reporter) => reporter(this.components)) const filteredReports = rawReports.map((report) => ({ From e918517447e9fe0232c4f575f6de2cadfe9b5e09 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:28:55 +0100 Subject: [PATCH 02/13] use debug --- package-lock.json | 60 +++++++++++++++++++++++++++++++++-------------- package.json | 4 +++- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index f04422e8..6b059802 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,9 @@ "version": "0.2.1", "license": "Apache-2.0", "dependencies": { - "@asyncapi/parser": "^2.1.0", + "@asyncapi/parser": "v3.0.0-next-major-spec.2", + "@types/debug": "^4.1.8", + "debug": "^4.3.4", "js-yaml": "^4.1.0", "jsonpath-plus": "^6.0.1", "lodash": "^4.17.21", @@ -36,9 +38,9 @@ } }, "node_modules/@asyncapi/parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.0.tgz", - "integrity": "sha512-78jjN3eW4ZmgJEa6Ap15lofzADCeItO4wHcAY2Jod3qLB1xf1zFDZQdtm3VSHYLeLhwoC1A33bAtzEf7M5P2bg==", + "version": "3.0.0-next-major-spec.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.2.tgz", + "integrity": "sha512-/gJgCYNYlUSDJhySK3IagjiyFfnwEsAZd5rTe396CB+HxJ6yDWDOZYdHzkFgU2RnCULSGzVOWZGx8t3PK+yuVg==", "dependencies": { "@asyncapi/specs": "^6.0.0-next-major-spec.6", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", @@ -1961,6 +1963,14 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/es-aggregate-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz", @@ -2032,6 +2042,11 @@ "integrity": "sha512-t5B5UfacpaP8opUvFGUwT0uQetFrD+qm1/I2ksxokJFLT0Tb4B2NI2G2LYz3ugMDKOE7adkNBZ6coK7RW6MAqA==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "node_modules/@types/node": { "version": "15.12.4", "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" @@ -3035,9 +3050,9 @@ } }, "node_modules/debug": { - "version": "4.3.1", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -7071,8 +7086,7 @@ }, "node_modules/ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -9098,9 +9112,9 @@ }, "dependencies": { "@asyncapi/parser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-2.1.0.tgz", - "integrity": "sha512-78jjN3eW4ZmgJEa6Ap15lofzADCeItO4wHcAY2Jod3qLB1xf1zFDZQdtm3VSHYLeLhwoC1A33bAtzEf7M5P2bg==", + "version": "3.0.0-next-major-spec.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.2.tgz", + "integrity": "sha512-/gJgCYNYlUSDJhySK3IagjiyFfnwEsAZd5rTe396CB+HxJ6yDWDOZYdHzkFgU2RnCULSGzVOWZGx8t3PK+yuVg==", "requires": { "@asyncapi/specs": "^6.0.0-next-major-spec.6", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", @@ -10615,6 +10629,14 @@ "@babel/types": "^7.3.0" } }, + "@types/debug": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", + "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", + "requires": { + "@types/ms": "*" + } + }, "@types/es-aggregate-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz", @@ -10686,6 +10708,11 @@ "integrity": "sha512-t5B5UfacpaP8opUvFGUwT0uQetFrD+qm1/I2ksxokJFLT0Tb4B2NI2G2LYz3ugMDKOE7adkNBZ6coK7RW6MAqA==", "dev": true }, + "@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, "@types/node": { "version": "15.12.4", "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" @@ -11403,9 +11430,9 @@ } }, "debug": { - "version": "4.3.1", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { "ms": "2.1.2" } @@ -14455,8 +14482,7 @@ }, "ms": { "version": "2.1.2", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", diff --git a/package.json b/package.json index 3a34c278..9b7825fb 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,9 @@ "access": "public" }, "dependencies": { - "@asyncapi/parser": "^v3.0.0-next-major-spec.1", + "@asyncapi/parser": "v3.0.0-next-major-spec.2", + "@types/debug": "^4.1.8", + "debug": "^4.3.4", "js-yaml": "^4.1.0", "jsonpath-plus": "^6.0.1", "lodash": "^4.17.21", From 3ae3324058527dd681df6fade5d375545ca45567 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:29:18 +0100 Subject: [PATCH 03/13] modify example --- examples/index.js | 52 +++---------------------- examples/input.yaml | 93 ++++++++++++++++++++++++++++++++++++++++++++ examples/output.yaml | 1 + 3 files changed, 100 insertions(+), 46 deletions(-) create mode 100644 examples/input.yaml create mode 100644 examples/output.yaml diff --git a/examples/index.js b/examples/index.js index a08dc68b..4211689c 100644 --- a/examples/index.js +++ b/examples/index.js @@ -1,59 +1,19 @@ // eslint-disable-next-line @typescript-eslint/no-var-requires const { Optimizer } = require('../lib/Optimizer') -const yaml = ` -asyncapi: 3.0.0 -info: - title: Account Service - version: 1.0.0 - description: This service is in charge of processing user signups -channels: - user/signedup: - address: user/signedup - messages: - subscribe.message: - $ref: '#/components/messages/UserSignedUp' -operations: - user/signedup.subscribe: - action: send - channel: - $ref: '#/channels/user~1signedup' - messages: - - $ref: '#/components/messages/UserSignedUp' -components: - messages: - UserSignedUp: - payload: - type: object - properties: - displayName: - type: string - description: Name of the user - email: - type: string - format: email - description: Email of the user - UserSignedUpV2: - payload: - type: object - properties: - displayName: - type: string - description: Name of the user - email: - type: string - format: email - description: Email of the user -` -const optimizer = new Optimizer(yaml) +// read input.yaml file synconously and store it as an string +const input = require('fs').readFileSync('./examples/input.yaml', 'utf8') +const optimizer = new Optimizer(input) optimizer.getReport().then((report) => { console.log(JSON.stringify(report)) const optimizedDocument = optimizer.getOptimizedDocument({ + output: 'JSON', rules: { reuseComponents: true, removeComponents: true, moveToComponents: true, }, }) - console.log(optimizedDocument) + //store optimizedDocument as to output.yaml + require('fs').writeFileSync('./examples/output.yaml', optimizedDocument) }) diff --git a/examples/input.yaml b/examples/input.yaml new file mode 100644 index 00000000..2e661883 --- /dev/null +++ b/examples/input.yaml @@ -0,0 +1,93 @@ +{ + 'asyncapi': '3.0.0', + 'info': + { + 'title': 'Untidy AsyncAPI file', + 'version': '1.0.0', + 'description': 'This file contains duplicate and unused messages across the file and is used to test the optimizer.', + }, + 'channels': + { + 'withDuplicatedMessage1': + { + 'address': 'user/signedup', + 'messages': + { 'duped1': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, + }, + 'withDuplicatedMessage2': + { + 'address': 'user/signedup', + 'messages': + { 'duped2': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, + }, + 'withFullFormMessage': + { + 'address': 'user/signedup', + 'messages': + { + 'canBeReused': { 'payload': { 'type': 'object', 'description': 'I can be reused.' } }, + }, + }, + 'UserSignedUp1': + { + 'address': 'user/signedup', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + 'UserSignedUp2': + { + 'address': 'user/signedup', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + 'deleteAccount': + { + 'address': 'user/deleteAccount', + 'messages': { 'deleteUser': { '$ref': '#/components/messages/DeleteUser' } }, + }, + }, + 'operations': + { + 'user/deleteAccount.subscribe': + { + 'action': 'send', + 'channel': { '$ref': '#/channels/deleteAccount' }, + 'messages': [{ '$ref': '#/components/messages/DeleteUser' }], + }, + }, + 'components': + { + 'channels': + { + 'unUsedChannel': + { + 'address': 'user/unused', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + }, + 'schemas': { 'canBeReused': { 'type': 'object', 'description': 'I can be reused.' } }, + 'messages': + { + 'unUsedMessage': { 'payload': { 'type': 'boolean' } }, + 'DeleteUser': + { + 'payload': + { + 'type': 'string', + 'description': 'userId of the user that is going to be deleted', + }, + }, + 'UserSignedUp': + { + 'payload': + { + 'type': 'object', + 'properties': + { + 'displayName': { 'type': 'string', 'description': 'Name of the user' }, + 'email': + { 'type': 'string', 'format': 'email', 'description': 'Email of the user' }, + }, + }, + }, + }, + }, +} diff --git a/examples/output.yaml b/examples/output.yaml new file mode 100644 index 00000000..324a5ab3 --- /dev/null +++ b/examples/output.yaml @@ -0,0 +1 @@ +{"asyncapi":"3.0.0","info":{"title":"Untidy AsyncAPI file","version":"1.0.0","description":"This file contains duplicate and unused messages across the file and is used to test the optimizer."},"channels":{"withDuplicatedMessage1":{"address":"user/signedup","messages":{"duped1":{"$ref":"#/components/messages/message-1"}}},"withDuplicatedMessage2":{"address":"user/signedup","messages":{"duped2":{"$ref":"#/components/messages/message-1"}}},"withFullFormMessage":{"address":"user/signedup","messages":{"canBeReused":{"payload":{"$ref":"#/components/schemas/canBeReused"}}}},"UserSignedUp1":{"$ref":"#/components/channels/channel-1"},"UserSignedUp2":{"$ref":"#/components/channels/channel-1"},"deleteAccount":{"address":"user/deleteAccount","messages":{"deleteUser":{"$ref":"#/components/messages/DeleteUser"}}}},"operations":{"user/deleteAccount.subscribe":{"action":"send","channel":{"$ref":"#/channels/deleteAccount"},"messages":[{"$ref":"#/components/messages/DeleteUser"}]}},"components":{"schemas":{"canBeReused":{"type":"object","description":"I can be reused."},"schema-1":{"type":"object","description":"I am duplicated"}},"messages":{"DeleteUser":{"payload":{"type":"string","description":"userId of the user that is going to be deleted"}},"UserSignedUp":{"payload":{"type":"object","properties":{"displayName":{"type":"string","description":"Name of the user"},"email":{"type":"string","format":"email","description":"Email of the user"}}}},"message-1":{"payload":{"$ref":"#/components/schemas/schema-1"}}},"channels":{"channel-1":{"address":"user/signedup","messages":{"myMessage":{"$ref":"#/components/messages/UserSignedUp"}}}}}} \ No newline at end of file From db74cf5d392130863e1c5e41b95ccdd5d1109295 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:29:40 +0100 Subject: [PATCH 04/13] remove warnings --- src/ComponentProvider.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ComponentProvider.ts b/src/ComponentProvider.ts index 7971162e..307e9e0d 100644 --- a/src/ComponentProvider.ts +++ b/src/ComponentProvider.ts @@ -56,17 +56,9 @@ export const getOptimizableComponents = ( const optimizeableComponents: OptimizableComponentGroup[] = [] const optimizableComponents = { - // eslint-disable-next-line no-warning-comments - //TODO: remove the second all() call after https://github.com/asyncapi/parser-js/pull/854 is merged - - //ignore the error for now - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - servers: asyncAPIDocument.allServers().all().all(), + servers: asyncAPIDocument.allServers().all(), messages: asyncAPIDocument.allMessages().all(), - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - channels: asyncAPIDocument.allChannels().all().all(), + channels: asyncAPIDocument.allChannels().all(), schemas: asyncAPIDocument.allSchemas().all(), operations: asyncAPIDocument.allOperations().all(), } From a85c76c2f8471a360bb0d889e9c540ad2f01afac Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:30:18 +0100 Subject: [PATCH 05/13] add a new filter --- src/Optimizer.ts | 11 +++++++++-- src/Utils/Helpers.ts | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Optimizer.ts b/src/Optimizer.ts index b5684790..c0678aa7 100644 --- a/src/Optimizer.ts +++ b/src/Optimizer.ts @@ -12,7 +12,8 @@ import YAML from 'js-yaml' import merge from 'merge-deep' import * as _ from 'lodash' import { getOptimizableComponents } from './ComponentProvider' -import { hasParent, sortReportElements, toJS } from './Utils' +import { filterReportElements, hasParent, sortReportElements, toJS } from './Utils' +import Debug from 'debug' export enum Action { Move = 'move', @@ -58,12 +59,14 @@ export class Optimizer { } this.components = getOptimizableComponents(parsedDocument.document) const rawReports = this.reporters.map((reporter) => reporter(this.components)) - const filteredReports = rawReports.map((report) => ({ + const reportsWithParents = rawReports.map((report) => ({ type: report.type, elements: report.elements.filter((reportElement) => hasParent(reportElement, this.outputObject) ), })) + + const filteredReports = filterReportElements(reportsWithParents) const sortedReports = filteredReports.map((report) => sortReportElements(report)) this.reports = sortedReports @@ -128,6 +131,7 @@ export class Optimizer { } private applyReport(changes: ReportElement[]): void { + const debug = Debug('optimizer:applyReport') for (const change of changes) { switch (change.action) { case Action.Move: @@ -135,6 +139,7 @@ export class Optimizer { _.set(this.outputObject, change.path, { $ref: `#/${change.target?.replace(/\./g, '/')}`, }) + debug('moved %s to %s', change.path, change.target) break case Action.Reuse: @@ -143,12 +148,14 @@ export class Optimizer { $ref: `#/${change.target?.replace(/\./g, '/')}`, }) } + debug('%s reused %s', change.path, change.target) break case Action.Remove: _.unset(this.outputObject, change.path) //if parent becomes empty after removing, parent should be removed as well. this.removeEmptyParent(change.path) + debug('removed %s', change.path) break } } diff --git a/src/Utils/Helpers.ts b/src/Utils/Helpers.ts index 9cf90fa8..39231a2e 100644 --- a/src/Utils/Helpers.ts +++ b/src/Utils/Helpers.ts @@ -29,6 +29,41 @@ export const sortReportElements = (report: NewReport): NewReport => { report.elements.sort((a, b) => a.action.length - b.action.length || b.path.length - a.path.length) return report } + +// Utility function to get all targets of reports with type 'reuseComponents' +const getReuseComponentTargets = ( + reports: { type: string; elements: ReportElement[] }[] +): Set => { + const targets = new Set() + for (const report of reports) { + if (report.type === 'reuseComponents') { + for (const element of report.elements) { + if (element.target) { + targets.add(element.target) + } + } + } + } + return targets +} + +// Main function to filter report elements +export const filterReportElements = ( + reports: { type: string; elements: ReportElement[] }[] +): NewReport[] => { + const reuseTargets = getReuseComponentTargets(reports) + + return reports.map((report) => { + if (report.type === 'removeComponents') { + const filteredElements = report.elements.filter((element) => { + return !reuseTargets.has(element.path) + }) + return { type: report.type, elements: filteredElements } + } + return report + }) +} + const isExtension = (fieldName: string): boolean => { return fieldName.startsWith('x-') } @@ -83,6 +118,7 @@ const compareComponents = (x: any, y: any): boolean => { * * @param component1 The first component that you want to compare with the second component. * @param component2 The second component. + * @param referentialEqualityCheck If `true` the function will return true if the two components have referential equality OR they have the same structure. If `false` the it will only return true if they have the same structure but they are NOT referentially equal. * @returns whether the two components are equal. */ From 802b06b63c99d7b40cf70813b1db7400d9e8f71f Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:31:11 +0100 Subject: [PATCH 06/13] add debuging logs --- src/Reporters/MoveToComponents.ts | 9 +++++++-- src/Reporters/RemoveComponents.ts | 7 +++++++ src/Reporters/ReuseComponents.ts | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Reporters/MoveToComponents.ts b/src/Reporters/MoveToComponents.ts index c4151c9e..5806fdd9 100644 --- a/src/Reporters/MoveToComponents.ts +++ b/src/Reporters/MoveToComponents.ts @@ -1,7 +1,8 @@ import { Action } from '../Optimizer' import { createReport, isEqual, isInComponents } from '../Utils' import { OptimizableComponent, OptimizableComponentGroup, ReportElement, Reporter } from 'index.d' - +import Debug from 'debug' +const debug = Debug('reporter:moveToComponents') /** * * @param optimizableComponentGroup components that you want to analyze for duplicates. @@ -49,7 +50,11 @@ const findDuplicateComponents = ( } } } - + debug( + 'duplicte %s: %O', + optimizableComponentGroup.type, + resultElements.map((element) => element.path) + ) return resultElements } diff --git a/src/Reporters/RemoveComponents.ts b/src/Reporters/RemoveComponents.ts index 45a2496a..d13643ca 100644 --- a/src/Reporters/RemoveComponents.ts +++ b/src/Reporters/RemoveComponents.ts @@ -1,6 +1,8 @@ import { Action } from '../Optimizer' import { OptimizableComponentGroup, ReportElement, Reporter } from '../index.d' import { createReport, isInComponents } from '../Utils' +import Debug from 'debug' +const debug = Debug('reporter:removeComponents') const findUnusedComponents = (componentsGroup: OptimizableComponentGroup): ReportElement[] => { const allComponents = componentsGroup.components @@ -13,6 +15,11 @@ const findUnusedComponents = (componentsGroup: OptimizableComponentGroup): Repor const unusedComponents = [...insideComponentsSection].filter( (component) => !outsideComponentsSection.has(component.component) ) + debug( + 'unused %s inside components section: %O', + componentsGroup.type, + unusedComponents.map((component) => component.path) + ) return unusedComponents.map((component) => ({ path: component.path, action: Action.Remove })) } diff --git a/src/Reporters/ReuseComponents.ts b/src/Reporters/ReuseComponents.ts index 98f4ae06..320b26e3 100644 --- a/src/Reporters/ReuseComponents.ts +++ b/src/Reporters/ReuseComponents.ts @@ -6,6 +6,8 @@ import { Reporter, } from '../index.d' import { createReport, isEqual, isInChannels, isInComponents } from '../Utils' +import Debug from 'debug' +const debug = Debug('reporter:reuseComponents') const isChannelToComponent = ( component1: OptimizableComponent, @@ -35,6 +37,10 @@ const findDuplicateComponents = ( } } } + for (const element of elements) { + debug('%s can reuse %s', element.path, element.target) + } + return elements } From e38b7410a7e25bbfbd5965db7283f6d5348dd60a Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 5 Sep 2023 12:31:54 +0100 Subject: [PATCH 07/13] update v3 test --- test/Reporters/Reporters.spec.ts | 39 +-- test/fixtures.ts | 446 +++++++++++++++++++------------ 2 files changed, 302 insertions(+), 183 deletions(-) diff --git a/test/Reporters/Reporters.spec.ts b/test/Reporters/Reporters.spec.ts index 119cb556..3736ec1e 100644 --- a/test/Reporters/Reporters.spec.ts +++ b/test/Reporters/Reporters.spec.ts @@ -4,47 +4,52 @@ import { Parser } from '@asyncapi/parser' import { getOptimizableComponents } from '../../src/ComponentProvider' import { OptimizableComponentGroup } from '../../src/index.d' -const MoveToComponentsExpectedResult = [ +const MoveToComponentsExpectedResult: any[] = [ { - path: 'channels.smartylighting/event/{streetlightId}/lighting/measured.subscribe.message.traits[0].headers', + path: 'channels.withDuplicatedMessage1.messages.duped1', action: 'move', - target: 'components.schemas.schema-1', + target: 'components.messages.message-1', }, { - path: 'channels.smartylighting/action/{streetlightId}/turn/on.publish.message.traits[0].headers', + path: 'channels.withDuplicatedMessage2.messages.duped2', action: 'reuse', - target: 'components.schemas.schema-1', + target: 'components.messages.message-1', }, { - path: 'channels.smartylighting/event/{streetlightId}/lighting/measured.subscribe.message.traits[0].headers.properties.my-app-header', + path: 'channels.UserSignedUp1', action: 'move', - target: 'components.schemas.schema-2', + target: 'components.channels.channel-1', }, { - path: 'channels.smartylighting/action/{streetlightId}/turn/on.publish.message.traits[0].headers.properties.my-app-header', + path: 'channels.UserSignedUp2', action: 'reuse', - target: 'components.schemas.schema-2', + target: 'components.channels.channel-1', }, { - path: 'channels.smartylighting/event/{streetlightId}/lighting/measured.parameters.streetlightId', + path: 'channels.withDuplicatedMessage1.messages.duped1.payload', action: 'move', - target: 'components.parameters.parameter-1', + target: 'components.schemas.schema-1', }, { - path: 'channels.smartylighting/action/{streetlightId}/turn/on.parameters.streetlightId', + path: 'channels.withDuplicatedMessage2.messages.duped2.payload', action: 'reuse', - target: 'components.parameters.parameter-1', + target: 'components.schemas.schema-1', }, ] const RemoveComponentsExpectedResult = [ - { path: 'components.messages.unusedMessage', action: 'remove' }, - { path: 'components.parameters.unusedParameter', action: 'remove' }, + { path: 'components.messages.unUsedMessage', action: 'remove' }, + { path: 'components.channels.unUsedChannel', action: 'remove' }, + { + path: 'components.messages.unUsedMessage.payload', + action: 'remove', + }, + { path: 'components.schemas.canBeReused', action: 'remove' }, ] const ReuseComponentsExpectedResult = [ { - path: 'channels.smartylighting/event/{streetlightId}/lighting/measured.subscribe.message.payload.properties.sentAt', + path: 'channels.withFullFormMessage.messages.canBeReused.payload', action: 'reuse', - target: 'components.schemas.sentAt', + target: 'components.schemas.canBeReused', }, ] diff --git a/test/fixtures.ts b/test/fixtures.ts index a9fb270b..43ac0879 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -1,21 +1,12 @@ -export const asyncapiYAMLWithoutComponents = `asyncapi: 2.0.0 +export const asyncapiYAMLWithoutComponents = `asyncapi: 3.0.0 info: title: Streetlights API - version: '1.0.0' + version: 1.0.0 channels: - smartylighting/event/{streetlightId}/lighting/measured: - parameters: - #this parameter is duplicated. it can be moved to components and ref-ed from here. - streetlightId: - schema: - type: string - subscribe: - operationId: receiveLightMeasurement - traits: - - bindings: - kafka: - clientId: my-app-id - message: + 'smartylighting/event/{streetlightId}/lighting/measured': + address: 'smartylighting/event/{streetlightId}/lighting/measured' + messages: + receiveLightMeasurement.message: name: lightMeasured title: Light measured contentType: application/json @@ -33,22 +24,15 @@ channels: lumens: type: integer minimum: 0 - #full form is used, we can ref it to: #/components/schemas/sentAt sentAt: type: string format: date-time - smartylighting/action/{streetlightId}/turn/on: parameters: - streetlightId: - schema: - type: string - publish: - operationId: turnOn - traits: - - bindings: - kafka: - clientId: my-app-id - message: + streetlightId: {} + 'smartylighting/action/{streetlightId}/turn/on': + address: 'smartylighting/action/{streetlightId}/turn/on' + messages: + turnOn.message: name: turnOnOff title: Turn on/off headers: @@ -65,159 +49,289 @@ channels: properties: sentAt: type: string - format: date-time`; + format: date-time + parameters: + streetlightId: {} +operations: + receiveLightMeasurement: + action: send + channel: + $ref: '#/channels/smartylighting~1event~1{streetlightId}~1lighting~1measured' + traits: + - bindings: + kafka: + clientId: + type: string + enum: ['myClientId'] -export const inputYAML = `asyncapi: 2.0.0 + messages: + - $ref: >- + #/channels/smartylighting~1event~1{streetlightId}~1lighting~1measured/messages/receiveLightMeasurement.message + turnOn: + action: receive + channel: + $ref: '#/channels/smartylighting~1action~1{streetlightId}~1turn~1on' + traits: + - bindings: + kafka: + clientId: + type: string + enum: ['myClientId'] + messages: + - $ref: >- + #/channels/smartylighting~1action~1{streetlightId}~1turn~1on/messages/turnOn.message +` + +export const inputYAML = `asyncapi: 3.0.0 info: - title: Streetlights API - version: '1.0.0' + title: Untidy AsyncAPI file + version: 1.0.0 + description: >- + This file contains duplicate and unused messages across the file and is used to test the optimizer. channels: - smartylighting/event/{streetlightId}/lighting/measured: - parameters: - #this parameter is duplicated. it can be moved to components and ref-ed from here. - streetlightId: - schema: - type: string - subscribe: - operationId: receiveLightMeasurement - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: lightMeasured - title: Light measured - contentType: application/json - traits: - - headers: - type: object - properties: - my-app-header: - type: integer - minimum: 0 - maximum: 100 + withDuplicatedMessage1: + address: user/signedup + messages: + duped1: payload: type: object - properties: - lumens: - type: integer - minimum: 0 - #full form is used, we can ref it to: #/components/schemas/sentAt - sentAt: - type: string - format: date-time - smartylighting/action/{streetlightId}/turn/on: - parameters: - streetlightId: - schema: - type: string - publish: - operationId: turnOn - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: turnOnOff - title: Turn on/off - traits: - - headers: - type: object - properties: - my-app-header: - type: integer - minimum: 0 - maximum: 100 + description: I am duplicated + withDuplicatedMessage2: + address: user/signedup + messages: + duped2: payload: type: object - properties: - sentAt: - $ref: "#/components/schemas/sentAt" + description: I am duplicated + withFullFormMessage: + address: user/signedup + messages: + canBeReused: + payload: + type: object + description: I can be reused. + UserSignedUp1: + address: user/signedup + messages: + myMessage: + $ref: '#/components/messages/UserSignedUp' + UserSignedUp2: + address: user/signedup + messages: + myMessage: + $ref: '#/components/messages/UserSignedUp' + deleteAccount: + address: user/deleteAccount + messages: + deleteUser: + $ref: '#/components/messages/DeleteUser' +operations: + user/deleteAccount.subscribe: + action: send + channel: + $ref: '#/channels/deleteAccount' + messages: + - $ref: '#/components/messages/DeleteUser' components: - messages: - #libarary should be able to find and delete this message, because it is not used anywhere. - unusedMessage: - name: unusedMessage - title: This message is not used in any channel. - + channels: + unUsedChannel: + address: user/unused + messages: + myMessage: + $ref: '#/components/messages/UserSignedUp' schemas: - #this schema is ref-ed in one channel and used full form in another. library should be able to identify and ref the second channel as well. - sentAt: - type: string - format: date-time - parameters: - unusedParameter: - schema: - type: number`; -export const outputYAML = `asyncapi: 2.0.0 + canBeReused: + type: object + description: I can be reused. + messages: + unUsedMessage: + payload: + type: boolean + DeleteUser: + payload: + type: string + description: userId of the user that is going to be deleted + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user +` +export const outputYAML = `asyncapi: 3.0.0 info: - title: Streetlights API + title: Untidy AsyncAPI file version: 1.0.0 + description: >- + This file contains duplicate and unused messages across the file and is used + to test the optimizer. channels: - smartylighting/event/{streetlightId}/lighting/measured: - parameters: - streetlightId: - $ref: '#/components/parameters/parameter-1' - subscribe: - operationId: receiveLightMeasurement - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: lightMeasured - title: Light measured - contentType: application/json - traits: - - headers: - $ref: '#/components/schemas/schema-1' - payload: - type: object - properties: - lumens: - type: integer - minimum: 0 - sentAt: - $ref: '#/components/schemas/sentAt' - smartylighting/action/{streetlightId}/turn/on: - parameters: - streetlightId: - $ref: '#/components/parameters/parameter-1' - publish: - operationId: turnOn - traits: - - bindings: - kafka: - clientId: my-app-id - message: - name: turnOnOff - title: Turn on/off - traits: - - headers: - $ref: '#/components/schemas/schema-1' + withDuplicatedMessage1: + address: user/signedup + messages: + duped1: + $ref: '#/components/messages/message-1' + withDuplicatedMessage2: + address: user/signedup + messages: + duped2: + $ref: '#/components/messages/message-1' + withFullFormMessage: + address: user/signedup + messages: + canBeReused: payload: - type: object - properties: - sentAt: - $ref: '#/components/schemas/sentAt' + $ref: '#/components/schemas/canBeReused' + UserSignedUp1: + $ref: '#/components/channels/channel-1' + UserSignedUp2: + $ref: '#/components/channels/channel-1' + deleteAccount: + address: user/deleteAccount + messages: + deleteUser: + $ref: '#/components/messages/DeleteUser' +operations: + user/deleteAccount.subscribe: + action: send + channel: + $ref: '#/channels/deleteAccount' + messages: + - $ref: '#/components/messages/DeleteUser' components: schemas: - sentAt: - type: string - format: date-time - schema-2: - type: integer - minimum: 0 - maximum: 100 + canBeReused: + type: object + description: I can be reused. schema-1: type: object - properties: - my-app-header: - $ref: '#/components/schemas/schema-2' - parameters: - parameter-1: - schema: - type: string`; - -export const inputJSON = '{"asyncapi":"2.0.0","info":{"title":"Streetlights API","version":"1.0.0"},"channels":{"smartylighting/event/{streetlightId}/lighting/measured":{"parameters":{"streetlightId":{"schema":{"type":"string"}}},"subscribe":{"operationId":"receiveLightMeasurement","traits":[{"bindings":{"kafka":{"clientId":"my-app-id"}}}],"message":{"name":"lightMeasured","title":"Light measured","contentType":"application/json","traits":[{"headers":{"type":"object","properties":{"my-app-header":{"type":"integer","minimum":0,"maximum":100}}}}],"payload":{"type":"object","properties":{"lumens":{"type":"integer","minimum":0},"sentAt":{"type":"string","format":"date-time"}}}}}},"smartylighting/action/{streetlightId}/turn/on":{"parameters":{"streetlightId":{"schema":{"type":"string"}}},"publish":{"operationId":"turnOn","traits":[{"bindings":{"kafka":{"clientId":"my-app-id"}}}],"message":{"name":"turnOnOff","title":"Turn on/off","traits":[{"headers":{"type":"object","properties":{"my-app-header":{"type":"integer","minimum":0,"maximum":100}}}}],"payload":{"type":"object","properties":{"sentAt":{"$ref":"#/components/schemas/sentAt"}}}}}}},"components":{"messages":{"unusedMessage":{"name":"unusedMessage","title":"This message is not used in any channel."}},"schemas":{"sentAt":{"type":"string","format":"date-time"}},"parameters":{"unusedParameter":{"schema":{"type":"number"}}}}}'; -export const outputJSON = '{"asyncapi":"2.0.0","info":{"title":"Streetlights API","version":"1.0.0"},"channels":{"smartylighting/event/{streetlightId}/lighting/measured":{"parameters":{"streetlightId":{"$ref":"#/components/parameters/parameter-1"}},"subscribe":{"operationId":"receiveLightMeasurement","traits":[{"bindings":{"kafka":{"clientId":"my-app-id"}}}],"message":{"name":"lightMeasured","title":"Light measured","contentType":"application/json","traits":[{"headers":{"$ref":"#/components/schemas/schema-1"}}],"payload":{"type":"object","properties":{"lumens":{"type":"integer","minimum":0},"sentAt":{"$ref":"#/components/schemas/sentAt"}}}}}},"smartylighting/action/{streetlightId}/turn/on":{"parameters":{"streetlightId":{"$ref":"#/components/parameters/parameter-1"}},"publish":{"operationId":"turnOn","traits":[{"bindings":{"kafka":{"clientId":"my-app-id"}}}],"message":{"name":"turnOnOff","title":"Turn on/off","traits":[{"headers":{"$ref":"#/components/schemas/schema-1"}}],"payload":{"type":"object","properties":{"sentAt":{"$ref":"#/components/schemas/sentAt"}}}}}}},"components":{"schemas":{"sentAt":{"type":"string","format":"date-time"},"schema-2":{"type":"integer","minimum":0,"maximum":100},"schema-1":{"type":"object","properties":{"my-app-header":{"$ref":"#/components/schemas/schema-2"}}}},"parameters":{"parameter-1":{"schema":{"type":"string"}}}}}'; + description: I am duplicated + messages: + DeleteUser: + payload: + type: string + description: userId of the user that is going to be deleted + UserSignedUp: + payload: + type: object + properties: + displayName: + type: string + description: Name of the user + email: + type: string + format: email + description: Email of the user + message-1: + payload: + $ref: '#/components/schemas/schema-1' + channels: + channel-1: + address: user/signedup + messages: + myMessage: + $ref: '#/components/messages/UserSignedUp' +` + +export const inputJSON = `{ + 'asyncapi': '3.0.0', + 'info': + { + 'title': 'Untidy AsyncAPI file', + 'version': '1.0.0', + 'description': 'This file contains duplicate and unused messages across the file and is used to test the optimizer.', + }, + 'channels': + { + 'withDuplicatedMessage1': + { + 'address': 'user/signedup', + 'messages': + { 'duped1': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, + }, + 'withDuplicatedMessage2': + { + 'address': 'user/signedup', + 'messages': + { 'duped2': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, + }, + 'withFullFormMessage': + { + 'address': 'user/signedup', + 'messages': + { + 'canBeReused': { 'payload': { 'type': 'object', 'description': 'I can be reused.' } }, + }, + }, + 'UserSignedUp1': + { + 'address': 'user/signedup', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + 'UserSignedUp2': + { + 'address': 'user/signedup', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + 'deleteAccount': + { + 'address': 'user/deleteAccount', + 'messages': { 'deleteUser': { '$ref': '#/components/messages/DeleteUser' } }, + }, + }, + 'operations': + { + 'user/deleteAccount.subscribe': + { + 'action': 'send', + 'channel': { '$ref': '#/channels/deleteAccount' }, + 'messages': [{ '$ref': '#/components/messages/DeleteUser' }], + }, + }, + 'components': + { + 'channels': + { + 'unUsedChannel': + { + 'address': 'user/unused', + 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, + }, + }, + 'schemas': { 'canBeReused': { 'type': 'object', 'description': 'I can be reused.' } }, + 'messages': + { + 'unUsedMessage': { 'payload': { 'type': 'boolean' } }, + 'DeleteUser': + { + 'payload': + { + 'type': 'string', + 'description': 'userId of the user that is going to be deleted', + }, + }, + 'UserSignedUp': + { + 'payload': + { + 'type': 'object', + 'properties': + { + 'displayName': { 'type': 'string', 'description': 'Name of the user' }, + 'email': + { 'type': 'string', 'format': 'email', 'description': 'Email of the user' }, + }, + }, + }, + }, + }, +} +` + +// eslint-disable-next-line quotes +export const outputJSON = `{"asyncapi":"3.0.0","info":{"title":"Untidy AsyncAPI file","version":"1.0.0","description":"This file contains duplicate and unused messages across the file and is used to test the optimizer."},"channels":{"withDuplicatedMessage1":{"address":"user/signedup","messages":{"duped1":{"$ref":"#/components/messages/message-1"}}},"withDuplicatedMessage2":{"address":"user/signedup","messages":{"duped2":{"$ref":"#/components/messages/message-1"}}},"withFullFormMessage":{"address":"user/signedup","messages":{"canBeReused":{"payload":{"$ref":"#/components/schemas/canBeReused"}}}},"UserSignedUp1":{"$ref":"#/components/channels/channel-1"},"UserSignedUp2":{"$ref":"#/components/channels/channel-1"},"deleteAccount":{"address":"user/deleteAccount","messages":{"deleteUser":{"$ref":"#/components/messages/DeleteUser"}}}},"operations":{"user/deleteAccount.subscribe":{"action":"send","channel":{"$ref":"#/channels/deleteAccount"},"messages":[{"$ref":"#/components/messages/DeleteUser"}]}},"components":{"schemas":{"canBeReused":{"type":"object","description":"I can be reused."},"schema-1":{"type":"object","description":"I am duplicated"}},"messages":{"DeleteUser":{"payload":{"type":"string","description":"userId of the user that is going to be deleted"}},"UserSignedUp":{"payload":{"type":"object","properties":{"displayName":{"type":"string","description":"Name of the user"},"email":{"type":"string","format":"email","description":"Email of the user"}}}},"message-1":{"payload":{"$ref":"#/components/schemas/schema-1"}}},"channels":{"channel-1":{"address":"user/signedup","messages":{"myMessage":{"$ref":"#/components/messages/UserSignedUp"}}}}}}` From 927c20b4decbf1365e1473b7bef29d20fa4ddda9 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Thu, 5 Oct 2023 14:24:41 +0100 Subject: [PATCH 08/13] turn off wrap regex --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index c33c1ff6..43b209de 100644 --- a/.eslintrc +++ b/.eslintrc @@ -121,7 +121,7 @@ } ], "no-console": "error", - "wrap-regex": 2, + "wrap-regex": 0, //"linebreak-style": ["error", "unix"], "linebreak-style": 0, "arrow-spacing": [ From 12a029865b2d0109e37a28addb6d14b5c8f57c85 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 19 Dec 2023 10:57:18 +0000 Subject: [PATCH 09/13] use latest version of parser --- package-lock.json | 246 +++++++++------------------------------------- package.json | 4 +- 2 files changed, 47 insertions(+), 203 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b059802..8b31dd51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.2.1", "license": "Apache-2.0", "dependencies": { - "@asyncapi/parser": "v3.0.0-next-major-spec.2", + "@asyncapi/parser": "^3.0.2", "@types/debug": "^4.1.8", "debug": "^4.3.4", "js-yaml": "^4.1.0", @@ -38,16 +38,20 @@ } }, "node_modules/@asyncapi/parser": { - "version": "3.0.0-next-major-spec.2", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.2.tgz", - "integrity": "sha512-/gJgCYNYlUSDJhySK3IagjiyFfnwEsAZd5rTe396CB+HxJ6yDWDOZYdHzkFgU2RnCULSGzVOWZGx8t3PK+yuVg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.2.tgz", + "integrity": "sha512-AtDFndWwnaqGoXZQY2DRtORT2Ls4BI7MSR+Rg7TRwxf5jxIz/WVvQwc5HElkHuDEkIZslYu+ukFzNq3awdj0aw==", "dependencies": { - "@asyncapi/specs": "^6.0.0-next-major-spec.6", + "@asyncapi/specs": "^6.2.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json": "^3.20.2", + "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-ref-resolver": "^1.0.3", + "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.11.0", @@ -56,9 +60,7 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7", - "ramldt2jsonschema": "^1.2.3", - "webapi-parser": "^0.5.0" + "node-fetch": "2.6.7" } }, "node_modules/@asyncapi/parser/node_modules/ajv": { @@ -98,9 +100,9 @@ } }, "node_modules/@asyncapi/specs": { - "version": "6.0.0-next-major-spec.6", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.6.tgz", - "integrity": "sha512-xYiXZetKiAZgZoh2q/sxt+Ceg8bLTK/WSCwtLvlH7JgnjpnOEtCDwKszijmmOJEYdGVSrj0OFXLPrg/dm4JANw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.2.0.tgz", + "integrity": "sha512-5uf/Rg6pavZHx7rVIkP0TP/icIahJCuHgmY1rdtkrWxHZMXbASDDV3DlTUaonbsUeemwchoqljmrTd1O1xqvxg==", "dependencies": { "@types/json-schema": "^7.0.11" } @@ -2425,6 +2427,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } @@ -2827,6 +2830,7 @@ "node_modules/co": { "version": "4.6.0", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -2965,14 +2969,6 @@ "node": ">=4" } }, - "node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "engines": { - "node": ">= 6" - } - }, "node_modules/common-sequence": { "version": "2.0.2", "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", @@ -3708,6 +3704,7 @@ "node_modules/esprima": { "version": "4.0.1", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3924,7 +3921,8 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -6525,38 +6523,10 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema-migrate": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", - "integrity": "sha512-dq4/oHWmtw/+0ytnXsDqVn+VsVweTEmzm5jLgguPn9BjSzn6/q58ZiZx3BHiQyJs612f0T5Z+MrUEUUY5DHsRg==", - "dependencies": { - "ajv": "^5.0.0" - } - }, - "node_modules/json-schema-migrate/node_modules/ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", - "dependencies": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "node_modules/json-schema-migrate/node_modules/fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==" - }, - "node_modules/json-schema-migrate/node_modules/json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -7566,33 +7536,6 @@ } ] }, - "node_modules/ramldt2jsonschema": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/ramldt2jsonschema/-/ramldt2jsonschema-1.2.3.tgz", - "integrity": "sha512-+wLDAV2NNv9NkfEUOYStaDu/6RYgYXeC1zLtXE+dMU/jDfjpN4iJnBGycDwFTFaIQGosOQhxph7fEX6Mpwxdug==", - "dependencies": { - "commander": "^5.0.0", - "js-yaml": "^3.14.0", - "json-schema-migrate": "^0.2.0", - "webapi-parser": "^0.5.0" - }, - "bin": { - "dt2js": "bin/dt2js.js", - "js2dt": "bin/js2dt.js" - } - }, - "node_modules/ramldt2jsonschema/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/randomatic": { "version": "3.1.1", "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", @@ -8218,7 +8161,8 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "node_modules/stack-utils": { "version": "2.0.6", @@ -8897,30 +8841,6 @@ "makeerror": "1.0.12" } }, - "node_modules/webapi-parser": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/webapi-parser/-/webapi-parser-0.5.0.tgz", - "integrity": "sha512-fPt6XuMqLSvBz8exwX4QE1UT+pROLHa00EMDCdO0ybICduwQ1V4f7AWX4pNOpCp+x+0FjczEsOxtQU0d8L3QKw==", - "dependencies": { - "ajv": "6.5.2" - } - }, - "node_modules/webapi-parser/node_modules/ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dependencies": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "node_modules/webapi-parser/node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" - }, "node_modules/webidl-conversions": { "version": "3.0.1", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" @@ -9112,16 +9032,20 @@ }, "dependencies": { "@asyncapi/parser": { - "version": "3.0.0-next-major-spec.2", - "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.2.tgz", - "integrity": "sha512-/gJgCYNYlUSDJhySK3IagjiyFfnwEsAZd5rTe396CB+HxJ6yDWDOZYdHzkFgU2RnCULSGzVOWZGx8t3PK+yuVg==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.2.tgz", + "integrity": "sha512-AtDFndWwnaqGoXZQY2DRtORT2Ls4BI7MSR+Rg7TRwxf5jxIz/WVvQwc5HElkHuDEkIZslYu+ukFzNq3awdj0aw==", "requires": { - "@asyncapi/specs": "^6.0.0-next-major-spec.6", + "@asyncapi/specs": "^6.2.0", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", + "@stoplight/json": "^3.20.2", + "@stoplight/json-ref-readers": "^1.2.2", "@stoplight/json-ref-resolver": "^3.1.5", "@stoplight/spectral-core": "^1.16.1", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", + "@stoplight/spectral-ref-resolver": "^1.0.3", + "@stoplight/types": "^13.12.0", "@types/json-schema": "^7.0.11", "@types/urijs": "^1.19.19", "ajv": "^8.11.0", @@ -9130,9 +9054,7 @@ "avsc": "^5.7.5", "js-yaml": "^4.1.0", "jsonpath-plus": "^7.2.0", - "node-fetch": "2.6.7", - "ramldt2jsonschema": "^1.2.3", - "webapi-parser": "^0.5.0" + "node-fetch": "2.6.7" }, "dependencies": { "ajv": { @@ -9165,9 +9087,9 @@ } }, "@asyncapi/specs": { - "version": "6.0.0-next-major-spec.6", - "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.0.0-next-major-spec.6.tgz", - "integrity": "sha512-xYiXZetKiAZgZoh2q/sxt+Ceg8bLTK/WSCwtLvlH7JgnjpnOEtCDwKszijmmOJEYdGVSrj0OFXLPrg/dm4JANw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-6.2.0.tgz", + "integrity": "sha512-5uf/Rg6pavZHx7rVIkP0TP/icIahJCuHgmY1rdtkrWxHZMXbASDDV3DlTUaonbsUeemwchoqljmrTd1O1xqvxg==", "requires": { "@types/json-schema": "^7.0.11" } @@ -10957,6 +10879,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -11252,7 +11175,8 @@ }, "co": { "version": "4.6.0", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==" + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true }, "coffee-script": { "version": "1.12.7", @@ -11358,11 +11282,6 @@ } } }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" - }, "common-sequence": { "version": "2.0.2", "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", @@ -11921,7 +11840,8 @@ }, "esprima": { "version": "4.0.1", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "esquery": { "version": "1.4.0", @@ -12083,7 +12003,8 @@ }, "fast-json-stable-stringify": { "version": "2.1.0", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -14026,40 +13947,10 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-migrate": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/json-schema-migrate/-/json-schema-migrate-0.2.0.tgz", - "integrity": "sha512-dq4/oHWmtw/+0ytnXsDqVn+VsVweTEmzm5jLgguPn9BjSzn6/q58ZiZx3BHiQyJs612f0T5Z+MrUEUUY5DHsRg==", - "requires": { - "ajv": "^5.0.0" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha512-Ajr4IcMXq/2QmMkEmSvxqfLN5zGmJ92gHXAeOXq1OekoH2rfDNsgdDoL2f7QaRCy7G/E6TpxBVdRuNraMztGHw==", - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==" - } - } - }, "json-schema-traverse": { "version": "0.4.1", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -14807,28 +14698,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "ramldt2jsonschema": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/ramldt2jsonschema/-/ramldt2jsonschema-1.2.3.tgz", - "integrity": "sha512-+wLDAV2NNv9NkfEUOYStaDu/6RYgYXeC1zLtXE+dMU/jDfjpN4iJnBGycDwFTFaIQGosOQhxph7fEX6Mpwxdug==", - "requires": { - "commander": "^5.0.0", - "js-yaml": "^3.14.0", - "json-schema-migrate": "^0.2.0", - "webapi-parser": "^0.5.0" - }, - "dependencies": { - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, "randomatic": { "version": "3.1.1", "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", @@ -15299,7 +15168,8 @@ }, "sprintf-js": { "version": "1.0.3", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, "stack-utils": { "version": "2.0.6", @@ -15806,32 +15676,6 @@ "makeerror": "1.0.12" } }, - "webapi-parser": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/webapi-parser/-/webapi-parser-0.5.0.tgz", - "integrity": "sha512-fPt6XuMqLSvBz8exwX4QE1UT+pROLHa00EMDCdO0ybICduwQ1V4f7AWX4pNOpCp+x+0FjczEsOxtQU0d8L3QKw==", - "requires": { - "ajv": "6.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==" - } - } - }, "webidl-conversions": { "version": "3.0.1", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" diff --git a/package.json b/package.json index 9b7825fb..061e694d 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "access": "public" }, "dependencies": { - "@asyncapi/parser": "v3.0.0-next-major-spec.2", + "@asyncapi/parser": "^3.0.2", "@types/debug": "^4.1.8", "debug": "^4.3.4", "js-yaml": "^4.1.0", @@ -65,4 +65,4 @@ "merge-deep": "^3.0.3", "yaml": "^2.3.1" } -} +} \ No newline at end of file From 5164bebe63c9ea49db54a9d98ef04d0d685f54a8 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 19 Dec 2023 11:13:23 +0000 Subject: [PATCH 10/13] fix tests --- test/fixtures.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fixtures.ts b/test/fixtures.ts index 43ac0879..1a112d4c 100644 --- a/test/fixtures.ts +++ b/test/fixtures.ts @@ -131,7 +131,7 @@ operations: channel: $ref: '#/channels/deleteAccount' messages: - - $ref: '#/components/messages/DeleteUser' + - $ref: '#/channels/deleteAccount/messages/deleteUser' components: channels: unUsedChannel: @@ -202,7 +202,7 @@ operations: channel: $ref: '#/channels/deleteAccount' messages: - - $ref: '#/components/messages/DeleteUser' + - $ref: '#/channels/deleteAccount/messages/deleteUser' components: schemas: canBeReused: @@ -290,7 +290,7 @@ export const inputJSON = `{ { 'action': 'send', 'channel': { '$ref': '#/channels/deleteAccount' }, - 'messages': [{ '$ref': '#/components/messages/DeleteUser' }], + 'messages': [{ '$ref': '#/channels/deleteAccount/messages/deleteUser' }], }, }, 'components': @@ -334,4 +334,4 @@ export const inputJSON = `{ ` // eslint-disable-next-line quotes -export const outputJSON = `{"asyncapi":"3.0.0","info":{"title":"Untidy AsyncAPI file","version":"1.0.0","description":"This file contains duplicate and unused messages across the file and is used to test the optimizer."},"channels":{"withDuplicatedMessage1":{"address":"user/signedup","messages":{"duped1":{"$ref":"#/components/messages/message-1"}}},"withDuplicatedMessage2":{"address":"user/signedup","messages":{"duped2":{"$ref":"#/components/messages/message-1"}}},"withFullFormMessage":{"address":"user/signedup","messages":{"canBeReused":{"payload":{"$ref":"#/components/schemas/canBeReused"}}}},"UserSignedUp1":{"$ref":"#/components/channels/channel-1"},"UserSignedUp2":{"$ref":"#/components/channels/channel-1"},"deleteAccount":{"address":"user/deleteAccount","messages":{"deleteUser":{"$ref":"#/components/messages/DeleteUser"}}}},"operations":{"user/deleteAccount.subscribe":{"action":"send","channel":{"$ref":"#/channels/deleteAccount"},"messages":[{"$ref":"#/components/messages/DeleteUser"}]}},"components":{"schemas":{"canBeReused":{"type":"object","description":"I can be reused."},"schema-1":{"type":"object","description":"I am duplicated"}},"messages":{"DeleteUser":{"payload":{"type":"string","description":"userId of the user that is going to be deleted"}},"UserSignedUp":{"payload":{"type":"object","properties":{"displayName":{"type":"string","description":"Name of the user"},"email":{"type":"string","format":"email","description":"Email of the user"}}}},"message-1":{"payload":{"$ref":"#/components/schemas/schema-1"}}},"channels":{"channel-1":{"address":"user/signedup","messages":{"myMessage":{"$ref":"#/components/messages/UserSignedUp"}}}}}}` +export const outputJSON = `{"asyncapi":"3.0.0","info":{"title":"Untidy AsyncAPI file","version":"1.0.0","description":"This file contains duplicate and unused messages across the file and is used to test the optimizer."},"channels":{"withDuplicatedMessage1":{"address":"user/signedup","messages":{"duped1":{"$ref":"#/components/messages/message-1"}}},"withDuplicatedMessage2":{"address":"user/signedup","messages":{"duped2":{"$ref":"#/components/messages/message-1"}}},"withFullFormMessage":{"address":"user/signedup","messages":{"canBeReused":{"payload":{"$ref":"#/components/schemas/canBeReused"}}}},"UserSignedUp1":{"$ref":"#/components/channels/channel-1"},"UserSignedUp2":{"$ref":"#/components/channels/channel-1"},"deleteAccount":{"address":"user/deleteAccount","messages":{"deleteUser":{"$ref":"#/components/messages/DeleteUser"}}}},"operations":{"user/deleteAccount.subscribe":{"action":"send","channel":{"$ref":"#/channels/deleteAccount"},"messages":[{"$ref":"#/channels/deleteAccount/messages/deleteUser"}]}},"components":{"schemas":{"canBeReused":{"type":"object","description":"I can be reused."},"schema-1":{"type":"object","description":"I am duplicated"}},"messages":{"DeleteUser":{"payload":{"type":"string","description":"userId of the user that is going to be deleted"}},"UserSignedUp":{"payload":{"type":"object","properties":{"displayName":{"type":"string","description":"Name of the user"},"email":{"type":"string","format":"email","description":"Email of the user"}}}},"message-1":{"payload":{"$ref":"#/components/schemas/schema-1"}}},"channels":{"channel-1":{"address":"user/signedup","messages":{"myMessage":{"$ref":"#/components/messages/UserSignedUp"}}}}}}` From 1bed69ca003c6ba36ea8ffa16a11574c70007d4e Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 19 Dec 2023 12:49:58 +0000 Subject: [PATCH 11/13] parser components from everywhere. --- package.json | 2 +- src/ComponentProvider.ts | 31 ++++++++++++++++++++++++++----- src/Utils/Helpers.ts | 4 ++-- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 7e1bd9c2..590a6608 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "test": "jest --coverage", "test:watch": "jest --watch", "build": "tsc", - "build:watch": "tsc --watch", + "dev": "tsc --watch", "docs": "jsdoc2md lib/Optimizer.js -f lib/**/*.js > API.md", "example": "tsc && node examples/index.js", "lint": "eslint --max-warnings 0 --config .eslintrc .", diff --git a/src/ComponentProvider.ts b/src/ComponentProvider.ts index 307e9e0d..7e8e6c78 100644 --- a/src/ComponentProvider.ts +++ b/src/ComponentProvider.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable security/detect-object-injection */ import type { AsyncAPIDocumentInterface } from '@asyncapi/parser' import { OptimizableComponentGroup, OptimizableComponent } from 'index.d' @@ -55,12 +57,31 @@ export const getOptimizableComponents = ( ): OptimizableComponentGroup[] => { const optimizeableComponents: OptimizableComponentGroup[] = [] + const getAllComponents = (type: string) => { + // @ts-ignore + if (typeof asyncAPIDocument[type] !== 'function') return [] + // @ts-ignore + return asyncAPIDocument[type]().all().concat(asyncAPIDocument.components()[type]().all()); + }; const optimizableComponents = { - servers: asyncAPIDocument.allServers().all(), - messages: asyncAPIDocument.allMessages().all(), - channels: asyncAPIDocument.allChannels().all(), - schemas: asyncAPIDocument.allSchemas().all(), - operations: asyncAPIDocument.allOperations().all(), + servers: getAllComponents('servers'), + messages: getAllComponents('messages'), + channels: getAllComponents('channels'), + schemas: getAllComponents('schemas'), + operations: getAllComponents('operations'), + securitySchemes: getAllComponents('securitySchemes'), + serverVariables: getAllComponents('serverVariables'), + parameters: getAllComponents('parameters'), + correlationIds: getAllComponents('correlationIds'), + replies: getAllComponents('replies'), + externalDocs: getAllComponents('externalDocs'), + tags: getAllComponents('tags'), + operationTraits: getAllComponents('operationTraits'), + messageTraits: getAllComponents('messageTraits'), + serverBindings: getAllComponents('serverBindings'), + channelBindings: getAllComponents('channelBindings'), + operationBindings: getAllComponents('operationBindings'), + messageBindings: getAllComponents('messageBindings'), } for (const [type, components] of Object.entries(optimizableComponents)) { if (components.length === 0) continue diff --git a/src/Utils/Helpers.ts b/src/Utils/Helpers.ts index 39231a2e..646bc8af 100644 --- a/src/Utils/Helpers.ts +++ b/src/Utils/Helpers.ts @@ -124,9 +124,9 @@ const compareComponents = (x: any, y: any): boolean => { */ const isEqual = (component1: any, component2: any, referentialEqualityCheck: boolean): boolean => { if (referentialEqualityCheck) { - return component1 === component2 || compareComponents(component1, component2) + return component1 === component2 || compareComponents(component1, component2); } - return component1 !== component2 && compareComponents(component1, component2) + return compareComponents(component1, component2); } /** From a126ae4cd5cde44dccd5fcd7ffc4cf367e149560 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 19 Dec 2023 15:10:55 +0000 Subject: [PATCH 12/13] fix tests --- examples/index.js | 2 +- examples/input.yaml | 346 ++++++++++++++++++++++--------- examples/output.yaml | 232 ++++++++++++++++++++- src/Utils/Helpers.ts | 5 +- test/Reporters/Reporters.spec.ts | 4 - 5 files changed, 487 insertions(+), 102 deletions(-) diff --git a/examples/index.js b/examples/index.js index 4211689c..ddf4f36b 100644 --- a/examples/index.js +++ b/examples/index.js @@ -7,7 +7,7 @@ const optimizer = new Optimizer(input) optimizer.getReport().then((report) => { console.log(JSON.stringify(report)) const optimizedDocument = optimizer.getOptimizedDocument({ - output: 'JSON', + output: 'YAML', rules: { reuseComponents: true, removeComponents: true, diff --git a/examples/input.yaml b/examples/input.yaml index 2e661883..c94091c6 100644 --- a/examples/input.yaml +++ b/examples/input.yaml @@ -1,93 +1,253 @@ -{ - 'asyncapi': '3.0.0', - 'info': - { - 'title': 'Untidy AsyncAPI file', - 'version': '1.0.0', - 'description': 'This file contains duplicate and unused messages across the file and is used to test the optimizer.', - }, - 'channels': - { - 'withDuplicatedMessage1': - { - 'address': 'user/signedup', - 'messages': - { 'duped1': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, - }, - 'withDuplicatedMessage2': - { - 'address': 'user/signedup', - 'messages': - { 'duped2': { 'payload': { 'type': 'object', 'description': 'I am duplicated' } } }, - }, - 'withFullFormMessage': - { - 'address': 'user/signedup', - 'messages': - { - 'canBeReused': { 'payload': { 'type': 'object', 'description': 'I can be reused.' } }, - }, - }, - 'UserSignedUp1': - { - 'address': 'user/signedup', - 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, - }, - 'UserSignedUp2': - { - 'address': 'user/signedup', - 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, - }, - 'deleteAccount': - { - 'address': 'user/deleteAccount', - 'messages': { 'deleteUser': { '$ref': '#/components/messages/DeleteUser' } }, - }, - }, - 'operations': - { - 'user/deleteAccount.subscribe': - { - 'action': 'send', - 'channel': { '$ref': '#/channels/deleteAccount' }, - 'messages': [{ '$ref': '#/components/messages/DeleteUser' }], - }, - }, - 'components': - { - 'channels': - { - 'unUsedChannel': - { - 'address': 'user/unused', - 'messages': { 'myMessage': { '$ref': '#/components/messages/UserSignedUp' } }, - }, - }, - 'schemas': { 'canBeReused': { 'type': 'object', 'description': 'I can be reused.' } }, - 'messages': - { - 'unUsedMessage': { 'payload': { 'type': 'boolean' } }, - 'DeleteUser': - { - 'payload': - { - 'type': 'string', - 'description': 'userId of the user that is going to be deleted', - }, - }, - 'UserSignedUp': - { - 'payload': - { - 'type': 'object', - 'properties': - { - 'displayName': { 'type': 'string', 'description': 'Name of the user' }, - 'email': - { 'type': 'string', 'format': 'email', 'description': 'Email of the user' }, - }, - }, - }, - }, - }, -} +asyncapi: 3.0.0 +info: + title: Streetlights MQTT API + version: 1.0.0 + description: "The Smartylighting Streetlights API allows you to remotely manage the city lights.\n\n### Check out its awesome features:\n\n* Turn a specific streetlight on/off \U0001F303\n* Dim a specific streetlight \U0001F60E\n* Receive real-time information about environmental lighting conditions \U0001F4C8\n" + license: + name: Apache 2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0' +defaultContentType: application/json +servers: + production: + host: 'test.mosquitto.org:{port}' + protocol: mqtt + description: Test broker + variables: + port: + description: Secure connection (TLS) is available through port 8883. + default: '1883' + enum: + - '1883' + - '8883' + security: + - $ref: '#/components/securitySchemes/apiKey' + - type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: 'https://authserver.example/auth' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + password: + tokenUrl: 'https://authserver.example/token' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + clientCredentials: + tokenUrl: 'https://authserver.example/token' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + authorizationCode: + authorizationUrl: 'https://authserver.example/auth' + tokenUrl: 'https://authserver.example/token' + refreshUrl: 'https://authserver.example/refresh' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + scopes: + - 'streetlights:on' + - 'streetlights:off' + - 'streetlights:dim' + - $ref: '#/components/securitySchemes/openIdConnectWellKnown' + tags: + - name: 'env:production' + description: This environment is meant for production use case + - name: 'kind:remote' + description: This server is a remote server. Not exposed by the application + - name: 'visibility:public' + description: This resource is public and available to everyone +channels: + lightingMeasured: + address: 'smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured' + messages: + lightMeasured: + $ref: '#/components/messages/lightMeasured' + description: The topic on which measured values may be produced and consumed. + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + lightTurnOn: + address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/on' + messages: + turnOn: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + lightTurnOff: + address: 'smartylighting/streetlights/1/0/action/{streetlightId}/turn/off' + messages: + turnOff: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + lightsDim: + address: 'smartylighting/streetlights/1/0/action/{streetlightId}/dim' + messages: + dimLight: + $ref: '#/components/messages/dimLight' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' +operations: + receiveLightMeasurement: + action: receive + channel: + $ref: '#/channels/lightingMeasured' + summary: >- + Inform about environmental lighting conditions of a particular + streetlight. + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightingMeasured/messages/lightMeasured' + turnOn: + action: send + channel: + $ref: '#/channels/lightTurnOn' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightTurnOn/messages/turnOn' + turnOff: + action: send + channel: + $ref: '#/channels/lightTurnOff' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightTurnOff/messages/turnOff' + dimLight: + action: send + channel: + $ref: '#/channels/lightsDim' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightsDim/messages/dimLight' +components: + messages: + lightMeasured: + name: lightMeasured + title: Light measured + summary: >- + Inform about environmental lighting conditions of a particular + streetlight. + contentType: application/json + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/lightMeasuredPayload' + turnOnOff: + name: turnOnOff + title: Turn on/off + summary: Command a particular streetlight to turn the lights on or off. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/turnOnOffPayload' + dimLight: + name: dimLight + title: Dim light + summary: Command a particular streetlight to dim the lights. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/dimLightPayload' + schemas: + lightMeasuredPayload: + type: object + properties: + lumens: + type: integer + minimum: 0 + description: Light intensity measured in lumens. + sentAt: + $ref: '#/components/schemas/sentAt' + turnOnOffPayload: + type: object + properties: + command: + type: string + enum: + - 'on' + - 'off' + description: Whether to turn on or off the light. + sentAt: + $ref: '#/components/schemas/sentAt' + dimLightPayload: + type: object + properties: + percentage: + type: integer + description: Percentage to which the light should be dimmed to. + minimum: 0 + maximum: 100 + sentAt: + $ref: '#/components/schemas/sentAt' + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + securitySchemes: + apiKey: + type: apiKey + in: user + description: Provide your API key as the user and leave the password empty. + supportedOauthFlows: + type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: 'https://authserver.example/auth' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + password: + tokenUrl: 'https://authserver.example/token' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + clientCredentials: + tokenUrl: 'https://authserver.example/token' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + authorizationCode: + authorizationUrl: 'https://authserver.example/auth' + tokenUrl: 'https://authserver.example/token' + refreshUrl: 'https://authserver.example/refresh' + availableScopes: + 'streetlights:on': Ability to switch lights on + 'streetlights:off': Ability to switch lights off + 'streetlights:dim': Ability to dim the lights + openIdConnectWellKnown: + type: openIdConnect + openIdConnectUrl: 'https://authserver.example/.well-known' + parameters: + streetlightId: + description: The ID of the streetlight. + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + operationTraits: + mqtt: + bindings: + mqtt: + qos: 1 \ No newline at end of file diff --git a/examples/output.yaml b/examples/output.yaml index 324a5ab3..d57614e7 100644 --- a/examples/output.yaml +++ b/examples/output.yaml @@ -1 +1,231 @@ -{"asyncapi":"3.0.0","info":{"title":"Untidy AsyncAPI file","version":"1.0.0","description":"This file contains duplicate and unused messages across the file and is used to test the optimizer."},"channels":{"withDuplicatedMessage1":{"address":"user/signedup","messages":{"duped1":{"$ref":"#/components/messages/message-1"}}},"withDuplicatedMessage2":{"address":"user/signedup","messages":{"duped2":{"$ref":"#/components/messages/message-1"}}},"withFullFormMessage":{"address":"user/signedup","messages":{"canBeReused":{"payload":{"$ref":"#/components/schemas/canBeReused"}}}},"UserSignedUp1":{"$ref":"#/components/channels/channel-1"},"UserSignedUp2":{"$ref":"#/components/channels/channel-1"},"deleteAccount":{"address":"user/deleteAccount","messages":{"deleteUser":{"$ref":"#/components/messages/DeleteUser"}}}},"operations":{"user/deleteAccount.subscribe":{"action":"send","channel":{"$ref":"#/channels/deleteAccount"},"messages":[{"$ref":"#/components/messages/DeleteUser"}]}},"components":{"schemas":{"canBeReused":{"type":"object","description":"I can be reused."},"schema-1":{"type":"object","description":"I am duplicated"}},"messages":{"DeleteUser":{"payload":{"type":"string","description":"userId of the user that is going to be deleted"}},"UserSignedUp":{"payload":{"type":"object","properties":{"displayName":{"type":"string","description":"Name of the user"},"email":{"type":"string","format":"email","description":"Email of the user"}}}},"message-1":{"payload":{"$ref":"#/components/schemas/schema-1"}}},"channels":{"channel-1":{"address":"user/signedup","messages":{"myMessage":{"$ref":"#/components/messages/UserSignedUp"}}}}}} \ No newline at end of file +asyncapi: 3.0.0 +info: + title: Streetlights MQTT API + version: 1.0.0 + description: > + The Smartylighting Streetlights API allows you to remotely manage the city + lights. + + + ### Check out its awesome features: + + + * Turn a specific streetlight on/off 🌃 + + * Dim a specific streetlight 😎 + + * Receive real-time information about environmental lighting conditions 📈 + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +defaultContentType: application/json +servers: + production: + host: test.mosquitto.org:{port} + protocol: mqtt + description: Test broker + variables: + port: + description: Secure connection (TLS) is available through port 8883. + default: '1883' + enum: + - '1883' + - '8883' + security: + - $ref: '#/components/securitySchemes/apiKey' + - type: oauth2 + description: Flows to support OAuth 2.0 + flows: + implicit: + authorizationUrl: https://authserver.example/auth + availableScopes: + streetlights:on: Ability to switch lights on + streetlights:off: Ability to switch lights off + streetlights:dim: Ability to dim the lights + password: + tokenUrl: https://authserver.example/token + availableScopes: + streetlights:on: Ability to switch lights on + streetlights:off: Ability to switch lights off + streetlights:dim: Ability to dim the lights + clientCredentials: + tokenUrl: https://authserver.example/token + availableScopes: + streetlights:on: Ability to switch lights on + streetlights:off: Ability to switch lights off + streetlights:dim: Ability to dim the lights + authorizationCode: + authorizationUrl: https://authserver.example/auth + tokenUrl: https://authserver.example/token + refreshUrl: https://authserver.example/refresh + availableScopes: + streetlights:on: Ability to switch lights on + streetlights:off: Ability to switch lights off + streetlights:dim: Ability to dim the lights + scopes: + - streetlights:on + - streetlights:off + - streetlights:dim + - $ref: '#/components/securitySchemes/openIdConnectWellKnown' + tags: + - name: env:production + description: This environment is meant for production use case + - name: kind:remote + description: This server is a remote server. Not exposed by the application + - name: visibility:public + description: This resource is public and available to everyone +channels: + lightingMeasured: + address: smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured + messages: + lightMeasured: + $ref: '#/components/messages/lightMeasured' + description: The topic on which measured values may be produced and consumed. + parameters: + streetlightId: + $ref: '#/components/schemas/schema-1' + lightTurnOn: + address: smartylighting/streetlights/1/0/action/{streetlightId}/turn/on + messages: + turnOn: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/schemas/schema-1' + lightTurnOff: + address: smartylighting/streetlights/1/0/action/{streetlightId}/turn/off + messages: + turnOff: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/schemas/schema-2' + lightsDim: + address: smartylighting/streetlights/1/0/action/{streetlightId}/dim + messages: + dimLight: + $ref: '#/components/messages/dimLight' + parameters: + streetlightId: + $ref: '#/components/schemas/schema-2' +operations: + receiveLightMeasurement: + action: receive + channel: + $ref: '#/channels/lightingMeasured' + summary: >- + Inform about environmental lighting conditions of a particular + streetlight. + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightingMeasured/messages/lightMeasured' + turnOn: + action: send + channel: + $ref: '#/channels/lightTurnOn' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightTurnOn/messages/turnOn' + turnOff: + action: send + channel: + $ref: '#/channels/lightTurnOff' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightTurnOff/messages/turnOff' + dimLight: + action: send + channel: + $ref: '#/channels/lightsDim' + traits: + - $ref: '#/components/operationTraits/mqtt' + messages: + - $ref: '#/channels/lightsDim/messages/dimLight' +components: + messages: + lightMeasured: + name: lightMeasured + title: Light measured + summary: >- + Inform about environmental lighting conditions of a particular + streetlight. + contentType: application/json + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/lightMeasuredPayload' + turnOnOff: + name: turnOnOff + title: Turn on/off + summary: Command a particular streetlight to turn the lights on or off. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/turnOnOffPayload' + dimLight: + name: dimLight + title: Dim light + summary: Command a particular streetlight to dim the lights. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + payload: + $ref: '#/components/schemas/dimLightPayload' + schemas: + lightMeasuredPayload: + type: object + properties: + lumens: + type: integer + minimum: 0 + description: Light intensity measured in lumens. + sentAt: + $ref: '#/components/schemas/sentAt' + turnOnOffPayload: + type: object + properties: + command: + type: string + enum: + - 'on' + - 'off' + description: Whether to turn on or off the light. + sentAt: + $ref: '#/components/schemas/sentAt' + dimLightPayload: + type: object + properties: + percentage: + type: integer + description: Percentage to which the light should be dimmed to. + minimum: 0 + maximum: 100 + sentAt: + $ref: '#/components/schemas/sentAt' + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + schema-1: + $ref: '#/components/parameters/streetlightId' + schema-2: + $ref: '#/components/parameters/streetlightId' + parameters: + streetlightId: + description: The ID of the streetlight. + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + operationTraits: + mqtt: + bindings: + mqtt: + qos: 1 diff --git a/src/Utils/Helpers.ts b/src/Utils/Helpers.ts index 646bc8af..2bed0314 100644 --- a/src/Utils/Helpers.ts +++ b/src/Utils/Helpers.ts @@ -118,15 +118,14 @@ const compareComponents = (x: any, y: any): boolean => { * * @param component1 The first component that you want to compare with the second component. * @param component2 The second component. - * @param referentialEqualityCheck If `true` the function will return true if the two components have referential equality OR they have the same structure. If `false` the it will only return true if they have the same structure but they are NOT referentially equal. * @returns whether the two components are equal. */ const isEqual = (component1: any, component2: any, referentialEqualityCheck: boolean): boolean => { if (referentialEqualityCheck) { - return component1 === component2 || compareComponents(component1, component2); + return component1 === component2 || compareComponents(component1, component2) } - return compareComponents(component1, component2); + return component1 !== component2 && compareComponents(component1, component2) } /** diff --git a/test/Reporters/Reporters.spec.ts b/test/Reporters/Reporters.spec.ts index 3736ec1e..067a8bba 100644 --- a/test/Reporters/Reporters.spec.ts +++ b/test/Reporters/Reporters.spec.ts @@ -39,10 +39,6 @@ const MoveToComponentsExpectedResult: any[] = [ const RemoveComponentsExpectedResult = [ { path: 'components.messages.unUsedMessage', action: 'remove' }, { path: 'components.channels.unUsedChannel', action: 'remove' }, - { - path: 'components.messages.unUsedMessage.payload', - action: 'remove', - }, { path: 'components.schemas.canBeReused', action: 'remove' }, ] const ReuseComponentsExpectedResult = [ From d08f45210e2cb59bd5fd33bc965e71739b429042 Mon Sep 17 00:00:00 2001 From: Khuda Dad Nomani Date: Tue, 19 Dec 2023 15:12:25 +0000 Subject: [PATCH 13/13] Empty commit