diff --git a/src/commands/validate.ts b/src/commands/validate.ts index fb3b32d6df4..6ee932eb162 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -1,9 +1,10 @@ import { Args } from '@oclif/core'; import Command from '../core/base'; -import { validate, ValidateOptions, ValidationStatus } from '../core/parser'; +import { validate, ValidateOptions, ValidationStatus, parse } from '../core/parser'; import { load } from '../core/models/SpecificationFile'; import { specWatcher } from '../core/globals'; import { validateFlags } from '../core/flags/validate.flags'; +import { calculateScore } from '../core/utils/scoreCalculator'; export default class Validate extends Command { static description = 'validate asyncapi file'; @@ -16,9 +17,14 @@ export default class Validate extends Command { async run() { const { args, flags } = await this.parse(Validate); //NOSONAR + const filePath = args['spec-file']; const watchMode = flags.watch; - + if (flags['score']) { + this.specFile = await load(filePath); + const { document } = await parse(this,this.specFile); + this.log(`The score of the asyncapi document is ${await calculateScore(document)}`); + } this.specFile = await load(filePath); if (watchMode) { specWatcher({ spec: this.specFile, handler: this, handlerName: 'validate' }); diff --git a/src/core/flags/validate.flags.ts b/src/core/flags/validate.flags.ts index b86de912be6..e6b439d127f 100644 --- a/src/core/flags/validate.flags.ts +++ b/src/core/flags/validate.flags.ts @@ -7,5 +7,10 @@ export const validateFlags = () => { help: Flags.help({ char: 'h' }), watch: watchFlag(), ...validationFlags(), + score: Flags.boolean({ + description: 'Compute the score of the AsyncAPI document. Scoring is based on whether the document has description, license, server and/or channels.', + required: false, + default: false + }) }; }; diff --git a/src/core/utils/scoreCalculator.ts b/src/core/utils/scoreCalculator.ts new file mode 100644 index 00000000000..982b6c9f8df --- /dev/null +++ b/src/core/utils/scoreCalculator.ts @@ -0,0 +1,18 @@ +import { AsyncAPIDocumentInterface } from '@asyncapi/parser/cjs/models'; + +export async function calculateScore(document: AsyncAPIDocumentInterface | undefined) { + let scoreEvaluate = 0; + if (document?.info().hasDescription()) { + scoreEvaluate += 0.15; + } + if (document?.info().hasLicense()) { + scoreEvaluate += 0.25; + } + if (!document?.servers().isEmpty()) { + scoreEvaluate += 0.25; + } + if (!document?.channels().isEmpty()) { + scoreEvaluate += 0.35; + } + return (scoreEvaluate / 1) * 100; +} diff --git a/test/fixtures/asyncapiTestingScore.yml b/test/fixtures/asyncapiTestingScore.yml new file mode 100644 index 00000000000..09ca0ba84ec --- /dev/null +++ b/test/fixtures/asyncapiTestingScore.yml @@ -0,0 +1,206 @@ +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' + 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/kafka' + messages: + - $ref: '#/channels/lightingMeasured/messages/lightMeasured' + turnOn: + action: send + channel: + $ref: '#/channels/lightTurnOn' + traits: + - $ref: '#/components/operationTraits/kafka' + messages: + - $ref: '#/channels/lightTurnOn/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 + 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 diff --git a/test/integration/validate.test.ts b/test/integration/validate.test.ts index 81f493b35a3..29e04e1cf97 100644 --- a/test/integration/validate.test.ts +++ b/test/integration/validate.test.ts @@ -245,4 +245,23 @@ describe('validate', () => { done(); }); }); + + describe('with --score flag',() => { + beforeEach(() => { + testHelper.createDummyContextFile(); + }); + + afterEach(() => { + testHelper.deleteDummyContextFile(); + }); + + test + .stdout() + .command(['validate', './test/fixtures/asyncapiTestingScore.yml', '--score']) + .it('work with --score flag', (ctx, done) => { + expect(ctx.stdout).to.contains('The score of the asyncapi document is 100\n'); + expect(ctx.stdout).to.contains('File ./test/fixtures/asyncapiTestingScore.yml is valid! File ./test/fixtures/asyncapiTestingScore.yml and referenced documents don\'t have governance issues.'); + done(); + }); + }); });