From a3909e4d0a98e619cc08b595c07681265d399747 Mon Sep 17 00:00:00 2001 From: Sergio Moya <1083296+smoya@users.noreply.github.com> Date: Sun, 22 Oct 2023 12:33:40 +0200 Subject: [PATCH 1/5] feat: add POC for measuring adoption --- package-lock.json | 23 ++++++++++++++++------- package.json | 3 ++- src/base.ts | 3 +++ src/commands/validate.ts | 22 +++++++++++++++++++++- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2af771bce1..400e46f87f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,13 +17,14 @@ "@asyncapi/modelina": "^1.9.1", "@asyncapi/openapi-schema-parser": "^3.0.5", "@asyncapi/optimizer": "^0.2.1", - "@asyncapi/parser": "^3.0.0-next-major-spec.2", + "@asyncapi/parser": "^3.0.0-next-major-spec.3", "@asyncapi/protobuf-schema-parser": "3.0.0", "@asyncapi/raml-dt-schema-parser": "^4.0.3", "@asyncapi/studio": "^0.17.3", "@oclif/core": "^1.26.2", "@oclif/errors": "^1.3.6", "@oclif/plugin-not-found": "^2.3.22", + "@smoya/asyncapi-adoption-metrics": "^1.1.1", "@smoya/multi-parser": "^4.0.0", "@stoplight/spectral-cli": "6.9.0", "ajv": "^8.12.0", @@ -1138,9 +1139,9 @@ } }, "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.0-next-major-spec.3", + "resolved": "https://registry.npmjs.org/@asyncapi/parser/-/parser-3.0.0-next-major-spec.3.tgz", + "integrity": "sha512-LCrAQqJpGxraMyU2k1Nh1X6Q1dz7a/YhTRRFFrQHOEo+TUT/kRdoUkRDP++e58dO7h9MBN+/hZK5TaqE+/jQiw==", "dependencies": { "@asyncapi/specs": "^6.0.0-next-major-spec.6", "@openapi-contrib/openapi-schema-to-json-schema": "~3.2.0", @@ -5475,10 +5476,18 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@smoya/asyncapi-adoption-metrics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smoya/asyncapi-adoption-metrics/-/asyncapi-adoption-metrics-1.1.1.tgz", + "integrity": "sha512-1UTfJsw3+iUSaROu03s6OXA32HkM+h9kT1P1gZCOCddRybL5PJzvP/X3FIJEM9IICu1B7btVXdy3VNIZ/9LCaw==", + "dependencies": { + "@smoya/multi-parser": "^4.1.0" + } + }, "node_modules/@smoya/multi-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smoya/multi-parser/-/multi-parser-4.0.0.tgz", - "integrity": "sha512-NgPxSaB3YqwrIVe7AtQ/wh9I2J0BHR4lP0PdqirYYrc0XXRwdDjIRrywEc2jjECWsL7tuGU/QtGMGIVaJe6ZYA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smoya/multi-parser/-/multi-parser-4.1.0.tgz", + "integrity": "sha512-0q4805I7ZdkKNEbNrbxmBNAdYEAH9aBYCuXiIXCbu8WC0sdRqLWmTsvg/DaNO8ZnsrGhVzy5VITf+8CKlVnGoQ==", "dependencies": { "@asyncapi/avro-schema-parser": "^3.0.3", "@asyncapi/openapi-schema-parser": "^3.0.4", diff --git a/package.json b/package.json index a724b554496..62c36eca24c 100644 --- a/package.json +++ b/package.json @@ -16,13 +16,14 @@ "@asyncapi/modelina": "^1.9.1", "@asyncapi/openapi-schema-parser": "^3.0.5", "@asyncapi/optimizer": "^0.2.1", - "@asyncapi/parser": "^3.0.0-next-major-spec.2", + "@asyncapi/parser": "^3.0.0-next-major-spec.3", "@asyncapi/protobuf-schema-parser": "3.0.0", "@asyncapi/raml-dt-schema-parser": "^4.0.3", "@asyncapi/studio": "^0.17.3", "@oclif/core": "^1.26.2", "@oclif/errors": "^1.3.6", "@oclif/plugin-not-found": "^2.3.22", + "@smoya/asyncapi-adoption-metrics": "^1.1.1", "@smoya/multi-parser": "^4.0.0", "@stoplight/spectral-cli": "6.9.0", "ajv": "^8.12.0", diff --git a/src/base.ts b/src/base.ts index c2761523adf..3ac8a66e16e 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,6 +1,9 @@ import { Command } from '@oclif/core'; +import { Recorder, StdOutSink } from '@smoya/asyncapi-adoption-metrics'; export default abstract class extends Command { + recorder = new Recorder('asyncapi-adoption', new StdOutSink()); + async catch(err: Error & { exitCode?: number; }): Promise { try { return await super.catch(err); diff --git a/src/commands/validate.ts b/src/commands/validate.ts index 7254b52f4f5..d889671d9b5 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -5,6 +5,8 @@ import { validate, validationFlags } from '../parser'; import { load } from '../models/SpecificationFile'; import { specWatcher } from '../globals'; import { watchFlag } from '../flags'; +import { MetadataFromDocument } from '@smoya/asyncapi-adoption-metrics'; +import { Parser } from '@asyncapi/parser'; export default class Validate extends Command { static description = 'validate asyncapi file'; @@ -19,6 +21,8 @@ export default class Validate extends Command { { name: 'spec-file', description: 'spec path, url, or context-name', required: false }, ]; + parser = new Parser(); + async run() { const { args, flags } = await this.parse(Validate); //NOSONAR const filePath = args['spec-file']; @@ -29,6 +33,22 @@ export default class Validate extends Command { specWatcher({ spec: specFile, handler: this, handlerName: 'validate' }); } - await validate(this, specFile, flags); + const result = await validate(this, specFile, flags); + + try { + // Metrics recording. + const {document} = await this.parser.parse(specFile.text()); + if (document !== undefined) { + const metadata = MetadataFromDocument(document); + metadata['success'] = true; + metadata['validation_result'] = result; + await this.recorder.recordActionExecution('validate', metadata); + await this.recorder.flush(); + } + } catch (e: any) { + if (e instanceof Error) { + this.log(`Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`); + } + } } } From fd65361b1c93ce71c72b9561364f50c4ed21285f Mon Sep 17 00:00:00 2001 From: Pedro Ramos Date: Mon, 6 Nov 2023 12:50:07 +0100 Subject: [PATCH 2/5] Update convert command --- src/commands/convert.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/commands/convert.ts b/src/commands/convert.ts index b404a6df3f6..871bd820903 100644 --- a/src/commands/convert.ts +++ b/src/commands/convert.ts @@ -6,6 +6,8 @@ import { ValidationError } from '../errors/validation-error'; import { load } from '../models/SpecificationFile'; import { SpecificationFileNotFound } from '../errors/specification-file'; import { convert } from '@asyncapi/converter'; +import { MetadataFromDocument } from '@smoya/asyncapi-adoption-metrics'; +import { Parser } from '@asyncapi/parser'; import type { ConvertVersion } from '@asyncapi/converter'; @@ -27,6 +29,8 @@ export default class Convert extends Command { { name: 'spec-file', description: 'spec path, url, or context-name', required: false }, ]; + parser = new Parser(); + async run() { const { args, flags } = await this.parse(Convert); const filePath = args['spec-file']; @@ -69,5 +73,23 @@ export default class Convert extends Command { this.error(err as Error); } } + + try { + // Metrics recording. + const {document} = await this.parser.parse(specFile.text()); + if (document !== undefined && convertedFileFormatted) { + const metadata = MetadataFromDocument(document); + metadata['success'] = true; + metadata['from_version'] = metadata['_asyncapi_version']; + metadata['to_version'] = flags['target-version']; + console.log(metadata); + await this.recorder.recordActionExecution('convert', metadata); + await this.recorder.flush(); + } + } catch (e: any) { + if (e instanceof Error) { + this.log(`Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`); + } + } } } From 5b25de2fe27a86e1a56c8d9159768845cf569fad Mon Sep 17 00:00:00 2001 From: Pedro Ramos Date: Tue, 31 Oct 2023 15:09:11 +0100 Subject: [PATCH 3/5] Add metrics recording to some commands --- src/commands/bundle.ts | 22 ++++++++++++++++++++++ src/commands/convert.ts | 2 +- src/commands/generate/fromTemplate.ts | 22 +++++++++++++++++++++- src/commands/optimize.ts | 23 +++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/commands/bundle.ts b/src/commands/bundle.ts index d014fbbe2ff..98d81c464c1 100644 --- a/src/commands/bundle.ts +++ b/src/commands/bundle.ts @@ -5,6 +5,8 @@ import bundle from '@asyncapi/bundler'; import { promises } from 'fs'; import path from 'path'; import { Specification, load } from '../models/SpecificationFile'; +import { MetadataFromDocument } from '@smoya/asyncapi-adoption-metrics'; +import { Parser } from '@asyncapi/parser'; const { writeFile } = promises; @@ -26,6 +28,8 @@ export default class Bundle extends Command { base: Flags.string({ char: 'b', description: 'Path to the file which will act as a base. This is required when some properties are to needed to be overwritten.' }), }; + parser = new Parser(); + async run() { const { argv, flags } = await this.parse(Bundle); const output = flags.output; @@ -73,6 +77,24 @@ export default class Bundle extends Command { } this.log(`Check out your shiny new bundled files at ${output}`); } + + const result = await load(output); + + try { + // Metrics recording. + const {document} = await this.parser.parse(result.text()); + if (document !== undefined) { + const metadata = MetadataFromDocument(document); + metadata['success'] = true; + metadata['files'] = AsyncAPIFiles.length; + await this.recorder.recordActionExecution('bundle', metadata); + await this.recorder.flush(); + } + } catch (e: any) { + if (e instanceof Error) { + this.log(`Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`); + } + } } async loadFiles(filepaths: string[]): Promise { diff --git a/src/commands/convert.ts b/src/commands/convert.ts index 871bd820903..d6031844ed1 100644 --- a/src/commands/convert.ts +++ b/src/commands/convert.ts @@ -80,7 +80,7 @@ export default class Convert extends Command { if (document !== undefined && convertedFileFormatted) { const metadata = MetadataFromDocument(document); metadata['success'] = true; - metadata['from_version'] = metadata['_asyncapi_version']; + metadata['from_version'] = document.version(); metadata['to_version'] = flags['target-version']; console.log(metadata); await this.recorder.recordActionExecution('convert', metadata); diff --git a/src/commands/generate/fromTemplate.ts b/src/commands/generate/fromTemplate.ts index e53ea665cb2..70a2bf2f0dc 100644 --- a/src/commands/generate/fromTemplate.ts +++ b/src/commands/generate/fromTemplate.ts @@ -11,6 +11,8 @@ import { watchFlag } from '../../flags'; import { isLocalTemplate, Watcher } from '../../utils/generator'; import { ValidationError } from '../../errors/validation-error'; import { GeneratorError } from '../../errors/generator-error'; +import { MetadataFromDocument } from '@smoya/asyncapi-adoption-metrics'; +import { Parser } from '@asyncapi/parser'; import type { Example } from '@oclif/core/lib/interfaces'; @@ -107,6 +109,8 @@ export default class Template extends Command { { name: 'template', description: '- Name of the generator template like for example @asyncapi/html-template or https://github.com/asyncapi/html-template', required: true } ]; + parser = new Parser(); + async run() { const { args, flags } = await this.parse(Template); // NOSONAR @@ -137,11 +141,27 @@ export default class Template extends Command { this.error(`${template} template does not support AsyncAPI v3 documents, please checkout ${v3IssueLink}`); } } - await this.generate(asyncapi, template, output, options, genOption); + const result = await this.generate(asyncapi, template, output, options, genOption); if (watchTemplate) { const watcherHandler = this.watcherHandler(asyncapi, template, output, options, genOption); await this.runWatchMode(asyncapi, template, output, watcherHandler); } + + try { + // Metrics recording. + const {document} = await this.parser.parse(asyncapiInput.text()); + if (document !== undefined && result) { + const metadata = MetadataFromDocument(document); + metadata['success'] = true; + metadata['template'] = template; + await this.recorder.recordActionExecution('generate_fromTemplate', metadata); + await this.recorder.flush(); + } + } catch (e: any) { + if (e instanceof Error) { + this.log(`Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`); + } + } } private parseFlags(disableHooks?: string[], params?: string[], mapBaseUrl?: string): ParsedFlags { diff --git a/src/commands/optimize.ts b/src/commands/optimize.ts index f21f14b764f..60283f29302 100644 --- a/src/commands/optimize.ts +++ b/src/commands/optimize.ts @@ -7,6 +7,9 @@ import * as inquirer from 'inquirer'; import chalk from 'chalk'; import { promises } from 'fs'; import { Example } from '@oclif/core/lib/interfaces'; +import { MetadataFromDocument } from '@smoya/asyncapi-adoption-metrics'; +import { Parser } from '@asyncapi/parser'; + const { writeFile } = promises; export enum Optimizations { @@ -44,6 +47,8 @@ export default class Optimize extends Command { { name: 'spec-file', description: 'spec path, url, or context-name', required: false }, ]; + parser = new Parser(); + async run() { const { args, flags } = await this.parse(Optimize); //NOSONAR const filePath = args['spec-file']; @@ -123,7 +128,25 @@ export default class Optimize extends Command { err: error }); } + + try { + // Metrics recording. + const {document} = await this.parser.parse(specFile.text()); + const optimizedDoc = optimizer.getOptimizedDocument(); + if (document !== undefined && optimizedDoc) { + const metadata = MetadataFromDocument(document); + metadata['success'] = true; + metadata['optimizations'] = this.optimizations; + await this.recorder.recordActionExecution('optimize', metadata); + await this.recorder.flush(); + } + } catch (e: any) { + if (e instanceof Error) { + this.log(`Skipping submitting anonymous metrics due to the following error: ${e.name}: ${e.message}`); + } + } } + private showOptimizations(elements: ReportElement[] | undefined) { if (!elements) { return; From 98673cf1e4638625b4ceb1bdc8fa011716efb23a Mon Sep 17 00:00:00 2001 From: Pedro Ramos Date: Thu, 16 Nov 2023 22:34:42 +0100 Subject: [PATCH 4/5] Change prefix format and pass New Relic specific sink when creating a new recorder instance --- src/base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base.ts b/src/base.ts index 3ac8a66e16e..87acbfac43e 100644 --- a/src/base.ts +++ b/src/base.ts @@ -1,8 +1,8 @@ import { Command } from '@oclif/core'; -import { Recorder, StdOutSink } from '@smoya/asyncapi-adoption-metrics'; +import { Recorder, NewRelicSink } from '@smoya/asyncapi-adoption-metrics'; export default abstract class extends Command { - recorder = new Recorder('asyncapi-adoption', new StdOutSink()); + recorder = new Recorder('asyncapi_adoption', new NewRelicSink('API key')); async catch(err: Error & { exitCode?: number; }): Promise { try { From 88c677920383019c0d2baecd0e5ea258a3c911f7 Mon Sep 17 00:00:00 2001 From: Pedro Ramos Date: Thu, 16 Nov 2023 23:04:03 +0100 Subject: [PATCH 5/5] Update '@smoya/asyncapi-adoption-metrics' to latest version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ff245fbb73..bcbba91b317 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@oclif/core": "^1.26.2", "@oclif/errors": "^1.3.6", "@oclif/plugin-not-found": "^2.3.22", - "@smoya/asyncapi-adoption-metrics": "^1.1.1", + "@smoya/asyncapi-adoption-metrics": "^1.3.2", "@smoya/multi-parser": "^4.0.0", "@stoplight/spectral-cli": "6.9.0", "ajv": "^8.12.0",