From ce1fb5887d15bbac4627da73c2a168d7e367133e Mon Sep 17 00:00:00 2001 From: Waleed Ashraf Date: Thu, 22 Aug 2024 02:43:44 +0200 Subject: [PATCH] fix issue when multiple operations use same channel (#67) --- CHANGELOG.md | 4 + package-lock.json | 4 +- package.json | 2 +- src/ValidatorFactory.js | 8 +- test/schemas/v3.0.0/streetLights.yml | 2 +- test/schemas/v3.0.0/streetLightsMultiMsg.yml | 212 +++++++++++++++++++ test/v3.0.0/streetLights.js | 2 +- test/v3.0.0/streetLightsMultiMsg.js | 26 +++ 8 files changed, 251 insertions(+), 9 deletions(-) create mode 100644 test/schemas/v3.0.0/streetLightsMultiMsg.yml create mode 100644 test/v3.0.0/streetLightsMultiMsg.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a97a5d..a1062a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Change Log ========== +Version 5.0.1 *(2024-08-22)* +---------------------------- +* Fix issue when multiple operations use same channel. + Version 5.0.0 *(2024-07-21)* ---------------------------- ### Breaking: diff --git a/package-lock.json b/package-lock.json index 8990679..4581929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "asyncapi-validator", - "version": "5.0.0", + "version": "5.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "asyncapi-validator", - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "@asyncapi/openapi-schema-parser": "3.0.24", diff --git a/package.json b/package.json index 1f67a77..a83c8a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "asyncapi-validator", - "version": "5.0.0", + "version": "5.0.1", "description": "message validator through asyncapi schema", "main": "index.js", "types": "index.d.ts", diff --git a/src/ValidatorFactory.js b/src/ValidatorFactory.js index f8bea54..96e1a2c 100644 --- a/src/ValidatorFactory.js +++ b/src/ValidatorFactory.js @@ -24,13 +24,13 @@ function ValidatorFactory() { */ const constructsChannels = (schema, msgIdentifier = '') => { const channels = {} - const messagesWithOperations = {} + const channelsWithOperations = {} let messagesWithId = {} if (schema.operations) { Object.keys(schema.operations).forEach(operation => { const channel = schema.operations[operation].channel['x-parser-unique-object-id'] - messagesWithOperations[channel] = messagesWithOperations[channel] ? messagesWithOperations[channel].push(operation) : [operation] + channelsWithOperations[channel] = channelsWithOperations[channel] ? channelsWithOperations[channel].concat(operation) : [operation] }) } @@ -52,8 +52,8 @@ const constructsChannels = (schema, msgIdentifier = '') => { }) // For AsyncAPI 3.x.x - if (messagesWithOperations[c] && messagesWithOperations[c].length) { - messagesWithOperations[c].forEach(operation => { + if (channelsWithOperations[c] && channelsWithOperations[c].length) { + channelsWithOperations[c].forEach(operation => { const {action, messages} = getMessagesByOperations(schema.operations, operation, msgIdentifier) channels[c] = { ...channels[c], diff --git a/test/schemas/v3.0.0/streetLights.yml b/test/schemas/v3.0.0/streetLights.yml index 767c33e..b6e48eb 100644 --- a/test/schemas/v3.0.0/streetLights.yml +++ b/test/schemas/v3.0.0/streetLights.yml @@ -7,7 +7,7 @@ info: lights. ### Check out its awesome features: - * Turn a specific streetlight on/off 🌃 + * Turn a specific streetlight on/off 🌃 * Dim a specific streetlight 😎 * Receive real-time information about environmental lighting conditions 📈 license: diff --git a/test/schemas/v3.0.0/streetLightsMultiMsg.yml b/test/schemas/v3.0.0/streetLightsMultiMsg.yml new file mode 100644 index 0000000..19e277f --- /dev/null +++ b/test/schemas/v3.0.0/streetLightsMultiMsg.yml @@ -0,0 +1,212 @@ +asyncapi: 3.0.0 +info: + title: Streetlights Kafka 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: + scram-connections: + host: test.mykafkacluster.org:18092 + protocol: kafka-secure + description: Test broker secured with scramSha256 + security: + - $ref: '#/components/securitySchemes/saslScram' + tags: + - name: env:test-scram + description: >- + This environment is meant for running internal tests through + scramSha256 + - name: kind:remote + description: This server is a remote server. Not exposed by the application + - name: visibility:private + description: This resource is private and only available to certain users + mtls-connections: + host: test.mykafkacluster.org:28092 + protocol: kafka-secure + description: Test broker secured with X509 + security: + - $ref: '#/components/securitySchemes/certs' + tags: + - name: env:test-mtls + description: This environment is meant for running internal tests through mtls + - name: kind:remote + description: This server is a remote server. Not exposed by the application + - name: visibility:private + description: This resource is private and only available to certain users +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' + turnOff: + $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' + turnOn: + $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/kafka' + messages: + - $ref: '#/channels/lightingMeasured/messages/lightMeasured' + turnOn: + action: send + channel: + $ref: '#/channels/lightTurnOff' + traits: + - $ref: '#/components/operationTraits/kafka' + messages: + - $ref: '#/channels/lightTurnOff/messages/turnOn' + turnOff: + action: send + channel: + $ref: '#/channels/lightTurnOff' + traits: + - $ref: '#/components/operationTraits/kafka' + messages: + - $ref: '#/channels/lightTurnOff/messages/turnOff' + dimLight: + action: send + channel: + $ref: '#/channels/lightsDim' + traits: + - $ref: '#/components/operationTraits/kafka' + 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 + required: + - lumens + 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: + saslScram: + type: scramSha256 + description: Provide your username and password for SASL/SCRAM authentication + certs: + type: X509 + description: Download the certificate files from service provider + parameters: + streetlightId: + description: The ID of the streetlight. + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + operationTraits: + kafka: + bindings: + kafka: + clientId: + type: string + enum: + - my-app-id \ No newline at end of file diff --git a/test/v3.0.0/streetLights.js b/test/v3.0.0/streetLights.js index 56d4200..9f39968 100644 --- a/test/v3.0.0/streetLights.js +++ b/test/v3.0.0/streetLights.js @@ -29,7 +29,7 @@ describe('deviceMessages', () => { expect(validate).toThrowError(new Error('data must have required property \'lumens\'')) }) - it('should not throw error with validate()', () => { + it('should validate lightMeasured', () => { const validate = validator.validate('lightMeasured', { lumens: 0, sentAt: '2019-08-24T14:15:22Z' diff --git a/test/v3.0.0/streetLightsMultiMsg.js b/test/v3.0.0/streetLightsMultiMsg.js new file mode 100644 index 0000000..2f172db --- /dev/null +++ b/test/v3.0.0/streetLightsMultiMsg.js @@ -0,0 +1,26 @@ +const AsyncApiValidator = require('../../index') + +describe('deviceMessages', () => { + let validator + beforeEach(async () => { + validator = await AsyncApiValidator.fromSource('./test/schemas/v3.0.0/streetLightsMultiMsg.yml', { + msgIdentifier: 'name' + }) + }) + + it('should validate lightMeasured with channel lightTurnOff', () => { + const validate = validator.validate('turnOnOff', { + command: 'on', + sentAt: '2019-08-24T14:15:22Z' + }, 'lightTurnOff', 'send') + expect(validate).toStrictEqual(true) + }) + + it('should throw error for channel which is not used in any operation', () => { + const validate = () => validator.validate('turnOnOff', { + command: 'on', + sentAt: '2019-08-24T14:15:22Z' + }, 'lightTurnOn', 'send') + expect(validate).toThrowError(new Error('channel "lightTurnOn" not found')) + }) +})