diff --git a/README.md b/README.md index 607dab8b40..c42cecaf7b 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ You can now install ApiDOM packages using `npm`: $ npm install @swagger-api/apidom-parser-adapter-json $ npm install @swagger-api/apidom-parser-adapter-openapi-json-3-0 $ npm install @swagger-api/apidom-parser-adapter-openapi-json-3-1 + $ npm install @swagger-api/apidom-parser-adapter-openapi-yaml-3-0 $ npm install @swagger-api/apidom-parser-adapter-openapi-yaml-3-1 $ npm install @swagger-api/apidom-parser-adapter-yaml-1-2 $ npm install @swagger-api/apidom-reference diff --git a/package-lock.json b/package-lock.json index fb68cf14d8..4997513a16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7340,6 +7340,10 @@ "resolved": "packages/apidom-parser-adapter-openapi-json-3-1", "link": true }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { + "resolved": "packages/apidom-parser-adapter-openapi-yaml-3-0", + "link": true + }, "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { "resolved": "packages/apidom-parser-adapter-openapi-yaml-3-1", "link": true @@ -30242,6 +30246,19 @@ "ramda-adjunct": "=3.2.0" } }, + "packages/apidom-parser-adapter-openapi-yaml-3-0": { + "version": "0.42.0", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "=7.18.9", + "@swagger-api/apidom-core": "^0.42.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.42.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.42.0", + "@types/ramda": "=0.28.15", + "ramda": "=0.28.0", + "ramda-adjunct": "=3.2.0" + } + }, "packages/apidom-parser-adapter-openapi-yaml-3-1": { "name": "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1", "version": "0.42.0", @@ -35825,6 +35842,18 @@ "ramda-adjunct": "=3.2.0" } }, + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { + "version": "file:packages/apidom-parser-adapter-openapi-yaml-3-0", + "requires": { + "@babel/runtime-corejs3": "=7.18.9", + "@swagger-api/apidom-core": "^0.42.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.42.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.42.0", + "@types/ramda": "=0.28.15", + "ramda": "=0.28.0", + "ramda-adjunct": "=3.2.0" + } + }, "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { "version": "file:packages/apidom-parser-adapter-openapi-yaml-3-1", "requires": { diff --git a/package.json b/package.json index 02214e0c53..926347e33e 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "clean": "lerna run clean", "test": "lerna run test", "link": "npm link --workspaces", - "unlink": "npm unlink --global @swagger-api/apidom-ast @swagger-api/apidom-core @swagger-api/apidom-json-path @swagger-api/apidom-json-pointer @swagger-api/apidom-parser-adapter-json @swagger-api/apidom-ns-api-design-systems @swagger-api/apidom-ns-asyncapi-2 @swagger-api/apidom-ns-json-schema-draft-4 @swagger-api/apidom-ns-json-schema-draft-6 @swagger-api/apidom-ns-json-schema-draft-7 @swagger-api/apidom-ns-openapi-3-0 @swagger-api/apidom-ns-openapi-3-1 @swagger-api/apidom-parser-adapter-yaml-1-2 @swagger-api/apidom-parser-adapter-asyncapi-yaml-2 @swagger-api/apidom-parser-adapter-openapi-yaml-3-1 @swagger-api/apidom-parser @swagger-api/apidom-parser-adapter-api-design-systems-json @swagger-api/apidom-parser-adapter-api-design-systems-yaml @swagger-api/apidom-parser-adapter-asyncapi-json-2 @swagger-api/apidom-ls @swagger-api/apidom-reference @swagger-api/apidom-parser-adapter-openapi-json-3-0 @swagger-api/apidom-parser-adapter-openapi-json-3-1 @swagger-api/apidom-playground", + "unlink": "npm unlink --global @swagger-api/apidom-ast @swagger-api/apidom-core @swagger-api/apidom-json-path @swagger-api/apidom-json-pointer @swagger-api/apidom-parser-adapter-json @swagger-api/apidom-ns-api-design-systems @swagger-api/apidom-ns-asyncapi-2 @swagger-api/apidom-ns-json-schema-draft-4 @swagger-api/apidom-ns-json-schema-draft-6 @swagger-api/apidom-ns-json-schema-draft-7 @swagger-api/apidom-ns-openapi-3-0 @swagger-api/apidom-ns-openapi-3-1 @swagger-api/apidom-parser-adapter-yaml-1-2 @swagger-api/apidom-parser-adapter-asyncapi-yaml-2 @swagger-api/apidom-parser-adapter-openapi-yaml-3-0 @swagger-api/apidom-parser-adapter-openapi-yaml-3-1 @swagger-api/apidom-parser @swagger-api/apidom-parser-adapter-api-design-systems-json @swagger-api/apidom-parser-adapter-api-design-systems-yaml @swagger-api/apidom-parser-adapter-asyncapi-json-2 @swagger-api/apidom-ls @swagger-api/apidom-reference @swagger-api/apidom-parser-adapter-openapi-json-3-0 @swagger-api/apidom-parser-adapter-openapi-json-3-1 @swagger-api/apidom-playground", "prepare": "husky install" }, "repository": { diff --git a/packages/apidom-parser-adapter-openapi-json-3-0/src/adapter.ts b/packages/apidom-parser-adapter-openapi-json-3-0/src/adapter.ts index f7f39bfbec..4f3a6e8f07 100644 --- a/packages/apidom-parser-adapter-openapi-json-3-0/src/adapter.ts +++ b/packages/apidom-parser-adapter-openapi-json-3-0/src/adapter.ts @@ -6,7 +6,7 @@ import openApiNamespace, { OpenApi3_0Element } from '@swagger-api/apidom-ns-open export { default as mediaTypes } from './media-types'; -export const detectionRegExp = /"openapi"\s*:\s*"(?3\.0\.([0123])(-rc[012])?)"/; +export const detectionRegExp = /"openapi"\s*:\s*"(?3\.0\.([0123])(?:-rc[012])?)"/; export const detect = async (source: string): Promise => detectionRegExp.test(source) && (await detectJSON(source)); diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/.eslintignore b/packages/apidom-parser-adapter-openapi-yaml-3-0/.eslintignore new file mode 100644 index 0000000000..856320e627 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/.eslintignore @@ -0,0 +1,8 @@ +/dist +/es +/cjs +/types +/config +/.nyc_output +/node_modules +/**/*.js diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/.gitignore b/packages/apidom-parser-adapter-openapi-yaml-3-0/.gitignore new file mode 100644 index 0000000000..0cc308a599 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/.gitignore @@ -0,0 +1,6 @@ +/dist +/es +/cjs +/types +/NOTICE +/swagger-api-apidom-parser-adapter-openapi-yaml-3-0-*.tgz diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/.mocharc.json b/packages/apidom-parser-adapter-openapi-yaml-3-0/.mocharc.json new file mode 100644 index 0000000000..e923c24114 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/.mocharc.json @@ -0,0 +1,5 @@ +{ + "recursive": true, + "spec": "test/**/*.ts", + "file": ["test/mocha-bootstrap.cjs"] +} diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/.npmrc b/packages/apidom-parser-adapter-openapi-yaml-3-0/.npmrc new file mode 100644 index 0000000000..4b82d2e7bb --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/.npmrc @@ -0,0 +1,2 @@ +save-prefix="=" +save=false diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/README.md b/packages/apidom-parser-adapter-openapi-yaml-3-0/README.md new file mode 100644 index 0000000000..bd37fb9381 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/README.md @@ -0,0 +1,105 @@ +# @swagger-api/apidom-parser-adapter-openapi-yaml-3-0 + +`@swagger-api/apidom-parser-adapter-openapi-yaml-3-0` is a parser adapter for the [OpenApi 3.0.x specification](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md) in [YAML format](https://yaml.org/spec/1.2/spec.html). +Under the hood this adapter uses [apidom-parser-adapter-yaml-1-2](https://github.com/swagger-api/apidom/tree/main/packages/apidom-parser-adapter-yaml-1-2) +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 [OpenApi 3.0.x Refractors](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-openapi-3-0#refractors). + +## Installation + +`@swagger-api/apidom-parser-adapter-openapi-yaml-3-0` is currently hosted on [GitHub packages registry](https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages). +For installing `@swagger-api/apidom-parser-adapter-openapi-yaml-3-0` from GitHub packages registry, create `.npmrc` file in your current directory and add +the following line to it: + +``` +@swagger-api:registry=https://npm.pkg.github.com +``` + +You can now install the package using `npm`: + +```sh + $ npm install @swagger-api/apidom-parser-adapter-openapi-yaml-3-0 +``` + +## 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.oai.openapi;version=3.0.0', + 'application/vnd.oai.openapi+yaml;version=3.0.0', + 'application/vnd.oai.openapi;version=3.0.0-rc0', + 'application/vnd.oai.openapi+yaml;version=3.0.0-rc0', + 'application/vnd.oai.openapi;version=3.0.0-rc1', + 'application/vnd.oai.openapi+yaml;version=3.0.0-rc1', + 'application/vnd.oai.openapi;version=3.0.0-rc2', + 'application/vnd.oai.openapi+yaml;version=3.0.0-rc2', + 'application/vnd.oai.openapi;version=3.0.1', + 'application/vnd.oai.openapi+yaml;version=3.0.1', + 'application/vnd.oai.openapi;version=3.0.2', + 'application/vnd.oai.openapi+yaml;version=3.0.2', + 'application/vnd.oai.openapi;version=3.0.3', + 'application/vnd.oai.openapi+yaml;version=3.0.3', +] +``` + +### detect + +[Detection](https://github.com/swagger-api/apidom/blob/main/packages/apidom-parser-adapter-openapi-yaml-3-0/src/adapter.ts#L13) is based on a regular expression matching required OpenApi 3.0.x specification symbols in YAML format. + +### namespace + +This adapter exposes an instance of [OpenApi 3.0.x ApiDOM namespace](https://github.com/swagger-api/apidom/tree/main/packages/apidom-ns-openapi-3-0#openapi-30x-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-openapi-3-0/src/refractor/specification.ts) | This specification object drives the YAML AST transformation to OpenAPI 3.0.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-openapi-3-0#refractor-plugins) during refracting phase. + +All unrecognized arbitrary options will be ignored. + +## Usage + +This parser adapter can be used directly or indirectly via [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-openapi-yaml-3-0'; + +// detecting +await detect('openapi: 3.0.3'); // => true +await detect('test'); // => false + +// parsing +const parseResult = await parse('openapi: 3.0.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 openApiYamlAdapter from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-0'; + +const parser = ApiDOMParser(); + +parser.use(openApiYamlAdapter); + +const parseResult = await parser.parse('openapi: 3.0.3', { mediaType: openApiYamlAdapter.mediaTypes.latest('yaml') }); +``` diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/config/rollup/types.dist.js b/packages/apidom-parser-adapter-openapi-yaml-3-0/config/rollup/types.dist.js new file mode 100644 index 0000000000..6ecc45f65c --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/config/rollup/types.dist.js @@ -0,0 +1,11 @@ +import dts from 'rollup-plugin-dts'; + +const config = [ + { + input: './types/adapter.d.ts', + output: [{ file: 'types/dist.d.ts', format: 'es' }], + plugins: [dts()], + }, +]; + +export default config; diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/config/webpack/browser.config.js b/packages/apidom-parser-adapter-openapi-yaml-3-0/config/webpack/browser.config.js new file mode 100644 index 0000000000..c003f21031 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/config/webpack/browser.config.js @@ -0,0 +1,88 @@ +import path from 'node:path'; +import { nonMinimizeTrait, minimizeTrait } from './traits.config.js'; + +const browser = { + mode: 'production', + entry: ['./src/adapter.ts'], + target: 'web', + performance: { + maxEntrypointSize: 712000, + maxAssetSize: 712000, + }, + output: { + path: path.resolve('./dist'), + filename: 'apidom-parser-adapter-openapi-yaml-3-0.browser.js', + libraryTarget: 'umd', + library: 'apidomParserAdapterOpenApiYaml3_0', + }, + 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', + output: { + path: path.resolve('./dist'), + filename: 'apidom-parser-adapter-openapi-yaml-3-0.browser.min.js', + libraryTarget: 'umd', + library: 'apidomParserAdapterOpenApiYaml3_0', + }, + 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-openapi-yaml-3-0/config/webpack/traits.config.js b/packages/apidom-parser-adapter-openapi-yaml-3-0/config/webpack/traits.config.js new file mode 100644 index 0000000000..9043521175 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/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-openapi-yaml-3-0/declaration.tsconfig.json b/packages/apidom-parser-adapter-openapi-yaml-3-0/declaration.tsconfig.json new file mode 100644 index 0000000000..5697fa3e89 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/declaration.tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "test/**/*" + ], + "compilerOptions": { + "declaration": true, + "declarationDir": "types", + "noEmit": false, + "emitDeclarationOnly": true + } +} diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/package.json b/packages/apidom-parser-adapter-openapi-yaml-3-0/package.json new file mode 100644 index 0000000000..ee0a26b664 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/package.json @@ -0,0 +1,56 @@ +{ + "name": "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0", + "version": "0.42.0", + "description": "Parser adapter for parsing YAML documents into OpenAPI 3.0.x namespace.", + "publishConfig": { + "registry": "https://npm.pkg.github.com" + }, + "type": "module", + "unpkg": "./dist/apidom-parser-apdater-openapi-yaml-3-0.browser.min.js", + "main": "./cjs/adapter.cjs", + "exports": { + "types": "./types/dist.d.ts", + "import": "./es/adapter.js", + "require": "./cjs/adapter.cjs" + }, + "types": "./types/dist.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 es --extensions '.ts' --root-mode 'upward'", + "build:cjs": "cross-env BABEL_ENV=cjs babel src --out-dir cjs --extensions '.ts' --out-file-extension '.cjs' --root-mode 'upward'", + "build:umd:browser": "cross-env BABEL_ENV=browser BROWSERSLIST_ENV=production webpack --config config/webpack/browser.config.js --progress", + "lint": "eslint ./", + "lint:fix": "eslint ./ --fix", + "clean": "rimraf ./es ./cjs ./dist ./types", + "typescript:check-types": "tsc --noEmit", + "typescript:declaration": "tsc -p declaration.tsconfig.json && rollup -c config/rollup/types.dist.js", + "test": "cross-env NODE_ENV=test BABEL_ENV=cjs mocha", + "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.18.9", + "@swagger-api/apidom-core": "^0.42.0", + "@swagger-api/apidom-ns-openapi-3-0": "^0.42.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^0.42.0", + "@types/ramda": "=0.28.15", + "ramda": "=0.28.0", + "ramda-adjunct": "=3.2.0" + }, + "files": [ + "cjs/", + "dist/", + "es/", + "types/dist.d.ts", + "LICENSES", + "NOTICE", + "README.md", + "CHANGELOG.md" + ] +} diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/src/adapter.ts b/packages/apidom-parser-adapter-openapi-yaml-3-0/src/adapter.ts new file mode 100644 index 0000000000..2caa1cf2fd --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/src/adapter.ts @@ -0,0 +1,36 @@ +import { propOr, omit } from 'ramda'; +import { isNotUndefined } from 'ramda-adjunct'; +import { ParseResultElement, createNamespace } from '@swagger-api/apidom-core'; +import { + parse as parseYAML, + detect as detectYAML, +} from '@swagger-api/apidom-parser-adapter-yaml-1-2'; +import openApiNamespace, { OpenApi3_0Element } from '@swagger-api/apidom-ns-openapi-3-0'; + +export { default as mediaTypes } from './media-types'; + +export const detectionRegExp = + /(?^(["']?)openapi\2\s*:\s*(["']?)(?3\.0\.[0123](?:-rc[012])?)\3(?:\s+|$))|(?"openapi"\s*:\s*"(?3\.0\.[0123](?:-rc[012])?)")/m; + +export const detect = async (source: string): Promise => + detectionRegExp.test(source) && (await detectYAML(source)); + +export const parse = async ( + source: string, + options: Record = {}, +): Promise => { + const refractorOpts: Record = propOr({}, 'refractorOpts', options); + const parserOpts = omit(['refractorOpts'], options); + const parseResultElement = await parseYAML(source, parserOpts); + const { result } = parseResultElement; + + if (isNotUndefined(result)) { + const openApiElement = OpenApi3_0Element.refract(result, refractorOpts); + openApiElement.classes.push('result'); + parseResultElement.replaceResult(openApiElement); + } + + return parseResultElement; +}; + +export const namespace = createNamespace(openApiNamespace); diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/src/media-types.ts b/packages/apidom-parser-adapter-openapi-yaml-3-0/src/media-types.ts new file mode 100644 index 0000000000..fbdf87f1b4 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/src/media-types.ts @@ -0,0 +1,8 @@ +import { mediaTypes, OpenAPIMediaTypes } from '@swagger-api/apidom-ns-openapi-3-0'; + +const yamlMediaTypes = new OpenAPIMediaTypes( + ...mediaTypes.filterByFormat('generic'), + ...mediaTypes.filterByFormat('yaml'), +); + +export default yamlMediaTypes; diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/test/.eslintrc b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/.eslintrc new file mode 100644 index 0000000000..85759a803c --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/.eslintrc @@ -0,0 +1,38 @@ +{ + "env": { + "mocha": true + }, + "globals": { + "document": true + }, + "plugins": [ + "mocha" + ], + "rules": { + "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 + }, + "overrides": [{ + "files": ["mocha-bootstrap.cjs"], + "parserOptions": { + "sourceType": "script" + }, + "rules": { + "@typescript-eslint/no-var-requires": 0 + } + }] +} diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/test/__snapshots__/adapter.ts.snap b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/__snapshots__/adapter.ts.snap new file mode 100644 index 0000000000..2a93fe8430 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/__snapshots__/adapter.ts.snap @@ -0,0 +1,746 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`adapter should parse 1`] = ` +(ParseResultElement + (OpenApi3_0Element + (MemberElement + (StringElement) + (OpenapiElement)) + (MemberElement + (StringElement) + (InfoElement + (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) + (StringElement)))) + (MemberElement + (StringElement) + (ArrayElement + (ServerElement + (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) + (PathsElement + (MemberElement + (StringElement) + (PathItemElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (OperationElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ExternalDocumentationElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ArrayElement + (ParameterElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement))) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))))) + (MemberElement + (StringElement) + (RequestBodyElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ExampleElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (EncodingElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (HeaderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)))))))))) + (MemberElement + (StringElement) + (BooleanElement)))) + (MemberElement + (StringElement) + (ResponsesElement + (MemberElement + (StringElement) + (ResponseElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement)))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (LinkElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ServerElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (CallbackElement + (MemberElement + (StringElement) + (PathItemElement + (MemberElement + (StringElement) + (OperationElement + (MemberElement + (StringElement) + (RequestBodyElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))))))))) + (MemberElement + (StringElement) + (ResponsesElement + (MemberElement + (StringElement) + (ResponseElement + (MemberElement + (StringElement) + (StringElement)))))))))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ArrayElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ArrayElement + (ServerElement + (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) + (OperationElement + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ResponsesElement + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (OperationElement)) + (MemberElement + (StringElement) + (ArrayElement + (ServerElement + (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 + (ParameterElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement))) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement))))))) + (MemberElement + (StringElement) + (PathItemElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ComponentsElement + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ResponseElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (HeaderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))))))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ParameterElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement)))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (ExampleElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (RequestBodyElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (BooleanElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement)))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (HeaderElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (SecuritySchemeElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (OAuthFlowsElement + (MemberElement + (StringElement) + (OAuthFlowElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (LinkElement + (MemberElement + (StringElement) + (StringElement)))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (CallbackElement + (MemberElement + (StringElement) + (PathItemElement + (MemberElement + (StringElement) + (OperationElement + (MemberElement + (StringElement) + (RequestBodyElement + (MemberElement + (StringElement) + (StringElement)) + (MemberElement + (StringElement) + (ObjectElement + (MemberElement + (StringElement) + (MediaTypeElement + (MemberElement + (StringElement) + (SchemaElement + (MemberElement + (StringElement) + (StringElement)))))))))) + (MemberElement + (StringElement) + (ResponsesElement + (MemberElement + (StringElement) + (ResponseElement + (MemberElement + (StringElement) + (StringElement)))))))))))) + (MemberElement + (StringElement) + (ReferenceElement + (MemberElement + (StringElement) + (StringElement)))))))) + (MemberElement + (StringElement) + (ArrayElement + (SecurityRequirementElement + (MemberElement + (StringElement) + (ArrayElement))))) + (MemberElement + (StringElement) + (ArrayElement + (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)))))) +`; diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/test/adapter.ts b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/adapter.ts new file mode 100644 index 0000000000..0a2deb14cf --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/adapter.ts @@ -0,0 +1,100 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { assert, expect } from 'chai'; +import dedent from 'dedent'; +import { isParseResultElement, SourceMapElement, sexprs } from '@swagger-api/apidom-core'; +import { isOpenApi3_0Element } from '@swagger-api/apidom-ns-openapi-3-0'; + +import * as adapter from '../src/adapter'; + +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 YAML 1.2 format', function () { + specify('should detect proper media type', async function () { + assert.isTrue(await adapter.detect(yamlSpec)); + }); + + specify('should not detect minor version bump', async function () { + assert.isFalse(await adapter.detect('openapi: "3.1.0"')); + }); + + specify('should not detect patch version bump', async function () { + assert.isFalse(await adapter.detect('openapi: "3.0.4"')); + }); + + specify('should not detect minor and patch version bump', async function () { + assert.isFalse(await adapter.detect('openapi: "3.1.4"')); + }); + }); + + context('given definition in JSON format', function () { + specify('should detect proper media type', async function () { + assert.isTrue(await adapter.detect(jsonSpec)); + }); + }); + + context('given definition of unknown type', function () { + specify('should detect proper media type', async function () { + assert.isFalse(await adapter.detect('"asyncapi": "2.4.0"')); + }); + }); + + it('should parse', async function () { + const parseResult = await adapter.parse(yamlSpec, { sourceMap: true }); + + assert.isTrue(isParseResultElement(parseResult)); + assert.isTrue(isOpenApi3_0Element(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 yaml file', function () { + specify('should return empty parser result', async function () { + const parseResult = await adapter.parse(' %YAML x ', { sourceMap: true }); + + assert.isTrue(parseResult.isEmpty); + }); + }); + + context('given YAML with empty node', function () { + specify('should generate source maps', async function () { + const yamlSource = dedent` + mapping: + sub-mapping: + `; + + const { result } = await adapter.parse(yamlSource, { sourceMap: true }); + // @ts-ignore + const subMappingValue = result.get('mapping').get('sub-mapping'); + + assert.instanceOf(subMappingValue.meta.get('sourceMap'), SourceMapElement); + }); + }); + + context('detectionRegExp', function () { + specify('should reject invalid version ranges', function () { + assert.isFalse(adapter.detectionRegExp.test('openapi: 3.01.0')); + assert.isFalse(adapter.detectionRegExp.test('openapi: 3.0.01')); + assert.isFalse(adapter.detectionRegExp.test('openapi: 3.1.0')); + assert.isFalse(adapter.detectionRegExp.test('openapi: 3.1.1')); + assert.isFalse(adapter.detectionRegExp.test('openapi: 3.0.15')); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/test/fixtures/sample-api.json b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/fixtures/sample-api.json new file mode 100644 index 0000000000..579d14dad9 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/fixtures/sample-api.json @@ -0,0 +1,369 @@ +{ + "openapi": "3.0.3", + "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-openapi-yaml-3-0/test/fixtures/sample-api.yaml b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/fixtures/sample-api.yaml new file mode 100644 index 0000000000..33a5243301 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/fixtures/sample-api.yaml @@ -0,0 +1,256 @@ +--- +openapi: 3.0.3 +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-openapi-yaml-3-0/test/media-types.ts b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/media-types.ts new file mode 100644 index 0000000000..6a3b3a2df7 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/media-types.ts @@ -0,0 +1,80 @@ +import { assert } from 'chai'; +import ApiDOMParser from '@swagger-api/apidom-parser'; + +import * as openApiYAMLAdapter from '../src/adapter'; + +describe('given adapter is used in parser', function () { + const parser = ApiDOMParser().use(openApiYAMLAdapter); + + context('given OpenAPI 3.0.3 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.3"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.3'); + }); + }); + + context('given OpenAPI 3.0.2 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.2"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.2'); + }); + }); + + context('given OpenAPI 3.0.1 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.1"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.1'); + }); + }); + + context('given OpenAPI 3.0.0 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.0"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.0'); + }); + }); + + context('given OpenAPI 3.0.0-rc0 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.0-rc0"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.0-rc0'); + }); + }); + + context('given OpenAPI 3.0.0-rc1 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.0-rc1"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.0-rc1'); + }); + }); + + context('given OpenAPI 3.0.0-rc2 definition in YAML format', function () { + specify('should find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.0-rc2"'); + + assert.strictEqual(mediaType, 'application/vnd.oai.openapi+yaml;version=3.0.0-rc2'); + }); + }); + + context('given OpenAPI 3.0.3-rc3 definition in YAML format', function () { + specify('should not find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.0.3-rc3"'); + + assert.strictEqual(mediaType, 'application/octet-stream'); + }); + }); + + context('given OpenAPI 3.1.0 definition in YAML format', function () { + specify('should not find appropriate media type', async function () { + const mediaType = await parser.findMediaType('openapi: "3.1.0"'); + + assert.strictEqual(mediaType, 'application/octet-stream'); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/test/mocha-bootstrap.cjs b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/mocha-bootstrap.cjs new file mode 100644 index 0000000000..bc8ce3c8fc --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/test/mocha-bootstrap.cjs @@ -0,0 +1,15 @@ +/* eslint-disable */ +require('@babel/register')({ extensions: ['.js', '.ts'], rootMode: 'upward' }); + +/** + * Configure snapshot testing. + */ +const chai = require('chai'); +const { jestSnapshotPlugin, addSerializer } = require('mocha-chai-jest-snapshot'); + +const jestApiDOMSerializer = require('../../../scripts/jest-serializer-apidom.cjs'); +const jestStringSerializer = require('../../../scripts/jest-serializer-string.cjs'); + +chai.use(jestSnapshotPlugin()); +addSerializer(jestApiDOMSerializer); +addSerializer(jestStringSerializer); diff --git a/packages/apidom-parser-adapter-openapi-yaml-3-0/tsconfig.json b/packages/apidom-parser-adapter-openapi-yaml-3-0/tsconfig.json new file mode 100644 index 0000000000..4081635a03 --- /dev/null +++ b/packages/apidom-parser-adapter-openapi-yaml-3-0/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "include": [ + "src/**/*", + "test/**/*" + ] +}