diff --git a/packages/apidom-ns-openapi-3-1/README.md b/packages/apidom-ns-openapi-3-1/README.md index c9ab8bdef..abc6fca7e 100644 --- a/packages/apidom-ns-openapi-3-1/README.md +++ b/packages/apidom-ns-openapi-3-1/README.md @@ -139,6 +139,7 @@ InfoElement.refract(objectElement, { plugins: [plugin] }); // => InfoElement({ t You can define as many plugins as needed to enhance the resulting namespaced ApiDOM structure. If multiple plugins with the same visitor method are defined, they run in parallel (just like in Babel). +All the plugins available in `@swagger-api/apidom-ns-openapi-3-1` are idempotent and the normalization state is stored in the root `OpenApi3_1Element` in ``. `` can be customized in the plugin configuration (default: `x-normalized`). #### Replace Empty Element plugin @@ -431,6 +432,375 @@ toValue(openApiElement); // } // } ``` +#### Normalize Parameter examples plugin + +`parameter.examples` and `parameter.example` override `parameter.schema.examples` and `parameter.schema.example` fields. The plugin overrides only the existing `parameter.schema.examples` and `parameter.schema.example`. + +- Does not apply to parameters defined under `components`. +- `parameter.examples` has precedence over deprecated `parameter.example`. + +```js +import { toValue } from '@swagger-api/apidom-core'; +import { OpenApi3_1Element, refractorPluginNormalizeParameterExamples } from '@swagger-api/apidom-ns-openapi-3-1'; +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; + +const yamlDefinition = ` +openapi: 3.1.0 +paths: + /: + get: + parameters: + - in: query + name: idempotent + schema: + type: number + examples: [1] + examples: + example1: + value: 2` + +const apiDOM = await parse(yamlDefinition); + +//default +const openApiElement = OpenApi3_1Element.refract(apiDOM.result, { + plugins: [refractorPluginNormalizeParameterExamples()], +}); +toValue(openApiElement); +// => +// { +// openapi: '3.1.0', +// paths: { +// '/': { +// get: { +// parameters: [ +// { +// in: 'query', +// name: 'idempotent', +// schema: { type: 'number', examples: [ 2 ] }, +// examples: { example1: { value: 2 } } +// } +// ] +// } +// } +// }, +// 'x-normalized': { 'parameter-examples': [ '/paths/~1/get/parameters/0' ] } +// } + +// custom storage field name +const openApiElementWithCustomField = OpenApi3_1Element.refract(apiDOM.result, { + plugins: [refractorPluginNormalizeParameterExamples({ storageField: '$$my-normalized' })], +}); +toValue(openApiElementWithCustomField); +// => +// { +// openapi: '3.1.0', +// paths: { +// '/': { +// get: { +// parameters: [ +// { +// in: 'query', +// name: 'idempotent', +// schema: { type: 'number', examples: [2] }, +// examples: { example1: { value: 2 } } +// } +// ] +// } +// } +// }, +// '$$my-normalized': { 'parameter-examples': [ '/paths/~1/get/parameters/0' ] } +// } +``` + +#### Normalize Header examples plugin + +`Header.examples` and `header.example` override `header.schema.examples` and `header.schema.example` fields. The plugin overrides only the existing `header.schema.examples` and `header.schema.example`. + +- Does not apply to headers defined under `components`. +- `header.examples` has precedence over deprecated `header.example`. + +```js +import { toValue } from '@swagger-api/apidom-core'; +import { OpenApi3_1Element, refractorPluginNormalizeHeaderExamples } from '@swagger-api/apidom-ns-openapi-3-1'; +import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2'; + +const yamlDefinition = ` +openapi: 3.1.0 +paths: + /: + get: + responses: + "200": + headers: + content-type: + schema: + type: number + example: 1 + examples: + example1: + value: 2 +` +const apiDOM = await parse(yamlDefinition); + +// default +const openApiElement = OpenApi3_1Element.refract(apiDOM.result, { + plugins: [refractorPluginNormalizeHeaderExamples()], +}); +toValue(openApiElement); +// => +// { +// openapi: '3.1.0', +// paths: { +// '/': { +// get: { +// responses: { +// '200': { +// headers: { +// 'content-type': { +// schema: { type: 'number', example: 2 }, +// examples: { example1: { value: 2 } } +// } +// } +// } +// } +// } +// } +// }, +// 'x-normalized': { +// 'header-examples': [ '/paths/~1/get/responses/200/headers/content-type' ] +// } +// } + +// custom storage field name + +const openApiElementWithCustomField = OpenApi3_1Element.refract(apiDOM.result, { + plugins: [refractorPluginNormalizeHeaderExamples({ storageField: '$$normalized' })], +}); +toValue(openApiElementWithCustomField); +// => +// { +// openapi: '3.1.0', +// paths: { +// '/': { +// get: { +// responses: { +// '200': { +// headers: { +// 'content-type': { +// schema: { type: 'number', example: 2 }, +// examples: { example1: { value: 2 } } +// } +// } +// } +// } +// } +// } +// }, +// '$$normalized': { +// 'header-examples': [ '/paths/~1/get/responses/200/headers/content-type' ] +// } +// } +``` + +#### Normalize Discriminator mapping plugin +This plugin normalizes the `discriminator.mapping` field in a Schema Object by: + +- Converting mapping values into inline Schema Objects when possible. +- Adding missing mapping entries based on the schema's `oneOf`, `anyOf`, or a prepared `allOf` mapping. The `allOf` mapping + is created based on the schemas defined in `components.schemas` during dereferencing. + +The `discriminator.mapping` field is not modified by the plugin. + +This plugin is intended to run on dereferenced OpenAPI 3.1 documents. During dereferencing Schema Objects are annotated with meta properties and the `allOf` mapping is created for Schema Objects defined in `components.schemas`. + +```json + +// fixture-example.json +{ + "openapi": "3.1.0", + "components": { + "schemas": { + "MyResponse": { + "type": "object", + "oneOf": [ + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ], + "discriminator": { + "propertyName": "petType" + } + }, + "Pet": { + "type": "object", + "properties": { + "petType": { + "type": "string" + } + } + }, + "Cat": { + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "meows": { + "type": "boolean" + } + } + } + ] + }, + "Dog": { + "allOf": [ + { + "$ref": "#/components/schemas/Pet" + }, + { + "type": "object", + "properties": { + "barks": { + "type": "boolean" + } + } + } + ] + } + } + } +} +``` + +```js +import { toValue, dispatchRefractorPlugins } from '@swagger-api/apidom-core'; +import { dereference } from '@swagger-api/apidom-reference'; +import FileResolver from '@swagger-api/apidom-reference/resolve/resolvers/file'; +import { + createToolbox, + refractorPluginNormalizeDiscriminatorMapping, + keyMap, + getNodeType, + mediaTypes, +} from '@swagger-api/apidom-ns-openapi-3-1'; +const uri = 'path/to/fixture-example.json'; // the arbitrary file name shown above + +// 1) dereference the document to annotate schemas with required metadata +const dereferenced = await dereference(uri, { + parse: { mediaType: mediaTypes.latest('json') }, + resolve: { + baseURI: uri, + resolvers: [ new FileResolver({ fileAllowList: [/\.json$/] }) ], + }, + dereference: { strategyOpts: { 'openapi-3-1': { dereferenceDiscriminatorMapping: true } } }, +}); + +// 2) dispatch the plugin and pass the same baseURI +const normalized = dispatchRefractorPlugins( + dereferenced.result, + [refractorPluginNormalizeDiscriminatorMapping({ baseURI: uri })], + { toolboxCreator: createToolbox, visitorOptions: { keyMap, nodeTypeGetter: getNodeType } } +); +toValue(normalized) + +// => +// { +// openapi: '3.1.0', +// components: { +// schemas: { +// MyResponse: { +// type: 'object', +// oneOf: [ +// { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { meows: { type: 'boolean' } } +// } +// ] +// }, +// { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { barks: { type: 'boolean' } } +// } +// ] +// } +// ], +// discriminator: { +// propertyName: 'petType', +// 'x-normalized-mapping': { +// Cat: { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { meows: { type: 'boolean' } } +// } +// ] +// }, +// Dog: { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { barks: { type: 'boolean' } } +// } +// ] +// } +// } +// } +// }, +// Pet: { type: 'object', properties: { petType: { type: 'string' } } }, +// Cat: { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { meows: { type: 'boolean' } } +// } +// ] +// }, +// Dog: { +// allOf: [ +// { +// type: 'object', +// properties: { petType: { type: 'string' } } +// }, +// { +// type: 'object', +// properties: { barks: { type: 'boolean' } } +// } +// ] +// } +// } +// }, +// 'x-normalized': { 'discriminator-mapping': [ '/components/schemas/MyResponse' ] } +// } +``` ## Implementation progress diff --git a/packages/apidom-ns-openapi-3-1/test/refractor/plugins/normalize-parameter-examples/storage-field.ts b/packages/apidom-ns-openapi-3-1/test/refractor/plugins/normalize-parameter-examples/storage-field.ts index 8603f1e9c..77e640185 100644 --- a/packages/apidom-ns-openapi-3-1/test/refractor/plugins/normalize-parameter-examples/storage-field.ts +++ b/packages/apidom-ns-openapi-3-1/test/refractor/plugins/normalize-parameter-examples/storage-field.ts @@ -25,7 +25,7 @@ describe('refractor', function () { example: 1 examples: example1: - value: 2 + value: 2 `; const apiDOM = await parse(yamlDefinition); const openApiElement = OpenApi3_1Element.refract(apiDOM.result, {