From 989e003b232ef3196ea38baedf54a097c3d95b7a Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Mon, 17 Nov 2025 11:09:33 +0530 Subject: [PATCH 1/8] feat: parser adapter for json in asyncapi3 --- package-lock.json | 21 +- packages/apidom-ls/package.json | 1 + .../services/validation/validation-service.ts | 4 + packages/apidom-ls/src/utils/utils.ts | 14 + packages/apidom-ns-asyncapi-3/package.json | 2 +- .../.eslintignore | 8 + .../.gitignore | 7 + .../.mocharc.json | 8 + .../.npmrc | 2 + .../CHANGELOG.md | 4 + .../README.md | 94 +++++ .../config/api-extractor/api-extractor.json | 5 + .../config/webpack/browser.config.js | 92 +++++ .../config/webpack/traits.config.js | 32 ++ .../package.json | 63 +++ .../src/adapter.ts | 45 +++ .../src/media-types.ts | 11 + .../test/.eslintrc | 5 + .../test/fixtures/sample-api.json | 369 ++++++++++++++++++ .../test/fixtures/sample-api.yaml | 256 ++++++++++++ .../test/index.ts | 24 ++ .../test/lexical-analysis.ts | 37 ++ .../test/parse.ts | 47 +++ .../test/perf/fixtures/asyncapi.json | 137 +++++++ .../test/refract.ts | 38 ++ .../test/syntactic-analysis.ts | 39 ++ .../tsconfig.declaration.json | 9 + .../tsconfig.json | 6 + 28 files changed, 1378 insertions(+), 2 deletions(-) create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/.eslintignore create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/.gitignore create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/.npmrc create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/CHANGELOG.md create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/README.md create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/config/api-extractor/api-extractor.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/traits.config.js create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/package.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/src/media-types.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/perf/fixtures/asyncapi.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.declaration.json create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.json diff --git a/package-lock.json b/package-lock.json index b562569751..8c9e684d79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6936,6 +6936,10 @@ "resolved": "packages/apidom-parser-adapter-asyncapi-json-2", "link": true }, + "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-3": { + "resolved": "packages/apidom-parser-adapter-asyncapi-json-3", + "link": true + }, "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { "resolved": "packages/apidom-parser-adapter-asyncapi-yaml-2", "link": true @@ -25984,6 +25988,7 @@ "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.0.0-rc.3", + "@swagger-api/apidom-parser-adapter-asyncapi-json-3": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-json": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-openapi-json-2": "^1.0.0-rc.3", @@ -26071,7 +26076,7 @@ }, "packages/apidom-ns-asyncapi-3": { "name": "@swagger-api/apidom-ns-asyncapi-3", - "version": "1.0.0-beta.0", + "version": "1.0.0-rc.3", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", @@ -26287,6 +26292,20 @@ "ramda-adjunct": "^5.0.0" } }, + "packages/apidom-parser-adapter-asyncapi-json-3": { + "name": "@swagger-api/apidom-parser-adapter-asyncapi-json-3", + "version": "1.0.0-rc.3", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-rc.3", + "@swagger-api/apidom-ns-asyncapi-3": "^1.0.0-rc.3", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-rc.3", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, "packages/apidom-parser-adapter-asyncapi-yaml-2": { "name": "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2", "version": "1.0.0-rc.3", diff --git a/packages/apidom-ls/package.json b/packages/apidom-ls/package.json index 47d2882734..2c1a7c7edf 100644 --- a/packages/apidom-ls/package.json +++ b/packages/apidom-ls/package.json @@ -106,6 +106,7 @@ "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^1.0.0-rc.3", + "@swagger-api/apidom-parser-adapter-asyncapi-json-3": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-json": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-openapi-json-2": "^1.0.0-rc.3", "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^1.0.0-rc.3", diff --git a/packages/apidom-ls/src/services/validation/validation-service.ts b/packages/apidom-ls/src/services/validation/validation-service.ts index b2e6105ad6..d8d073a34f 100644 --- a/packages/apidom-ls/src/services/validation/validation-service.ts +++ b/packages/apidom-ls/src/services/validation/validation-service.ts @@ -450,6 +450,10 @@ export class DefaultValidationService implements ValidationService { let processedText; const nameSpace = await findNamespace(text, this.settings?.defaultContentLanguage); + // TODO: Turned off validation, because we will implement it in the future. + if (nameSpace.namespace === 'asyncapi' && nameSpace.version === '3.0.0') { + return []; + } let docNs: string = nameSpace.namespace; // no API document has been parsed if (result.annotations) { diff --git a/packages/apidom-ls/src/utils/utils.ts b/packages/apidom-ls/src/utils/utils.ts index a913e6ab2b..eed2adf441 100644 --- a/packages/apidom-ls/src/utils/utils.ts +++ b/packages/apidom-ls/src/utils/utils.ts @@ -6,6 +6,7 @@ import * as openapi31xAdapterJson from '@swagger-api/apidom-parser-adapter-opena import * as openapi31xAdapterYaml from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1'; import * as asyncapi2AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-2'; import * as asyncapi2AdapterYaml from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2'; +import * as asyncapi3AdapterJson from '@swagger-api/apidom-parser-adapter-asyncapi-json-3'; import * as adsAdapterJson from '@swagger-api/apidom-parser-adapter-api-design-systems-json'; import * as adsAdapterYaml from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml'; import * as adapterJson from '@swagger-api/apidom-parser-adapter-json'; @@ -836,6 +837,19 @@ export async function findNamespace( }; } + if (await asyncapi3AdapterJson.detect(text)) { + const asyncapi3JsonMatch = text.match(asyncapi3AdapterJson.detectionRegExp)!; + const groups = asyncapi3JsonMatch.groups!; + const version = groups.version_json; + + return { + namespace: 'asyncapi', + version, + format: 'JSON', + mediaType: asyncapi3AdapterJson.mediaTypes.findBy(version, 'json'), + }; + } + if (await openapi2AdapterJson.detect(text)) { const openapi2JsonMatch = text.match(openapi2AdapterJson.detectionRegExp)!; const groups = openapi2JsonMatch.groups!; diff --git a/packages/apidom-ns-asyncapi-3/package.json b/packages/apidom-ns-asyncapi-3/package.json index 25a0abbbb6..8e5d9be2f3 100644 --- a/packages/apidom-ns-asyncapi-3/package.json +++ b/packages/apidom-ns-asyncapi-3/package.json @@ -1,6 +1,6 @@ { "name": "@swagger-api/apidom-ns-asyncapi-3", - "version": "1.0.0-beta.0", + "version": "1.0.0-rc.3", "description": "AsyncAPI 3.x.y namespace for ApiDOM.", "publishConfig": { "access": "public", diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.eslintignore b/packages/apidom-parser-adapter-asyncapi-json-3/.eslintignore new file mode 100644 index 0000000000..23853b909a --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.eslintignore @@ -0,0 +1,8 @@ +/**/*.js +/**/*.mjs +/**/*.cjs +/dist +/types +/config +/.nyc_output +/node_modules diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.gitignore b/packages/apidom-parser-adapter-asyncapi-json-3/.gitignore new file mode 100644 index 0000000000..9b4d60ee63 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.gitignore @@ -0,0 +1,7 @@ +/src/**/*.mjs +/src/**/*.cjs +/test/**/*.mjs +/dist +/types +/NOTICE +/swagger-api-apidom-parser-adapter-asyncapi-json-3-*.tgz diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json new file mode 100644 index 0000000000..cbd88d9b45 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json @@ -0,0 +1,8 @@ +{ + "extensions": ["ts"], + "loader": "ts-node/esm", + "recursive": true, + "spec": "test/**/*.mjs", + "file": ["test/mocha-bootstrap.ts"], + "ignore": ["test/perf/**/*.ts"] +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.npmrc b/packages/apidom-parser-adapter-asyncapi-json-3/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/CHANGELOG.md b/packages/apidom-parser-adapter-asyncapi-json-3/CHANGELOG.md new file mode 100644 index 0000000000..e9fb6ecf59 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. \ No newline at end of file diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/README.md b/packages/apidom-parser-adapter-asyncapi-json-3/README.md new file mode 100644 index 0000000000..f98420744c --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/README.md @@ -0,0 +1,94 @@ +# @swagger-api/apidom-parser-adapter-asyncapi-json-3 + +`@swagger-api/apidom-parser-adapter-asyncapi-json-3` is a parser adapter for following AsyncAPI specification versions defined in [JSON format](https://www.json.org/json-en.html): + + + +Under the hood this adapter uses [@swagger-api/apidom-parser-adapter-json](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-json) +to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom#base-namespace) +which is then refracted with [AsyncApi 3.x.y Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#refractors). + +## Installation + +After [prerequisites](https://github.com/swagger-api/apidom/blob/main/README.md#prerequisites) for installing this package are satisfied, you can install it +via [npm CLI](https://docs.npmjs.com/cli) by running the following command: + +```sh + $ npm install @swagger-api/apidom-parser-adapter-asyncapi-json-3 +``` + +## Parser adapter API + +This parser adapter is fully compatible with parser adapter interface required by [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#mounting-parser-adapters) +and implements all required properties. + +### mediaTypes + +Defines list of media types that this parser adapter recognizes. + +```js +[ + 'application/vnd.aai.asyncapi;version=3.0.0', + 'application/vnd.aai.asyncapi+json;version=3.0.0', + 'application/vnd.aai.asyncapi+yaml;version=3.0.0', + 'application/vnd.aai.asyncapi;version=3.0.1', + 'application/vnd.aai.asyncapi+json;version=3.0.1', + 'application/vnd.aai.asyncapi+yaml;version=3.0.1', +] +``` + +### detect + +[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts#L13) is based on a regular expression matching required AsyncApi 3.6.0 specification symbols in JSON format. + +### namespace + +This adapter exposes an instance of [AsyncApi 3.x.y ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#asyncapi-2xy-namespace). + +### parse + +`parse` function consumes various options as a second argument. Here is a list of these options: + +Option | Type | Default | Description +--- | --- | --- | --- +`specObj` | `Object` | [Specification Object](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ns-asyncapi-3/src/refractor/specification.ts) | This specification object drives the JSON AST transformation to AsyncAPI 3.x ApiDOM namespace. +`sourceMap` | `Boolean` | `false` | Indicate whether to generate source maps. +`refractorOpts` | `Object` | `{}` | Refractor options are [passed to refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#refractor-plugins) during refracting phase. + +All unrecognized arbitrary options will be ignored. + +## Usage + +This parser adapter can be used directly or indirectly via [@swagger-api/apidom-parser](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser). + +### Direct usage + +During direct usage you don't need to provide `mediaType` as the `parse` function is already pre-bound +with [supported media types](#mediatypes). + +```js +import { parse, detect } from '@swagger-api/apidom-parser-adapter-asyncapi-json-3'; + +// detecting +await detect('{"asyncapi": "3.0.0"}'); // => true +await detect('{"asyncapi": "3.0.1"}'); // => true +await detect('test'); // => false + +// parsing +const parseResult = await parse('{"asyncapi": "3.6.0"}', { sourceMap: true }); +``` + +### Indirect usage + +You can omit the `mediaType` option here, but please read [Word on detect vs mediaTypes](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser#word-on-detect-vs-mediatypes) before you do so. + +```js +import ApiDOMParser from '@swagger-api/apidom-parser'; +import * as asyncApiJsonAdapter from '@swagger-api/apidom-parser-adapter-asyncapi-json-3'; + +const parser = new ApiDOMParser(); + +parser.use(asyncApiJsonAdapter); + +const parseResult = await parser.parse('{"asyncapi": "3.6.0"}', { mediaType: asyncApiJsonAdapter.mediaTypes.latest('json') }); +``` diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/config/api-extractor/api-extractor.json b/packages/apidom-parser-adapter-asyncapi-json-3/config/api-extractor/api-extractor.json new file mode 100644 index 0000000000..40bee5b261 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/config/api-extractor/api-extractor.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../../../api-extractor.json", + "mainEntryPointFilePath": "../../types/adapter.d.ts" +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js new file mode 100644 index 0000000000..1137c6572c --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js @@ -0,0 +1,92 @@ +import path from 'node:path'; +import { nonMinimizeTrait, minimizeTrait } from './traits.config.js'; + +const browser = { + mode: 'production', + entry: ['./src/adapter.ts'], + target: 'web', + performance: { + maxEntrypointSize: 2300000, + maxAssetSize: 2300000, + }, + output: { + path: path.resolve('./dist'), + filename: 'apidom-parser-adapter-asyncapi-json-3.browser.js', + libraryTarget: 'umd', + library: 'apidomParserAdapterAsyncApiJson2', + }, + resolve: { + extensions: ['.ts', '.mjs', '.js', '.json'], + fallback: { + fs: false, + path: false, + }, + }, + module: { + rules: [ + { + test: /\.wasm$/, + loader: 'file-loader', + type: 'javascript/auto', + }, + { + test: /\.(ts|js)?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + babelrc: true, + rootMode: 'upward', + }, + }, + }, + ], + }, + ...nonMinimizeTrait, +}; + +const browserMin = { + mode: 'production', + entry: ['./src/adapter.ts'], + target: 'web', + performance: { + maxEntrypointSize: 350000, + maxAssetSize: 350000, + }, + output: { + path: path.resolve('./dist'), + filename: 'apidom-parser-adapter-asyncapi-json-3.browser.min.js', + libraryTarget: 'umd', + library: 'apidomParserAdapterAsyncApiJson2', + }, + resolve: { + extensions: ['.ts', '.mjs', '.js', '.json'], + fallback: { + fs: false, + path: false, + }, + }, + module: { + rules: [ + { + test: /\.wasm$/, + loader: 'file-loader', + type: 'javascript/auto', + }, + { + test: /\.(ts|js)?$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + babelrc: true, + rootMode: 'upward', + }, + }, + }, + ], + }, + ...minimizeTrait, +}; + +export default [browser, browserMin]; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/traits.config.js b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/traits.config.js new file mode 100644 index 0000000000..9043521175 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/traits.config.js @@ -0,0 +1,32 @@ +import webpack from 'webpack'; +import TerserPlugin from 'terser-webpack-plugin'; + +export const nonMinimizeTrait = { + optimization: { + minimize: false, + usedExports: false, + concatenateModules: false, + }, +}; + +export const minimizeTrait = { + plugins: [ + new webpack.LoaderOptionsPlugin({ + minimize: true, + }), + ], + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: { + warnings: false, + }, + output: { + comments: false, + }, + }, + }), + ], + }, +}; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/package.json b/packages/apidom-parser-adapter-asyncapi-json-3/package.json new file mode 100644 index 0000000000..0923fd7a38 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/package.json @@ -0,0 +1,63 @@ +{ + "name": "@swagger-api/apidom-parser-adapter-asyncapi-json-3", + "version": "1.0.0-rc.3", + "description": "Parser adapter for parsing JSON documents into AsyncAPI 3.x.y namespace.", + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "type": "module", + "sideEffects": false, + "unpkg": "./dist/apidom-parser-apdater-asyncapi-json-3.browser.min.js", + "main": "./src/adapter.cjs", + "exports": { + "types": "./types/apidom-parser-adapter-asyncapi-json-3.d.ts", + "import": "./src/adapter.mjs", + "require": "./src/adapter.cjs" + }, + "types": "./types/apidom-parser-adapter-asyncapi-json-3.d.ts", + "scripts": { + "build": "npm run clean && run-p --max-parallel ${CPU_CORES:-2} typescript:declaration build:es build:cjs build:umd:browser", + "build:es": "cross-env BABEL_ENV=es babel src --out-dir src --extensions '.ts' --out-file-extension '.mjs' --root-mode 'upward'", + "build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir src --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'", + "build:umd:browser": "cross-env BABEL_ENV=browser webpack --config config/webpack/browser.config.js --progress", + "lint": "eslint ./", + "lint:fix": "eslint ./ --fix", + "clean": "rimraf --glob 'src/**/*.mjs' 'src/**/*.cjs' ./dist ./types", + "typescript:check-types": "tsc --noEmit && tsc -p ./test/tsconfig.json --noEmit", + "typescript:declaration": "tsc -p tsconfig.declaration.json && api-extractor run -l -c ./config/api-extractor/api-extractor.json", + "test": "NODE_ENV=test ts-mocha --exit", + "perf": "cross-env BABEL_ENV=es babel ./test/perf/index.ts --out-file ./test/perf/index.mjs --root-mode 'upward' && cross-env NODE_ENV=test node ./test/perf/index.mjs", + "perf:lexical-analysis": "cross-env BABEL_ENV=es babel ./test/perf/lexical-analysis.ts --out-file ./test/perf/lexical-analysis.mjs --root-mode 'upward' && cross-env NODE_ENV=test node ./test/perf/lexical-analysis.mjs", + "perf:syntactic-analysis": "cross-env BABEL_ENV=es babel ./test/perf/syntactic-analysis.ts --out-file ./test/perf/syntactic-analysis.mjs --root-mode 'upward' && cross-env NODE_ENV=test node ./test/perf/syntactic-analysis.mjs", + "perf:refract": "cross-env BABEL_ENV=es babel ./test/perf/refract.ts --out-file ./test/perf/refract.mjs --root-mode 'upward' && cross-env NODE_ENV=test node ./test/perf/refract.mjs", + "perf:parse": "cross-env BABEL_ENV=es babel ./test/perf/parse.ts --out-file ./test/perf/parse.mjs --root-mode 'upward' && cross-env NODE_ENV=test node ./test/perf/parse.mjs", + "prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .", + "postpack": "rimraf NOTICE LICENSES" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/swagger-api/apidom.git" + }, + "author": "Vladimir Gorej", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-rc.3", + "@swagger-api/apidom-ns-asyncapi-3": "^1.0.0-rc.3", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-rc.3", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + }, + "files": [ + "src/**/*.mjs", + "src/**/*.cjs", + "dist/", + "types/apidom-parser-adapter-asyncapi-json-3.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts b/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts new file mode 100644 index 0000000000..dd2d3611d9 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts @@ -0,0 +1,45 @@ +import { propOr, omit } from 'ramda'; +import { isNotUndefined } from 'ramda-adjunct'; +import { ParseResultElement, createNamespace } from '@swagger-api/apidom-core'; +import { parse as parseJSON, detect as detectJSON } from '@swagger-api/apidom-parser-adapter-json'; +import asyncApiNamespace, { AsyncApi3Element } from '@swagger-api/apidom-ns-asyncapi-3'; + +export { default as mediaTypes } from './media-types.ts'; + +/** + * @public + */ +export const detectionRegExp = + /"asyncapi"\s*:\s*"(?3\.(?:[1-9]\d*|0)\.(?:[1-9]\d*|0))"/; + +/** + * @public + */ +export const detect = async (source: string): Promise => + detectionRegExp.test(source) && (await detectJSON(source)); + +/** + * @public + */ +export const parse = async ( + source: string, + options: Record = {}, +): Promise => { + const refractorOpts: Record = propOr({}, 'refractorOpts', options); + const parserOpts = omit(['refractorOpts'], options); + const parseResultElement = await parseJSON(source, parserOpts); + const { result } = parseResultElement; + + if (isNotUndefined(result)) { + const asyncApiElement = AsyncApi3Element.refract(result, refractorOpts); + asyncApiElement.classes.push('result'); + parseResultElement.replaceResult(asyncApiElement); + } + + return parseResultElement; +}; + +/** + * @public + */ +export const namespace = createNamespace(asyncApiNamespace); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/src/media-types.ts b/packages/apidom-parser-adapter-asyncapi-json-3/src/media-types.ts new file mode 100644 index 0000000000..a9e624cf78 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/src/media-types.ts @@ -0,0 +1,11 @@ +import { mediaTypes, AsyncAPIMediaTypes } from '@swagger-api/apidom-ns-asyncapi-3'; + +/** + * @public + */ +const jsonMediaTypes = new AsyncAPIMediaTypes( + ...mediaTypes.filterByFormat('generic'), + ...mediaTypes.filterByFormat('json'), +); + +export default jsonMediaTypes; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc b/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc new file mode 100644 index 0000000000..6ed40ebc25 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "@typescript-eslint/no-var-requires": 0 + } +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json new file mode 100644 index 0000000000..fd0bb7f5c9 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json @@ -0,0 +1,369 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Sample Pet Store App", + "description": "This is a sample server for a pet store.", + "termsOfService": "https://example.com/terms/", + "contact": { + "name": "API Support", + "url": "https://www.example.com/support", + "email": "support@example.com" + }, + "license": { + "name": "Apache 2.0", + "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.1" + }, + "servers": [ + { + "url": "{username}.gigantic-server.com", + "description": "Production server", + "variables": { + "username": { + "enum": ["demo", "demo1", "demo2"], + "default": "demo", + "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" + } + } + } + ], + "paths": { + "/path1": { + "summary": "path1 summary", + "description": "path1 description", + "get": {}, + "put": { + "tags": ["tag1", "tag2"], + "summary": "path2 components item summary", + "description": "path2 components item description", + "externalDocs": { + "description": "external documentation 1", + "url": "https://example.com/external-doc1" + }, + "operationId": "path-1-put", + "parameters": [ + { + "name": "parameter3", + "in": "query", + "description": "parameter3 description", + "required": false, + "deprecated": false, + "allowEmptyValue": true + }, + { + "$ref": "#/components/parameters/parameter2" + } + ], + "requestBody": { + "description": "request body description", + "content": { + "application/json": { + "schema": { + "type": "object" + }, + "example": { + "a": "b" + }, + "examples": { + "example1": { + "summary": "example 1 summary", + "description": "example 1 description", + "value": "c", + "externalValue": "https://example1.com/" + }, + "example2": { + "$ref": "#/components/examples/example3" + } + }, + "encoding": { + "historyMetadata": { + "contentType": "application/json", + "headers": { + "X-Rate-Limit-Limit": { + "description": "x-rate limit description", + "schema": { + "type": "integer" + } + }, + "Content-Type": { + "$ref": "#/components/headers/Content-Type" + } + }, + "style": "simple", + "explode": true, + "allowReserved": true + } + } + } + }, + "required": true + }, + "responses": { + "default": { + "description": "default response", + "content": { + "application/json": {} + }, + "links": { + "link1": { + "operationRef": "https://example.com/external-link", + "operationId": "operationId-1", + "parameters": { + "parameter1": "{$url}" + }, + "requestBody": "{$method}", + "description": "link 1 description", + "server": {} + }, + "link2": { + "$ref": "#/components/links/link3" + } + } + }, + "201": { + "$ref": "#/components/responses/201" + } + }, + "callbacks": { + "myCallback1": { + "{$request.query.queryUrl}": { + "post": { + "requestBody": { + "description": "Callback payload", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "callback successfully processed" + } + } + } + } + }, + "myCallback2": { + "$ref": "#/components/callbacks/callback1" + } + }, + "deprecated": false, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ], + "servers": [ + { + "url": "{username}.gigantic-server.com", + "description": "Production server", + "variables": { + "username": { + "enum": ["demo", "demo1", "demo2"], + "default": "demo", + "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" + } + } + } + ] + }, + "post": { + "requestBody": { + "$ref": "#/components/requestBodies/requestBody1" + }, + "responses": { + "default": { + "$ref": "#/components/responses/201" + } + } + }, + "delete": {}, + "options": {}, + "head": {}, + "patch": {}, + "trace": {}, + "servers": [ + { + "url": "{username}.gigantic-server.com", + "description": "Production server", + "variables": { + "username": { + "enum": ["demo", "demo1", "demo2"], + "default": "demo", + "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" + } + } + } + ], + "parameters": [ + { + "name": "parameter1", + "in": "query", + "description": "parameter1 description", + "required": false, + "deprecated": false, + "allowEmptyValue": true + }, + { + "$ref": "#/components/parameters/parameter2" + } + ] + }, + "/path2": { + "$ref": "#/components/pathItems/path2" + } + }, + "components": { + "schemas": { + "schema1": { + "type": "object" + }, + "schema2": { + "$ref": "#/components/schemas/schema1" + } + }, + "responses": { + "201": { + "description": "201 description", + "headers": { + "Content-Type": { + "description": "The number of allowed requests in the current period", + "schema": { + "type": "integer" + } + } + } + }, + "400": { + "$ref": "#/components/responses/201" + } + }, + "parameters": { + "parameter2": { + "name": "parameter2", + "in": "query", + "description": "parameter2 description", + "required": false, + "deprecated": false, + "allowEmptyValue": true, + "content": { + "application/json": {} + } + }, + "parameter3": { + "$ref": "#/components/parameters/parameter2" + } + }, + "examples": { + "example3": { + "summary": "example 3 summary", + "description": "example 3 description", + "value": "c", + "externalValue": "https://example3.com/" + }, + "example4": { + "$ref": "#/components/examples/example3" + } + }, + "requestBodies": { + "requestBody1": { + "description": "request body 1 description", + "required": true, + "content": { + "application/json": {} + } + }, + "requestBody2": { + "$ref": "#/components/requestBodies/requestBody1" + } + }, + "headers": { + "Content-Type": { + "description": "The number of allowed requests in the current period", + "schema": { + "type": "integer" + } + }, + "X-Custom-Header": { + "$ref": "#/components/headers/Content-Type" + } + }, + "securitySchemes": { + "securityScheme1": { + "type": "http", + "description": "security scheme description", + "name": "apiKey", + "in": "apiKey", + "scheme": "http", + "flows": { + "implicit": { + "authorizationUrl": "https://authorization-url.com/" + } + } + }, + "securityScheme2": { + "$ref": "#/components/securitySchemes/securityScheme1" + } + }, + "links": { + "link3": { + "description": "link 3 description" + }, + "link4": { + "$ref": "#/components/links/link4" + } + }, + "callbacks": { + "callback1": { + "{$request.query.queryUrl}": { + "put": { + "requestBody": { + "description": "Callback payload", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "callback successfully processed" + } + } + } + } + }, + "callback2": { + "$ref": "#/components/callbacks/callback1" + } + } + }, + "security": [ + { + "api_key": [] + } + ], + "tags": [ + { + "name": "tag1", + "description": "tag1 description", + "externalDocs": { + "description": "external docs tag description", + "url": "https://example.com/extenral-docs-tag" + } + } + ], + "externalDocs": { + "description": "external docs top level description", + "url": "https://example.com/extenral-docs-top-level" + } +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml new file mode 100644 index 0000000000..0f44890b39 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml @@ -0,0 +1,256 @@ +--- +openapi: 3.0.4 +info: + title: Sample Pet Store App + description: This is a sample server for a pet store. + termsOfService: https://example.com/terms/ + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0.html + version: 1.0.1 +servers: +- url: "{username}.gigantic-server.com" + description: Production server + variables: + username: + enum: + - demo + - demo1 + - demo2 + default: demo + description: This value is assigned by the service provider, in this example + `gigantic-server.com` +paths: + "/path1": + summary: path1 summary + description: path1 description + get: {} + put: + tags: + - tag1 + - tag2 + summary: path2 components item summary + description: path2 components item description + externalDocs: + description: external documentation 1 + url: https://example.com/external-doc1 + operationId: path-1-put + parameters: + - name: parameter3 + in: query + description: parameter3 description + required: false + deprecated: false + allowEmptyValue: true + - "$ref": "#/components/parameters/parameter2" + requestBody: + description: request body description + content: + application/json: + schema: + type: object + example: + a: b + examples: + example1: + summary: example 1 summary + description: example 1 description + value: c + externalValue: https://example1.com/ + example2: + "$ref": "#/components/examples/example3" + encoding: + historyMetadata: + contentType: application/json + headers: + X-Rate-Limit-Limit: + description: x-rate limit description + schema: + type: integer + Content-Type: + "$ref": "#/components/headers/Content-Type" + style: simple + explode: true + allowReserved: true + required: true + responses: + '201': + "$ref": "#/components/responses/201" + default: + description: default response + content: + application/json: {} + links: + link1: + operationRef: https://example.com/external-link + operationId: operationId-1 + parameters: + parameter1: "{$url}" + requestBody: "{$method}" + description: link 1 description + server: {} + link2: + "$ref": "#/components/links/link3" + callbacks: + myCallback1: + "{$request.query.queryUrl}": + post: + requestBody: + description: Callback payload + content: + application/json: + schema: + type: object + responses: + '200': + description: callback successfully processed + myCallback2: + "$ref": "#/components/callbacks/callback1" + deprecated: false + security: + - petstore_auth: + - write:pets + - read:pets + servers: + - url: "{username}.gigantic-server.com" + description: Production server + variables: + username: + enum: + - demo + - demo1 + - demo2 + default: demo + description: This value is assigned by the service provider, in this example + `gigantic-server.com` + post: + requestBody: + "$ref": "#/components/requestBodies/requestBody1" + responses: + default: + "$ref": "#/components/responses/201" + delete: {} + options: {} + head: {} + patch: {} + trace: {} + servers: + - url: "{username}.gigantic-server.com" + description: Production server + variables: + username: + enum: + - demo + - demo1 + - demo2 + default: demo + description: This value is assigned by the service provider, in this example + `gigantic-server.com` + parameters: + - name: parameter1 + in: query + description: parameter1 description + required: false + deprecated: false + allowEmptyValue: true + - "$ref": "#/components/parameters/parameter2" + "/path2": + "$ref": "#/components/pathItems/path2" +components: + schemas: + schema1: + type: object + schema2: + "$ref": "#/components/schemas/schema1" + responses: + '201': + description: 201 description + headers: + Content-Type: + description: The number of allowed requests in the current period + schema: + type: integer + '400': + "$ref": "#/components/responses/201" + parameters: + parameter2: + name: parameter2 + in: query + description: parameter2 description + required: false + deprecated: false + allowEmptyValue: true + content: + application/json: {} + parameter3: + "$ref": "#/components/parameters/parameter2" + examples: + example3: + summary: example 3 summary + description: example 3 description + value: c + externalValue: https://example3.com/ + example4: + "$ref": "#/components/examples/example3" + requestBodies: + requestBody1: + description: request body 1 description + required: true + content: + application/json: {} + requestBody2: + "$ref": "#/components/requestBodies/requestBody1" + headers: + Content-Type: + description: The number of allowed requests in the current period + schema: + type: integer + X-Custom-Header: + "$ref": "#/components/headers/Content-Type" + securitySchemes: + securityScheme1: + type: http + description: security scheme description + name: apiKey + in: apiKey + scheme: http + flows: + implicit: + authorizationUrl: https://authorization-url.com/ + securityScheme2: + "$ref": "#/components/securitySchemes/securityScheme1" + links: + link3: + description: link 3 description + link4: + "$ref": "#/components/links/link4" + callbacks: + callback1: + "{$request.query.queryUrl}": + put: + requestBody: + description: Callback payload + content: + application/json: + schema: + type: object + responses: + '200': + description: callback successfully processed + callback2: + "$ref": "#/components/callbacks/callback1" +security: +- api_key: [] +tags: +- name: tag1 + description: tag1 description + externalDocs: + description: external docs tag description + url: https://example.com/extenral-docs-tag +externalDocs: + description: external docs top level description + url: https://example.com/extenral-docs-top-level diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts new file mode 100644 index 0000000000..94522d856c --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts @@ -0,0 +1,24 @@ +import Benchmark from 'benchmark'; +import type { Event } from 'benchmark'; + +import lexicalAnalysisBench from './lexical-analysis.ts'; +import syntacticAnalysisBench from './syntactic-analysis.ts'; +import refractBench from './refract.ts'; +import parseBench from './parse.ts'; + +const suite = new Benchmark.Suite(); + +suite + .add(lexicalAnalysisBench) + .add(syntacticAnalysisBench) + .add(refractBench) + .add(parseBench) + // add listeners + .on('cycle', function (event: Event) { + console.info(String(event.target)); + }) + .on('complete', function () { + console.info('\nAll benchmarks have completed'); + }) + // run + .run(); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts new file mode 100644 index 0000000000..03584c3fe0 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts @@ -0,0 +1,37 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import Benchmark from 'benchmark'; +import type { Deferred, Event } from 'benchmark'; +import { lexicalAnalysis } from '@swagger-api/apidom-parser-adapter-json'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const source = fs.readFileSync(fixturePath).toString(); + +const options = { + name: 'lexical-analysis', + defer: true, + minSamples: 1400, + expected: '1,294 ops/sec ±1.97% (1473 runs sampled)', + async fn(deferred: Deferred) { + await lexicalAnalysis(source); + deferred.resolve(); + }, +}; + +export default options; + +// we're running as a script +if (import.meta.url === `file://${process.argv[1]}`) { + const bench = new Benchmark({ + ...options, + onComplete(event: Event) { + console.info(String(event.target)); + }, + onError(event: Event) { + console.error(event); + }, + }); + bench.run(); +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts new file mode 100644 index 0000000000..55ab60eb6c --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts @@ -0,0 +1,47 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import Benchmark from 'benchmark'; +import type { Deferred, Event } from 'benchmark'; + +import { parse } from '../../src/adapter.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const source = fs.readFileSync(fixturePath).toString(); + +const options = { + name: 'parse', + defer: true, + minSamples: 600, + expected: '41.22 ops/sec ±1.71% (658 runs sampled)', + async fn(deferred: Deferred) { + await parse(source); + deferred.resolve(); + }, +}; + +/** + * # Analysis of ApiDOM stages + * + * Parse stage: 14,56 ms + * Lexical Analysis phase: 0,77 ms + * Syntactic Analysis phase: 8,13 ms + * Refract stage: 9,7 ms + */ + +export default options; + +// we're running as a script +if (import.meta.url === `file://${process.argv[1]}`) { + const bench = new Benchmark({ + ...options, + onComplete(event: Event) { + console.info(String(event.target)); + }, + onError(event: Event) { + console.error(event); + }, + }); + bench.run(); +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/fixtures/asyncapi.json b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/fixtures/asyncapi.json new file mode 100644 index 0000000000..edede5c280 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/fixtures/asyncapi.json @@ -0,0 +1,137 @@ +{ + "asyncapi": "3.0.0", + "id": "urn:com:smartylighting:streetlights:server", + "info": { + "title": "AsyncAPI Sample App", + "version": "1.0.1" + }, + "servers": { + "production":{ + "url": "{username}.gigantic-server.com:{port}/{basePath}", + "protocol": "secure-mqtt", + "protocolVersion": "1.0.0", + "variables": { + "username": { + "default": "demo", + "description": "This value is assigned by the service provider, in this example `gigantic-server.com`", + "examples": ["value1", "value2"] + }, + "port": { + "enum": [ + "8883", + "8884" + ], + "default": "8883" + }, + "basePath": { + "default": "v2" + } + }, + "security": [ + { + "user_pass": [] + } + ] + } + }, + "channels": { + "user/{userId}/signup": { + "description": "This channel is used to exchange messages about users signing up", + "parameters": { + "userId": { + "description": "Id of the user.", + "schema": { + "type": "string" + } + } + }, + "subscribe": { + "message": { + "description": "A longer description of the message", + "payload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/user" + }, + "signup": { + "$ref": "#/components/schemas/signup" + } + } + } + } + }, + "bindings": { + "amqp": { + "is": "queue", + "queue": { + "exclusive": true + } + } + } + } + }, + "components": { + "x-extension": "value", + "schemas": { + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + } + }, + "messages": { + "userSignUp": { + "summary": "Action to sign a user up.", + "description": "Multiline description of what this action does.\nHere you have another line.\n", + "tags": [ + { + "name": "user" + }, + { + "name": "signup" + } + ], + "headers": { + "type": "object", + "properties": { + "applicationInstanceId": { + "description": "Unique identifier for a given instance of the publishing application", + "type": "string" + } + } + }, + "payload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/userCreate" + }, + "signup": { + "$ref": "#/components/schemas/signup" + } + } + } + } + } + } +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts new file mode 100644 index 0000000000..1a25042fa4 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts @@ -0,0 +1,38 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import Benchmark from 'benchmark'; +import type { Event } from 'benchmark'; +import { ObjectElement } from '@swagger-api/apidom-core'; +import { AsyncApi2Element } from '@swagger-api/apidom-ns-asyncapi-2'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const source = fs.readFileSync(fixturePath).toString(); +const pojo = JSON.parse(source); +const genericObjectElement = new ObjectElement(pojo); + +const options = { + name: 'refract', + minSamples: 600, + expected: '103 ops/sec ±0.92% (668 runs sampled)', + fn() { + AsyncApi2Element.refract(genericObjectElement); + }, +}; + +export default options; + +// we're running as a script +if (import.meta.url === `file://${process.argv[1]}`) { + const bench = new Benchmark({ + ...options, + onComplete(event: Event) { + console.info(String(event.target)); + }, + onError(event: Event) { + console.error(event); + }, + }); + bench.run(); +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts new file mode 100644 index 0000000000..6f6be785f5 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts @@ -0,0 +1,39 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import Benchmark from 'benchmark'; +import type { Deferred, Event } from 'benchmark'; +import { lexicalAnalysis, syntacticAnalysis } from '@swagger-api/apidom-parser-adapter-json'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const source = fs.readFileSync(fixturePath).toString(); +const cstP = lexicalAnalysis(source); + +const options = { + name: 'syntactic-analysis', + defer: true, + minSamples: 600, + expected: '123 ops/sec ±1.38% (676 runs sampled)', + async fn(deferred: Deferred) { + const cst = await cstP; + syntacticAnalysis(cst); + deferred.resolve(); + }, +}; + +export default options; + +// we're running as a script +if (import.meta.url === `file://${process.argv[1]}`) { + const bench = new Benchmark({ + ...options, + onComplete(event: Event) { + console.info(String(event.target)); + }, + onError(event: Event) { + console.error(event); + }, + }); + bench.run(); +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.declaration.json b/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.declaration.json new file mode 100644 index 0000000000..82d128fa80 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.declaration.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.json b/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.json new file mode 100644 index 0000000000..5cc50cd885 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*" + ] +} From a6488ab46b3bbfd043c9b5feb579afc1d0c89cc1 Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Mon, 17 Nov 2025 16:27:37 +0530 Subject: [PATCH 2/8] feat: add test --- package.json | 3 +- .../.mocharc.json | 4 +- .../README.md | 6 +- .../config/webpack/browser.config.js | 4 +- .../test/__snapshots__/adapter.ts.snap | 985 ++++++++++++++++++ .../test/adapter.ts | 94 ++ .../test/fixtures/sample-api.json | 681 ++++++------ .../test/fixtures/sample-api.yaml | 591 ++++++----- .../test/media-types.ts | 17 + .../test/mocha-bootstrap.ts | 11 + .../test/{ => perf}/index.ts | 0 .../test/{ => perf}/lexical-analysis.ts | 0 .../test/{ => perf}/parse.ts | 0 .../test/{ => perf}/refract.ts | 4 +- .../test/{ => perf}/syntactic-analysis.ts | 0 .../test/tsconfig.json | 10 + 16 files changed, 1849 insertions(+), 561 deletions(-) create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts rename packages/apidom-parser-adapter-asyncapi-json-3/test/{ => perf}/index.ts (100%) rename packages/apidom-parser-adapter-asyncapi-json-3/test/{ => perf}/lexical-analysis.ts (100%) rename packages/apidom-parser-adapter-asyncapi-json-3/test/{ => perf}/parse.ts (100%) rename packages/apidom-parser-adapter-asyncapi-json-3/test/{ => perf}/refract.ts (88%) rename packages/apidom-parser-adapter-asyncapi-json-3/test/{ => perf}/syntactic-analysis.ts (100%) create mode 100644 packages/apidom-parser-adapter-asyncapi-json-3/test/tsconfig.json diff --git a/package.json b/package.json index 8e638c81ce..a407f05be1 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ }, "homepage": "https://github.com/swagger-api/apidom#readme", "workspaces": [ - "./packages/*" + "./packages/*", + "./packages/apidom-parser-adapter-asyncapi-json-3" ], "devDependencies": { "@babel/cli": "=7.28.0", diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json index cbd88d9b45..f7eb537d54 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json @@ -2,7 +2,7 @@ "extensions": ["ts"], "loader": "ts-node/esm", "recursive": true, - "spec": "test/**/*.mjs", + "spec": "test/**/*.ts", "file": ["test/mocha-bootstrap.ts"], "ignore": ["test/perf/**/*.ts"] -} +} \ No newline at end of file diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/README.md b/packages/apidom-parser-adapter-asyncapi-json-3/README.md index f98420744c..fe067c2e2e 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/README.md +++ b/packages/apidom-parser-adapter-asyncapi-json-3/README.md @@ -39,11 +39,11 @@ Defines list of media types that this parser adapter recognizes. ### detect -[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts#L13) is based on a regular expression matching required AsyncApi 3.6.0 specification symbols in JSON format. +[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts#L13) is based on a regular expression matching required AsyncApi 3.0.0 specification symbols in JSON format. ### namespace -This adapter exposes an instance of [AsyncApi 3.x.y ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#asyncapi-2xy-namespace). +This adapter exposes an instance of [AsyncApi 3.x.y ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#asyncapi-3xy-namespace). ### parse @@ -90,5 +90,5 @@ const parser = new ApiDOMParser(); parser.use(asyncApiJsonAdapter); -const parseResult = await parser.parse('{"asyncapi": "3.6.0"}', { mediaType: asyncApiJsonAdapter.mediaTypes.latest('json') }); +const parseResult = await parser.parse('{"asyncapi": "3.0.1"}', { mediaType: asyncApiJsonAdapter.mediaTypes.latest('json') }); ``` diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js index 1137c6572c..3e21580d6e 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js +++ b/packages/apidom-parser-adapter-asyncapi-json-3/config/webpack/browser.config.js @@ -13,7 +13,7 @@ const browser = { path: path.resolve('./dist'), filename: 'apidom-parser-adapter-asyncapi-json-3.browser.js', libraryTarget: 'umd', - library: 'apidomParserAdapterAsyncApiJson2', + library: 'apidomParserAdapterAsyncApiJson3', }, resolve: { extensions: ['.ts', '.mjs', '.js', '.json'], @@ -57,7 +57,7 @@ const browserMin = { path: path.resolve('./dist'), filename: 'apidom-parser-adapter-asyncapi-json-3.browser.min.js', libraryTarget: 'umd', - library: 'apidomParserAdapterAsyncApiJson2', + library: 'apidomParserAdapterAsyncApiJson3', }, resolve: { extensions: ['.ts', '.mjs', '.js', '.json'], diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap new file mode 100644 index 0000000000..924fb82588 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap @@ -0,0 +1,985 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`adapter should parse 1`] = ` +(ParseResultElement + (AsyncApi3Element + (MemberElement + (StringElement) + (AsyncApiVersionElement)) + (MemberElement + (StringElement) + (IdentifierElement)) + (MemberElement + (StringElement) + (InfoElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ContactElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (LicenseElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ServersElement + (MemberElement + (StringElement) + (ServerElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ServerVariableElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecuritySchemeElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ServerBindingsElement + (MemberElement + (StringElement) + (KafkaServerBindingElement)))) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))))) + (MemberElement + (StringElement) + (ServerElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ServerBindingsElement + (MemberElement + (StringElement) + (JmsServerBindingElement)))))))) + (MemberElement + (StringElement) + (DefaultContentTypeElement)) + (MemberElement + (StringElement) + (ChannelsElement + (MemberElement + (StringElement) + (ChannelElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (MessagesElement + (MemberElement + (StringElement) + (MessageElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (CorrelationIDElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (MessageBindingsElement + (MemberElement + (StringElement) + (KafkaMessageBindingElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ArrayElement + (MessageExampleElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (ArrayElement + (MessageTraitElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (CorrelationIDElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (MessageBindingsElement + (MemberElement + (StringElement) + (KafkaMessageBindingElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ArrayElement + (MessageExampleElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))))))))) + (MemberElement + (StringElement) + (ParametersElement + (MemberElement + (StringElement) + (ParameterElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ArrayElement + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (ChannelBindingsElement + (MemberElement + (StringElement) + (AmqpChannelBindingElement)))) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ComponentsElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (MultiFormatSchemaElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (SchemaElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MessageElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ParameterElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (CorrelationIDElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (OperationTraitElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MessageTraitElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ServerBindingsElement + (MemberElement + (StringElement) + (KafkaServerBindingElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ChannelBindingsElement + (MemberElement + (StringElement) + (KafkaChannelBindingElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (OperationBindingsElement + (MemberElement + (StringElement) + (KafkaOperationBindingElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MessageBindingsElement + (MemberElement + (StringElement) + (KafkaMessageBindingElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ServerElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ServerVariableElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ChannelElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (OperationReplyElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (OperationReplyAddressElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ExternalDocumentationElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (TagElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (OperationsElement + (MemberElement + (StringElement) + (OperationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (TagsElement + (TagElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (OperationBindingsElement + (MemberElement + (StringElement) + (KafkaOperationBindingElement)))) + (MemberElement + (StringElement) + (ArrayElement + (OperationTraitElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (OperationBindingsElement + (MemberElement + (StringElement) + (KafkaOperationBindingElement)))) + (MemberElement + (StringElement) + (ArrayElement + (SecuritySchemeElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecuritySchemeElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)) + (MemberElement + (StringElement) + (ObjectElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (OperationReplyElement + (MemberElement + (StringElement) + (OperationReplyAddressElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))))))))))))) +`; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts new file mode 100644 index 0000000000..e2d373c7a2 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts @@ -0,0 +1,94 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { assert, expect } from 'chai'; +import { isParseResultElement, sexprs } from '@swagger-api/apidom-core'; +import { isAsyncApi3Element } from '@swagger-api/apidom-ns-asyncapi-3'; + +import * as adapter from '../src/adapter.ts'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const jsonSpec = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample-api.json')).toString(); +const yamlSpec = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample-api.yaml')).toString(); + +describe('adapter', function () { + context('given definition in JSON format', function () { + specify('should detect proper media type', async function () { + assert.isTrue(await adapter.detect(jsonSpec)); + }); + + specify('should detect minor version bump', async function () { + assert.isTrue(await adapter.detect('{"asyncapi": "3.25.0"}')); + }); + + specify('should detect patch version bump', async function () { + assert.isTrue(await adapter.detect('{"asyncapi": "3.6.1"}')); + }); + + specify('should detect minor and patch version bump', async function () { + assert.isTrue(await adapter.detect('{"asyncapi": "3.25.1"}')); + }); + }); + + context('given definition in YAML 1.3 format', function () { + specify('should detect proper media type', async function () { + assert.isFalse(await adapter.detect(yamlSpec)); + }); + }); + + context('given definition of unknown type', function () { + specify('should detect proper media type', async function () { + assert.isFalse(await adapter.detect('"openapi": "2.1.0"')); + }); + }); + + it('should parse', async function () { + const parseResult = await adapter.parse(jsonSpec, { sourceMap: true }); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isAsyncApi3Element(parseResult.api)); + expect(sexprs(parseResult)).toMatchSnapshot(); + }); + + context('given zero byte empty file', function () { + specify('should return empty parse result', async function () { + const parseResult = await adapter.parse('', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('given non-zero byte empty file', function () { + specify('should return empty parser result', async function () { + const parseResult = await adapter.parse(' ', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('given invalid json file', function () { + specify('should return empty parser result', async function () { + const parseResult = await adapter.parse(' a ', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('detectionRegExp', function () { + specify('should detect version ranges in forward compatible way', function () { + assert.isTrue(adapter.detectionRegExp.test('"asyncapi": "3.0.0"')); + assert.isTrue(adapter.detectionRegExp.test('"asyncapi": "3.0.145"')); + assert.isTrue(adapter.detectionRegExp.test('"asyncapi": "3.0.1"')); + }); + + specify('should reject invalid version ranges', function () { + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.0.0"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.1.0"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.1.1"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.5.0"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.6.01"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.6.01"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.6.01"')); + }); + }); +}); \ No newline at end of file diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json index fd0bb7f5c9..f1cbd29430 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.json @@ -1,369 +1,458 @@ { - "openapi": "3.0.4", + "asyncapi": "3.0.0", + "id": "urn:com:smartylighting:streetlights:server", "info": { - "title": "Sample Pet Store App", - "description": "This is a sample server for a pet store.", - "termsOfService": "https://example.com/terms/", + "title": "AsyncAPI Sample App", + "version": "1.0.1", + "description": "This is a sample server.", + "termsOfService": "http://asyncapi.org/terms/", "contact": { "name": "API Support", - "url": "https://www.example.com/support", - "email": "support@example.com" + "url": "http://www.asyncapi.org/support", + "email": "support@asyncapi.org" }, "license": { "name": "Apache 2.0", - "url": "https://www.apache.org/licenses/LICENSE-2.0.html" + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" }, - "version": "1.0.1" + "tags": [ + { + "name": "topLevelTag", + "description": "topLevelTag description", + "externalDocs": { + "description": "description of topLevelTag external docs", + "url": "https://example.com/topLevelTag" + } + } + ], + "externalDocs": { + "description": "description of this document external docs", + "url": "https://example.com/document" + } }, - "servers": [ - { - "url": "{username}.gigantic-server.com", - "description": "Production server", + "servers": { + "production": { + "host": "{username}.gigantic-server.com", + "protocol": "kafka", + "protocolVersion": "1.0.0", + "pathname": "/v2", + "description": "Production server description", + "title": "Production server", + "summary": "Production server summary.", "variables": { "username": { "enum": ["demo", "demo1", "demo2"], "default": "demo", - "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" + "description": "This value is assigned by the service provider, in this example `gigantic-server.com`", + "examples": ["demo", "demo1", "demo2"] + } + }, + "security": [ + { + "petstore_auth": { + "type": "apiKey", + "description": "Petstore Auth description", + "name": "Petstore Auth", + "in": "password", + "scheme": "Authorization", + "bearerFormat": "JWT", + "flows": { + "implicit": {}, + "password": {}, + "clientCredentials": {}, + "authorizationCode": {} + }, + "openIdConnectUrl": "security-scheme-openIdConnectUrl", + "scopes": ["write:pets", "read:pets"] + } } + ], + "bindings": { + "kafka": {} + }, + "tags": [ + { + "name": "serversLevelTag", + "description": "serversLevelTag description", + "externalDocs": { + "description": "description of serversLevelTag external docs", + "url": "https://example.com/serversLevelTag" + } + } + ] + }, + "development": { + "host": "gigantic-server.com", + "protocol": "kafka", + "bindings": { + "jms": {} } } - ], - "paths": { - "/path1": { - "summary": "path1 summary", - "description": "path1 description", - "get": {}, - "put": { - "tags": ["tag1", "tag2"], - "summary": "path2 components item summary", - "description": "path2 components item description", - "externalDocs": { - "description": "external documentation 1", - "url": "https://example.com/external-doc1" - }, - "operationId": "path-1-put", - "parameters": [ - { - "name": "parameter3", - "in": "query", - "description": "parameter3 description", - "required": false, - "deprecated": false, - "allowEmptyValue": true + }, + "defaultContentType": "application/json", + "channels": { + "userSignedUp": { + "address": "users.{userId}", + "title": "Users channel", + "description": "This channel is used to exchange messages about user events.", + "summary": "Exchange messages about user events.", + "messages": { + "userSignedUp": { + "headers": { + "type": "object" }, - { - "$ref": "#/components/parameters/parameter2" - } - ], - "requestBody": { - "description": "request body description", - "content": { - "application/json": { - "schema": { - "type": "object" - }, - "example": { - "a": "b" - }, - "examples": { - "example1": { - "summary": "example 1 summary", - "description": "example 1 description", - "value": "c", - "externalValue": "https://example1.com/" - }, - "example2": { - "$ref": "#/components/examples/example3" - } + "payload": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/user" }, - "encoding": { - "historyMetadata": { - "contentType": "application/json", - "headers": { - "X-Rate-Limit-Limit": { - "description": "x-rate limit description", - "schema": { - "type": "integer" - } - }, - "Content-Type": { - "$ref": "#/components/headers/Content-Type" - } - }, - "style": "simple", - "explode": true, - "allowReserved": true - } + "signup": { + "$ref": "#/components/schemas/signup" } } }, - "required": true - }, - "responses": { - "default": { - "description": "default response", - "content": { - "application/json": {} - }, - "links": { - "link1": { - "operationRef": "https://example.com/external-link", - "operationId": "operationId-1", - "parameters": { - "parameter1": "{$url}" - }, - "requestBody": "{$method}", - "description": "link 1 description", - "server": {} - }, - "link2": { - "$ref": "#/components/links/link3" + "correlationId": { + "description": "correlation id description", + "location": "http://asyncapi.com/" + }, + "contentType": "application/json", + "name": "name of the message", + "title": "title of the message", + "summary": "summary of the message", + "description": "A longer description of the message", + "tags": [ + { + "name": "tag3", + "description": "description of tag3", + "externalDocs": { + "description": "description of tag 3 external docs", + "url": "https://example.com/tag3" } } + ], + "externalDocs": { + "description": "description of message 1 external docs", + "url": "https://example.com/message-1-external-docs" }, - "201": { - "$ref": "#/components/responses/201" - } - }, - "callbacks": { - "myCallback1": { - "{$request.query.queryUrl}": { - "post": { - "requestBody": { - "description": "Callback payload", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "callback successfully processed" - } - } - } + "bindings": { + "kafka": { + "key": { + "type": "integer" + }, + "bindingVersion": "0.1.0" } }, - "myCallback2": { - "$ref": "#/components/callbacks/callback1" - } - }, - "deprecated": false, - "security": [ - { - "petstore_auth": [ - "write:pets", - "read:pets" - ] - } - ], - "servers": [ - { - "url": "{username}.gigantic-server.com", - "description": "Production server", - "variables": { - "username": { - "enum": ["demo", "demo1", "demo2"], - "default": "demo", - "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" - } + "examples": [ + { + "headers": { "Content-Type": "application/json" }, + "payload": "{\"a\":\"b\"}", + "name": "example name", + "summary": "example summary" } - } - ] + ], + "traits": [ + { + "headers": { + "type": "object" + }, + "correlationId": { + "description": "correlation id description", + "location": "http://asyncapi.com/" + }, + "contentType": "application/json", + "name": "name of the message trait", + "title": "title of the message trait", + "summary": "summary of the message trait", + "description": "A longer description of the message trait", + "tags": [ + { + "name": "tag4", + "description": "description of tag4", + "externalDocs": { + "description": "description of tag 4 external docs", + "url": "https://example.com/tag4" + } + } + ], + "externalDocs": { + "description": "description of message 1 trait external docs", + "url": "https://example.com/message-1-trait-external-docs" + }, + "bindings": { + "kafka": { + "key": { + "type": "integer" + }, + "bindingVersion": "0.1.0" + } + }, + "examples": [ + { + "headers": { "Content-Type": "application/json" }, + "payload": "{\"a\":\"b\"}", + "name": "example name", + "summary": "example summary" + } + ] + } + ] + } }, - "post": { - "requestBody": { - "$ref": "#/components/requestBodies/requestBody1" - }, - "responses": { - "default": { - "$ref": "#/components/responses/201" - } + "parameters": { + "userId": { + "description": "User identifier" } }, - "delete": {}, - "options": {}, - "head": {}, - "patch": {}, - "trace": {}, "servers": [ - { - "url": "{username}.gigantic-server.com", - "description": "Production server", - "variables": { - "username": { - "enum": ["demo", "demo1", "demo2"], - "default": "demo", - "description": "This value is assigned by the service provider, in this example `gigantic-server.com`" - } - } - } + { "$ref": "#/servers/rabbitmqInProd" }, + { "$ref": "#/servers/rabbitmqInStaging" } ], - "parameters": [ - { - "name": "parameter1", - "in": "query", - "description": "parameter1 description", - "required": false, - "deprecated": false, - "allowEmptyValue": true - }, + "bindings": { + "amqp": {} + }, + "tags": [ { - "$ref": "#/components/parameters/parameter2" + "name": "user", + "description": "User-related messages" } - ] + ], + "externalDocs": { + "description": "Find more info here", + "url": "https://example.com" + } }, - "/path2": { - "$ref": "#/components/pathItems/path2" + "user/loggedout": { + "$ref": "https://outside.com/#/path/to/channel" } }, "components": { "schemas": { - "schema1": { + "Schema1": { "type": "object" }, - "schema2": { - "$ref": "#/components/schemas/schema1" + "Schema2": { + "$ref": "#/components/schemas/Schema1" + }, + "Schema3": { + "schemaFormat": "application/vnd.aai.asyncapi;version=3.0.0", + "schema": {} } }, - "responses": { - "201": { - "description": "201 description", - "headers": { - "Content-Type": { - "description": "The number of allowed requests in the current period", - "schema": { - "type": "integer" - } - } - } - }, - "400": { - "$ref": "#/components/responses/201" + "messages": { + "Message1": {}, + "Message2": { + "$ref": "#/components/messages/Message1" + } + }, + "securitySchemes": { + "SecurityScheme1": {}, + "SecurityScheme2": { + "$ref": "#/components/securitySchemes/SecurityScheme1" } }, "parameters": { - "parameter2": { - "name": "parameter2", - "in": "query", - "description": "parameter2 description", - "required": false, - "deprecated": false, - "allowEmptyValue": true, - "content": { - "application/json": {} - } + "Parameter1": { + "description": "parameter description", + "location": "http://example.com" }, - "parameter3": { - "$ref": "#/components/parameters/parameter2" + "Parameter2": { + "$ref": "#/components/parameters/Parameter1" + } + }, + "correlationIds": { + "CorrelationID1": {}, + "CorrelationID2": { + "$ref": "#/components/correlationIds/CorrelationID1" + } + }, + "operationTraits": { + "OperationTrait1": {}, + "OperationTrait2": { + "$ref": "#/components/operationTraits/OperationTrait1" } }, - "examples": { - "example3": { - "summary": "example 3 summary", - "description": "example 3 description", - "value": "c", - "externalValue": "https://example3.com/" + "messageTraits": { + "MessageTrait1": {}, + "MessageTrait2": { + "$ref": "#/components/messageTraits/MessageTrait1" + } + }, + "serverBindings": { + "ServerBinding1": { + "kafka": {} }, - "example4": { - "$ref": "#/components/examples/example3" + "ServerBinding2": { + "$ref": "#/components/serverBindings/ServerBinding1" } }, - "requestBodies": { - "requestBody1": { - "description": "request body 1 description", - "required": true, - "content": { - "application/json": {} - } + "channelBindings": { + "ChannelBinding1": { + "kafka": {} }, - "requestBody2": { - "$ref": "#/components/requestBodies/requestBody1" + "ChannelBinding2": { + "$ref": "#/components/channelBindings/ChannelBinding1" } }, - "headers": { - "Content-Type": { - "description": "The number of allowed requests in the current period", - "schema": { - "type": "integer" - } + "operationBindings": { + "OperationBinding1": { + "kafka": {} }, - "X-Custom-Header": { - "$ref": "#/components/headers/Content-Type" + "OperationBinding2": { + "$ref": "#/components/operationBindings/OperationBinding1" } }, - "securitySchemes": { - "securityScheme1": { - "type": "http", - "description": "security scheme description", - "name": "apiKey", - "in": "apiKey", - "scheme": "http", - "flows": { - "implicit": { - "authorizationUrl": "https://authorization-url.com/" - } - } + "messageBindings": { + "MessageBinding1": { + "kafka": {} }, - "securityScheme2": { - "$ref": "#/components/securitySchemes/securityScheme1" + "MessageBinding2": { + "$ref": "#/components/messageBindings/MessageBinding1" + } + }, + "servers": { + "server1": {}, + "server2": { + "$ref": "#/components/servers/server1" } }, - "links": { - "link3": { - "description": "link 3 description" + "serverVariables": { + "port": { + "enum": ["8883", "8884"], + "default": "8883" }, - "link4": { - "$ref": "#/components/links/link4" + "port1": { + "$ref": "#/components/serverVariables/port" } }, - "callbacks": { - "callback1": { - "{$request.query.queryUrl}": { - "put": { - "requestBody": { - "description": "Callback payload", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "callback successfully processed" + "channels": { + "channel1": {}, + "channel2": { + "$ref": "#/components/channels/channel1" + } + }, + "operations": { + "SignUp1": {}, + "SignUp2": { + "$ref": "#/components/operations/SignUp1" + } + }, + "replies": { + "UserReply1": {}, + "UserReply2": { + "$ref": "#/components/replies/UserReply1" + } + }, + "replyAddresses": { + "UserReplyAddress1": {}, + "UserReplyAddress2": { + "$ref": "#/components/replyAddresses/UserReplyAddress1" + } + }, + "externalDocs": { + "ExternalDoc1": {}, + "ExternalDoc2": { + "$ref": "#/components/externalDocs/ExternalDoc1" + } + }, + "tags": { + "UserTag1": {}, + "UserTag2": { + "$ref": "#/components/tags/UserTag1" + } + } + }, + "operations": { + "onUserSignUp": { + "title": "User sign up", + "summary": "Action to sign a user up.", + "description": "A longer description", + "channel": { + "$ref": "#/channels/userSignup" + }, + "action": "send", + "tags": [ + { + "name": "operationTag", + "description": "operationTag description", + "externalDocs": { + "description": "description of operationTag external docs", + "url": "https://example.com/operationTag" + } + } + ], + "bindings": { + "kafka": {} + }, + "traits": [ + { + "title": "Operation trait title", + "description": "Operation trait description", + "summary": "Operation trait summary", + "bindings": { + "kafka": {} + }, + "security": [ + { + "petstore_auth": { + "type": "apiKey", + "description": "Petstore Auth description", + "name": "Petstore Auth", + "in": "password", + "scheme": "Authorization", + "bearerFormat": "JWT", + "flows": { + "implicit": {}, + "password": {}, + "clientCredentials": {}, + "authorizationCode": {} + }, + "openIdConnectUrl": "security-scheme-openIdConnectUrl", + "scopes": ["write:pets", "read:pets"] } } + ], + "externalDocs": { + "description": "Find more info here", + "url": "https://example.com" } } - }, - "callback2": { - "$ref": "#/components/callbacks/callback1" - } - } - }, - "security": [ - { - "api_key": [] - } - ], - "tags": [ - { - "name": "tag1", - "description": "tag1 description", + ], + "security": [ + { + "petstore_auth": { + "type": "apiKey", + "description": "Petstore Auth description", + "name": "Petstore Auth", + "in": "password", + "scheme": "Authorization", + "bearerFormat": "JWT", + "flows": { + "implicit": {}, + "password": {}, + "clientCredentials": {}, + "authorizationCode": {} + }, + "openIdConnectUrl": "security-scheme-openIdConnectUrl", + "scopes": ["write:pets", "read:pets"] + } + } + ], "externalDocs": { - "description": "external docs tag description", - "url": "https://example.com/extenral-docs-tag" + "description": "Find more info here", + "url": "https://example.com" + }, + "messages": [{ "$ref": "#/components/messages/Message1" }], + "reply": { + "address": { + "description": "Reply address description", + "location": "http://example.com/reply" + }, + "channel": { + "$ref": "#/components/channels/channel1" + }, + "messages": [{ "$ref": "#/components/messages/Message1" }] } } - ], - "externalDocs": { - "description": "external docs top level description", - "url": "https://example.com/extenral-docs-top-level" } } diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml index 0f44890b39..d13011cce9 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/fixtures/sample-api.yaml @@ -1,256 +1,337 @@ --- -openapi: 3.0.4 -info: - title: Sample Pet Store App - description: This is a sample server for a pet store. - termsOfService: https://example.com/terms/ - contact: - name: API Support - url: https://www.example.com/support - email: support@example.com - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0.html - version: 1.0.1 -servers: -- url: "{username}.gigantic-server.com" - description: Production server - variables: - username: - enum: - - demo - - demo1 - - demo2 - default: demo - description: This value is assigned by the service provider, in this example - `gigantic-server.com` -paths: - "/path1": - summary: path1 summary - description: path1 description - get: {} - put: - tags: - - tag1 - - tag2 - summary: path2 components item summary - description: path2 components item description - externalDocs: - description: external documentation 1 - url: https://example.com/external-doc1 - operationId: path-1-put - parameters: - - name: parameter3 - in: query - description: parameter3 description - required: false - deprecated: false - allowEmptyValue: true - - "$ref": "#/components/parameters/parameter2" - requestBody: - description: request body description - content: - application/json: - schema: - type: object - example: - a: b - examples: - example1: - summary: example 1 summary - description: example 1 description - value: c - externalValue: https://example1.com/ - example2: - "$ref": "#/components/examples/example3" - encoding: - historyMetadata: - contentType: application/json - headers: - X-Rate-Limit-Limit: - description: x-rate limit description - schema: - type: integer - Content-Type: - "$ref": "#/components/headers/Content-Type" - style: simple - explode: true - allowReserved: true - required: true - responses: - '201': - "$ref": "#/components/responses/201" - default: - description: default response - content: - application/json: {} - links: - link1: - operationRef: https://example.com/external-link - operationId: operationId-1 - parameters: - parameter1: "{$url}" - requestBody: "{$method}" - description: link 1 description - server: {} - link2: - "$ref": "#/components/links/link3" - callbacks: - myCallback1: - "{$request.query.queryUrl}": - post: - requestBody: - description: Callback payload - content: - application/json: - schema: - type: object - responses: - '200': - description: callback successfully processed - myCallback2: - "$ref": "#/components/callbacks/callback1" - deprecated: false - security: - - petstore_auth: - - write:pets - - read:pets - servers: - - url: "{username}.gigantic-server.com" - description: Production server - variables: - username: - enum: - - demo - - demo1 - - demo2 - default: demo - description: This value is assigned by the service provider, in this example - `gigantic-server.com` - post: - requestBody: - "$ref": "#/components/requestBodies/requestBody1" - responses: - default: - "$ref": "#/components/responses/201" - delete: {} - options: {} - head: {} - patch: {} - trace: {} - servers: - - url: "{username}.gigantic-server.com" - description: Production server - variables: - username: - enum: - - demo - - demo1 - - demo2 - default: demo - description: This value is assigned by the service provider, in this example - `gigantic-server.com` - parameters: - - name: parameter1 - in: query - description: parameter1 description - required: false - deprecated: false - allowEmptyValue: true - - "$ref": "#/components/parameters/parameter2" - "/path2": - "$ref": "#/components/pathItems/path2" -components: - schemas: - schema1: - type: object - schema2: - "$ref": "#/components/schemas/schema1" - responses: - '201': - description: 201 description - headers: - Content-Type: - description: The number of allowed requests in the current period - schema: - type: integer - '400': - "$ref": "#/components/responses/201" - parameters: - parameter2: - name: parameter2 - in: query - description: parameter2 description - required: false - deprecated: false - allowEmptyValue: true - content: - application/json: {} - parameter3: - "$ref": "#/components/parameters/parameter2" - examples: - example3: - summary: example 3 summary - description: example 3 description - value: c - externalValue: https://example3.com/ - example4: - "$ref": "#/components/examples/example3" - requestBodies: - requestBody1: - description: request body 1 description - required: true - content: - application/json: {} - requestBody2: - "$ref": "#/components/requestBodies/requestBody1" - headers: - Content-Type: - description: The number of allowed requests in the current period - schema: - type: integer - X-Custom-Header: - "$ref": "#/components/headers/Content-Type" - securitySchemes: - securityScheme1: - type: http - description: security scheme description - name: apiKey - in: apiKey - scheme: http - flows: - implicit: - authorizationUrl: https://authorization-url.com/ - securityScheme2: - "$ref": "#/components/securitySchemes/securityScheme1" - links: - link3: - description: link 3 description - link4: - "$ref": "#/components/links/link4" - callbacks: - callback1: - "{$request.query.queryUrl}": - put: - requestBody: - description: Callback payload - content: - application/json: - schema: - type: object - responses: - '200': - description: callback successfully processed - callback2: - "$ref": "#/components/callbacks/callback1" -security: -- api_key: [] -tags: -- name: tag1 - description: tag1 description - externalDocs: - description: external docs tag description - url: https://example.com/extenral-docs-tag -externalDocs: - description: external docs top level description - url: https://example.com/extenral-docs-top-level +asyncapi: "3.0.0" +id: "urn:com:smartylighting:streetlights:server" +info: + title: "AsyncAPI Sample App" + version: "1.0.1" + description: "This is a sample server." + termsOfService: "http://asyncapi.org/terms/" + contact: + name: "API Support" + url: "http://www.asyncapi.org/support" + email: "support@asyncapi.org" + license: + name: "Apache 2.0" + url: "http://www.apache.org/licenses/LICENSE-2.0.html" + tags: + - name: "topLevelTag" + description: "topLevelTag description" + externalDocs: + description: "description of topLevelTag external docs" + url: "https://example.com/topLevelTag" + externalDocs: + description: "description of this document external docs" + url: "https://example.com/document" +servers: + production: + host: "{username}.gigantic-server.com" + protocol: "kafka" + protocolVersion: "1.0.0" + pathname: "/v2" + description: "Production server description" + title: "Production server" + summary: "Production server summary." + variables: + username: + enum: + - "demo" + - "demo1" + - "demo2" + default: "demo" + description: "This value is assigned by the service provider, in this example `gigantic-server.com`" + examples: + - "demo" + - "demo1" + - "demo2" + security: + - + petstore_auth: + type: "apiKey" + description: "Petstore Auth description" + name: "Petstore Auth" + in: "password" + scheme: "Authorization" + bearerFormat: "JWT" + flows: + implicit: + password: + clientCredentials: + authorizationCode: + openIdConnectUrl: "security-scheme-openIdConnectUrl" + scopes: + - "write:pets" + - "read:pets" + bindings: + kafka: + tags: + - name: "serversLevelTag" + description: "serversLevelTag description" + externalDocs: + description: "description of serversLevelTag external docs" + url: "https://example.com/serversLevelTag" + development: + host: "gigantic-server.com" + protocol: "kafka" + bindings: + jms: +defaultContentType: "application/json" +channels: + userSignedUp: + address: "users.{userId}" + title: "Users channel" + description: "This channel is used to exchange messages about user events." + summary: "Exchange messages about user events." + messages: + userSignedUp: + headers: + type: "object" + payload: + type: "object" + properties: + user: + $ref: "#/components/schemas/user" + signup: + $ref: "#/components/schemas/signup" + correlationId: + description: "correlation id description" + location: "http://asyncapi.com/" + contentType: "application/json" + name: "name of the message" + title: "title of the message" + summary: "summary of the message" + description: "A longer description of the message" + tags: + - name: "tag3" + description: "description of tag3" + externalDocs: + description: "description of tag 3 external docs" + url: "https://example.com/tag3" + externalDocs: + description: "description of message 1 external docs" + url: "https://example.com/message-1-external-docs" + bindings: + kafka: + key: + type: "integer" + bindingVersion: "0.1.0" + examples: + - + headers: + Content-Type: "application/json" + payload: "{"a":"b"}" + name: "example name" + summary: "example summary" + traits: + - + headers: + type: "object" + correlationId: + description: "correlation id description" + location: "http://asyncapi.com/" + contentType: "application/json" + name: "name of the message trait" + title: "title of the message trait" + summary: "summary of the message trait" + description: "A longer description of the message trait" + tags: + - name: "tag4" + description: "description of tag4" + externalDocs: + description: "description of tag 4 external docs" + url: "https://example.com/tag4" + externalDocs: + description: "description of message 1 trait external docs" + url: "https://example.com/message-1-trait-external-docs" + bindings: + kafka: + key: + type: "integer" + bindingVersion: "0.1.0" + examples: + - + headers: + Content-Type: "application/json" + payload: "{"a":"b"}" + name: "example name" + summary: "example summary" + parameters: + userId: + description: "User identifier" + servers: + - $ref: "#/servers/rabbitmqInProd" + - $ref: "#/servers/rabbitmqInStaging" + bindings: + amqp: + tags: + - name: "user" + description: "User-related messages" + externalDocs: + description: "Find more info here" + url: "https://example.com" + user/loggedout: + $ref: "https://outside.com/#/path/to/channel" +components: + schemas: + Schema1: + type: "object" + Schema2: + $ref: "#/components/schemas/Schema1" + Schema3: + schemaFormat: "application/vnd.aai.asyncapi;version=3.0.0" + schema: + messages: + Message1: + Message2: + $ref: "#/components/messages/Message1" + securitySchemes: + SecurityScheme1: + SecurityScheme2: + $ref: "#/components/securitySchemes/SecurityScheme1" + parameters: + Parameter1: + description: "parameter description" + location: "http://example.com" + Parameter2: + $ref: "#/components/parameters/Parameter1" + correlationIds: + CorrelationID1: + CorrelationID2: + $ref: "#/components/correlationIds/CorrelationID1" + operationTraits: + OperationTrait1: + OperationTrait2: + $ref: "#/components/operationTraits/OperationTrait1" + messageTraits: + MessageTrait1: + MessageTrait2: + $ref: "#/components/messageTraits/MessageTrait1" + serverBindings: + ServerBinding1: + kafka: + ServerBinding2: + $ref: "#/components/serverBindings/ServerBinding1" + channelBindings: + ChannelBinding1: + kafka: + ChannelBinding2: + $ref: "#/components/channelBindings/ChannelBinding1" + operationBindings: + OperationBinding1: + kafka: + OperationBinding2: + $ref: "#/components/operationBindings/OperationBinding1" + messageBindings: + MessageBinding1: + kafka: + MessageBinding2: + $ref: "#/components/messageBindings/MessageBinding1" + servers: + server1: + server2: + $ref: "#/components/servers/server1" + serverVariables: + port: + enum: + - "8883" + - "8884" + default: "8883" + port1: + $ref: "#/components/serverVariables/port" + channels: + channel1: + channel2: + $ref: "#/components/channels/channel1" + operations: + SignUp1: + SignUp2: + $ref: "#/components/operations/SignUp1" + replies: + UserReply1: + UserReply2: + $ref: "#/components/replies/UserReply1" + replyAddresses: + UserReplyAddress1: + UserReplyAddress2: + $ref: "#/components/replyAddresses/UserReplyAddress1" + externalDocs: + ExternalDoc1: + ExternalDoc2: + $ref: "#/components/externalDocs/ExternalDoc1" + tags: + UserTag1: + UserTag2: + $ref: "#/components/tags/UserTag1" +operations: + onUserSignUp: + title: "User sign up" + summary: "Action to sign a user up." + description: "A longer description" + channel: + $ref: "#/channels/userSignup" + action: "send" + tags: + - name: "operationTag" + description: "operationTag description" + externalDocs: + description: "description of operationTag external docs" + url: "https://example.com/operationTag" + bindings: + kafka: + traits: + - title: "Operation trait title" + description: "Operation trait description" + summary: "Operation trait summary" + bindings: + kafka: + security: + - + petstore_auth: + type: "apiKey" + description: "Petstore Auth description" + name: "Petstore Auth" + in: "password" + scheme: "Authorization" + bearerFormat: "JWT" + flows: + implicit: + password: + clientCredentials: + authorizationCode: + openIdConnectUrl: "security-scheme-openIdConnectUrl" + scopes: + - "write:pets" + - "read:pets" + externalDocs: + description: "Find more info here" + url: "https://example.com" + security: + - + petstore_auth: + type: "apiKey" + description: "Petstore Auth description" + name: "Petstore Auth" + in: "password" + scheme: "Authorization" + bearerFormat: "JWT" + flows: + implicit: + password: + clientCredentials: + authorizationCode: + openIdConnectUrl: "security-scheme-openIdConnectUrl" + scopes: + - "write:pets" + - "read:pets" + externalDocs: + description: "Find more info here" + url: "https://example.com" + messages: + - $ref: "#/components/messages/Message1" + reply: + address: + description: "Reply address description" + location: "http://example.com/reply" + channel: + $ref: "#/components/channels/channel1" + messages: + - $ref: "#/components/messages/Message1" \ No newline at end of file diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts new file mode 100644 index 0000000000..55b5b6b9a9 --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts @@ -0,0 +1,17 @@ +import { assert } from 'chai'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +import * as asyncApiJsonAdapter from '../src/adapter.ts'; + +describe('given adapter is used in parser', function () { + const parser = new ApiDOMParser().use(asyncApiJsonAdapter); + + context('given AsyncAPI 3.0.0 definition in JSON format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('{"asyncapi": "3.0.0"}'); + + assert.strictEqual(mediaType, 'application/vnd.aai.asyncapi+json;version=3.0.0'); + }); + + }); +}); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts new file mode 100644 index 0000000000..aec560d03f --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts @@ -0,0 +1,11 @@ +import * as chai from 'chai'; +import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + +// @ts-ignore +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/index.ts similarity index 100% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/index.ts rename to packages/apidom-parser-adapter-asyncapi-json-3/test/perf/index.ts diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts similarity index 100% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/lexical-analysis.ts rename to packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts similarity index 100% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/parse.ts rename to packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts similarity index 88% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts rename to packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts index 1a25042fa4..e55696656e 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/refract.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; import Benchmark from 'benchmark'; import type { Event } from 'benchmark'; import { ObjectElement } from '@swagger-api/apidom-core'; -import { AsyncApi2Element } from '@swagger-api/apidom-ns-asyncapi-2'; +import { AsyncApi3Element } from '@swagger-api/apidom-ns-asyncapi-3'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); @@ -17,7 +17,7 @@ const options = { minSamples: 600, expected: '103 ops/sec ±0.92% (668 runs sampled)', fn() { - AsyncApi2Element.refract(genericObjectElement); + AsyncApi3Element.refract(genericObjectElement); }, }; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts similarity index 100% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/syntactic-analysis.ts rename to packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/tsconfig.json b/packages/apidom-parser-adapter-asyncapi-json-3/test/tsconfig.json new file mode 100644 index 0000000000..405aae2d2f --- /dev/null +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "node" + }, + "include": [ + "." + ] +} From ad2a65bc00f3e87cdd1e1e5084fd7d2c078fb701 Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Mon, 17 Nov 2025 17:46:29 +0530 Subject: [PATCH 3/8] chore: fix lint error --- .../apidom-parser-adapter-asyncapi-json-3/test/adapter.ts | 8 ++++---- .../test/media-types.ts | 1 - .../test/mocha-bootstrap.ts | 4 ++-- .../test/perf/lexical-analysis.ts | 4 ++-- .../test/perf/parse.ts | 4 ++-- .../test/perf/refract.ts | 4 ++-- .../test/perf/syntactic-analysis.ts | 4 ++-- 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts index e2d373c7a2..d0f9ee8cf5 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts @@ -7,9 +7,9 @@ import { isAsyncApi3Element } from '@swagger-api/apidom-ns-asyncapi-3'; import * as adapter from '../src/adapter.ts'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const jsonSpec = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample-api.json')).toString(); -const yamlSpec = fs.readFileSync(path.join(__dirname, 'fixtures', 'sample-api.yaml')).toString(); +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const jsonSpec = fs.readFileSync(path.join(dirname, 'fixtures', 'sample-api.json')).toString(); +const yamlSpec = fs.readFileSync(path.join(dirname, 'fixtures', 'sample-api.yaml')).toString(); describe('adapter', function () { context('given definition in JSON format', function () { @@ -91,4 +91,4 @@ describe('adapter', function () { assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.6.01"')); }); }); -}); \ No newline at end of file +}); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts index 55b5b6b9a9..08be514354 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/media-types.ts @@ -12,6 +12,5 @@ describe('given adapter is used in parser', function () { assert.strictEqual(mediaType, 'application/vnd.aai.asyncapi+json;version=3.0.0'); }); - }); }); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts index aec560d03f..6f5b4d36e6 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts @@ -1,9 +1,9 @@ import * as chai from 'chai'; import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; -// @ts-ignore +// eslint-disable-next-line import/no-relative-packages import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; -// @ts-ignore +// eslint-disable-next-line import/no-relative-packages import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; chai.use(jestSnapshotPlugin()); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts index 03584c3fe0..a66e7db268 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/lexical-analysis.ts @@ -5,8 +5,8 @@ import type { Deferred, Event } from 'benchmark'; import { lexicalAnalysis } from '@swagger-api/apidom-parser-adapter-json'; import { fileURLToPath } from 'node:url'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(dirname, 'fixtures/asyncapi.json'); const source = fs.readFileSync(fixturePath).toString(); const options = { diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts index 55ab60eb6c..af07996e30 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/parse.ts @@ -6,8 +6,8 @@ import type { Deferred, Event } from 'benchmark'; import { parse } from '../../src/adapter.ts'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(dirname, 'fixtures/asyncapi.json'); const source = fs.readFileSync(fixturePath).toString(); const options = { diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts index e55696656e..cb50d4d8da 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/refract.ts @@ -6,8 +6,8 @@ import type { Event } from 'benchmark'; import { ObjectElement } from '@swagger-api/apidom-core'; import { AsyncApi3Element } from '@swagger-api/apidom-ns-asyncapi-3'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(dirname, 'fixtures/asyncapi.json'); const source = fs.readFileSync(fixturePath).toString(); const pojo = JSON.parse(source); const genericObjectElement = new ObjectElement(pojo); diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts index 6f6be785f5..fed54ae7d2 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/perf/syntactic-analysis.ts @@ -5,8 +5,8 @@ import type { Deferred, Event } from 'benchmark'; import { lexicalAnalysis, syntacticAnalysis } from '@swagger-api/apidom-parser-adapter-json'; import { fileURLToPath } from 'node:url'; -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const fixturePath = path.join(__dirname, 'fixtures/asyncapi.json'); +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturePath = path.join(dirname, 'fixtures/asyncapi.json'); const source = fs.readFileSync(fixturePath).toString(); const cstP = lexicalAnalysis(source); From 85f531f5e5f0cc14de7d6bd77154050a3fafcbd9 Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Tue, 18 Nov 2025 12:32:27 +0530 Subject: [PATCH 4/8] chore: fix lint issue --- .../apidom-parser-adapter-asyncapi-json-3/.mocharc.json | 2 +- .../apidom-parser-adapter-asyncapi-json-3/package.json | 2 +- .../__snapshots__/{adapter.ts.snap => adapter.mjs.snap} | 0 .../test/mocha-bootstrap.ts | 9 ++++----- 4 files changed, 6 insertions(+), 7 deletions(-) rename packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/{adapter.ts.snap => adapter.mjs.snap} (100%) diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json index f7eb537d54..7e6d84b72e 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json +++ b/packages/apidom-parser-adapter-asyncapi-json-3/.mocharc.json @@ -2,7 +2,7 @@ "extensions": ["ts"], "loader": "ts-node/esm", "recursive": true, - "spec": "test/**/*.ts", + "spec": "test/**/*.mjs", "file": ["test/mocha-bootstrap.ts"], "ignore": ["test/perf/**/*.ts"] } \ No newline at end of file diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/package.json b/packages/apidom-parser-adapter-asyncapi-json-3/package.json index 0923fd7a38..4735555777 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/package.json +++ b/packages/apidom-parser-adapter-asyncapi-json-3/package.json @@ -39,7 +39,7 @@ "type": "git", "url": "git+https://github.com/swagger-api/apidom.git" }, - "author": "Vladimir Gorej", + "author": "Shikha Lakhotia", "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.26.10", diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.mjs.snap similarity index 100% rename from packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.ts.snap rename to packages/apidom-parser-adapter-asyncapi-json-3/test/__snapshots__/adapter.mjs.snap diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts index 6f5b4d36e6..3b729a2d95 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts @@ -1,10 +1,9 @@ import * as chai from 'chai'; import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; - -// eslint-disable-next-line import/no-relative-packages -import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; -// eslint-disable-next-line import/no-relative-packages -import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; +// @ts-ignore +import * as jestApiDOMSerializer from 'apidom-monorepo/scripts/jest-serializer-apidom.mjs'; +// @ts-ignore +import * as jestStringSerializer from 'apidom-monorepo/scripts/jest-serializer-string.mjs'; chai.use(jestSnapshotPlugin()); addSerializer(jestApiDOMSerializer); From b83bfc1bb1d1efbb5828f0264d056937d43f9634 Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Tue, 18 Nov 2025 14:02:14 +0530 Subject: [PATCH 5/8] chore: update import --- package-lock.json | 3 ++- .../test/mocha-bootstrap.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c9e684d79..9b82db927d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.0.0-ignore", "license": "Apache-2.0", "workspaces": [ - "./packages/*" + "./packages/*", + "./packages/apidom-parser-adapter-asyncapi-json-3" ], "devDependencies": { "@babel/cli": "=7.28.0", diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts index 3b729a2d95..aec560d03f 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/mocha-bootstrap.ts @@ -1,9 +1,10 @@ import * as chai from 'chai'; import { jestSnapshotPlugin, addSerializer } from 'mocha-chai-jest-snapshot'; + // @ts-ignore -import * as jestApiDOMSerializer from 'apidom-monorepo/scripts/jest-serializer-apidom.mjs'; +import * as jestApiDOMSerializer from '../../../scripts/jest-serializer-apidom.mjs'; // @ts-ignore -import * as jestStringSerializer from 'apidom-monorepo/scripts/jest-serializer-string.mjs'; +import * as jestStringSerializer from '../../../scripts/jest-serializer-string.mjs'; chai.use(jestSnapshotPlugin()); addSerializer(jestApiDOMSerializer); From dfbd010514466cc8c3828ebde671d1709ce1bf2a Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Tue, 18 Nov 2025 14:10:00 +0530 Subject: [PATCH 6/8] chore: update test .eslintrc file --- .../test/.eslintrc | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc b/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc index 6ed40ebc25..c47eea4f48 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/.eslintrc @@ -1,5 +1,55 @@ { + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], "rules": { - "@typescript-eslint/no-var-requires": 0 + "no-void": 0, + "func-names": 0, + "prefer-arrow-callback": 0, + "no-array-constructor": 0, + "prefer-rest-params": 0, + "no-new-wrappers": 0, + "mocha/no-skipped-tests": 2, + "mocha/handle-done-callback": 2, + "mocha/valid-suite-description": 2, + "mocha/no-mocha-arrows": 2, + "mocha/no-hooks-for-single-case": 2, + "mocha/no-sibling-hooks": 2, + "mocha/no-top-level-hooks": 2, + "mocha/no-identical-title": 2, + "mocha/no-nested-tests": 2, + "mocha/no-exclusive-tests": 2, + "no-underscore-dangle": 0, + "import/no-relative-packages": 0, + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "variable", + "format": ["camelCase", "PascalCase", "UPPER_CASE"], + "leadingUnderscore": "forbid" + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__dirname$", + "match": true + } + }, + { + "selector": "variable", + "format": null, + "filter": { + "regex": "^__filename$", + "match": true + } + } + ] } } From 2a3b60ee68c883417ee1657cc509c5c02aa9fd2b Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Wed, 19 Nov 2025 15:49:47 +0530 Subject: [PATCH 7/8] chore: resolve review comment --- package-lock.json | 3 +-- package.json | 3 +-- packages/apidom-parser-adapter-asyncapi-json-3/README.md | 7 ++++--- .../apidom-parser-adapter-asyncapi-json-3/test/adapter.ts | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b82db927d..8c9e684d79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "0.0.0-ignore", "license": "Apache-2.0", "workspaces": [ - "./packages/*", - "./packages/apidom-parser-adapter-asyncapi-json-3" + "./packages/*" ], "devDependencies": { "@babel/cli": "=7.28.0", diff --git a/package.json b/package.json index a407f05be1..8e638c81ce 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,7 @@ }, "homepage": "https://github.com/swagger-api/apidom#readme", "workspaces": [ - "./packages/*", - "./packages/apidom-parser-adapter-asyncapi-json-3" + "./packages/*" ], "devDependencies": { "@babel/cli": "=7.28.0", diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/README.md b/packages/apidom-parser-adapter-asyncapi-json-3/README.md index fe067c2e2e..edcd9b02b5 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/README.md +++ b/packages/apidom-parser-adapter-asyncapi-json-3/README.md @@ -2,10 +2,11 @@ `@swagger-api/apidom-parser-adapter-asyncapi-json-3` is a parser adapter for following AsyncAPI specification versions defined in [JSON format](https://www.json.org/json-en.html): - +- [AsyncAPI 3.0.0 specification](https://github.com/asyncapi/spec/blob/v3.0.0/spec/asyncapi.md) +- [AsyncAPI 3.0.1 specification](https://github.com/asyncapi/spec/blob/v3.0.1/spec/asyncapi.md) Under the hood this adapter uses [@swagger-api/apidom-parser-adapter-json](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-json) -to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom#base-namespace) +to parse a source string into generic ApiDOM in [base ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-core#base-namespace) which is then refracted with [AsyncApi 3.x.y Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-asyncapi-3#refractors). ## Installation @@ -75,7 +76,7 @@ await detect('{"asyncapi": "3.0.1"}'); // => true await detect('test'); // => false // parsing -const parseResult = await parse('{"asyncapi": "3.6.0"}', { sourceMap: true }); +const parseResult = await parse('{"asyncapi": "3.0.0"}', { sourceMap: true }); ``` ### Indirect usage diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts index d0f9ee8cf5..cf8d73a5c0 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts +++ b/packages/apidom-parser-adapter-asyncapi-json-3/test/adapter.ts @@ -88,7 +88,7 @@ describe('adapter', function () { assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.5.0"')); assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "2.6.01"')); assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.6.01"')); - assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.6.01"')); + assert.isFalse(adapter.detectionRegExp.test('"asyncapi": "3.06.0"')); }); }); }); From 7e524d2a1abd1b329161ad132b56dd4c7b7626c8 Mon Sep 17 00:00:00 2001 From: ShikhaSaboo Date: Wed, 19 Nov 2025 18:25:02 +0530 Subject: [PATCH 8/8] chore: resolve review comment --- .../apidom-ls/src/services/validation/validation-service.ts | 2 +- packages/apidom-parser-adapter-asyncapi-json-3/README.md | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/apidom-ls/src/services/validation/validation-service.ts b/packages/apidom-ls/src/services/validation/validation-service.ts index d8d073a34f..2c16819e8a 100644 --- a/packages/apidom-ls/src/services/validation/validation-service.ts +++ b/packages/apidom-ls/src/services/validation/validation-service.ts @@ -451,7 +451,7 @@ export class DefaultValidationService implements ValidationService { let processedText; const nameSpace = await findNamespace(text, this.settings?.defaultContentLanguage); // TODO: Turned off validation, because we will implement it in the future. - if (nameSpace.namespace === 'asyncapi' && nameSpace.version === '3.0.0') { + if (nameSpace.namespace === 'asyncapi' && (nameSpace.version === '3.0.0' || nameSpace.version === '3.0.1')) { return []; } let docNs: string = nameSpace.namespace; diff --git a/packages/apidom-parser-adapter-asyncapi-json-3/README.md b/packages/apidom-parser-adapter-asyncapi-json-3/README.md index edcd9b02b5..e01533bdf1 100644 --- a/packages/apidom-parser-adapter-asyncapi-json-3/README.md +++ b/packages/apidom-parser-adapter-asyncapi-json-3/README.md @@ -31,16 +31,14 @@ Defines list of media types that this parser adapter recognizes. [ 'application/vnd.aai.asyncapi;version=3.0.0', 'application/vnd.aai.asyncapi+json;version=3.0.0', - 'application/vnd.aai.asyncapi+yaml;version=3.0.0', 'application/vnd.aai.asyncapi;version=3.0.1', 'application/vnd.aai.asyncapi+json;version=3.0.1', - 'application/vnd.aai.asyncapi+yaml;version=3.0.1', ] ``` ### detect -[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts#L13) is based on a regular expression matching required AsyncApi 3.0.0 specification symbols in JSON format. +[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-asyncapi-json-3/src/adapter.ts#L18) is based on a regular expression matching required AsyncApi 3.0.0 specification symbols in JSON format. ### namespace