Skip to content

Commit 023f279

Browse files
authored
docs(ns-openapi-3-1): add missing refractor plugins documentation (#5027)
1 parent b728c58 commit 023f279

File tree

2 files changed

+371
-1
lines changed

2 files changed

+371
-1
lines changed

packages/apidom-ns-openapi-3-1/README.md

Lines changed: 370 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ InfoElement.refract(objectElement, { plugins: [plugin] }); // => InfoElement({ t
139139

140140
You can define as many plugins as needed to enhance the resulting namespaced ApiDOM structure.
141141
If multiple plugins with the same visitor method are defined, they run in parallel (just like in Babel).
142+
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 `<storageField>`. `<storageField>` can be customized in the plugin configuration (default: `x-normalized`).
142143

143144
#### Replace Empty Element plugin
144145

@@ -431,6 +432,375 @@ toValue(openApiElement);
431432
// }
432433
// }
433434
```
435+
#### Normalize Parameter examples plugin
436+
437+
`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`.
438+
439+
- Does not apply to parameters defined under `components`.
440+
- `parameter.examples` has precedence over deprecated `parameter.example`.
441+
442+
```js
443+
import { toValue } from '@swagger-api/apidom-core';
444+
import { OpenApi3_1Element, refractorPluginNormalizeParameterExamples } from '@swagger-api/apidom-ns-openapi-3-1';
445+
import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2';
446+
447+
const yamlDefinition = `
448+
openapi: 3.1.0
449+
paths:
450+
/:
451+
get:
452+
parameters:
453+
- in: query
454+
name: idempotent
455+
schema:
456+
type: number
457+
examples: [1]
458+
examples:
459+
example1:
460+
value: 2`
461+
462+
const apiDOM = await parse(yamlDefinition);
463+
464+
//default
465+
const openApiElement = OpenApi3_1Element.refract(apiDOM.result, {
466+
plugins: [refractorPluginNormalizeParameterExamples()],
467+
});
468+
toValue(openApiElement);
469+
// =>
470+
// {
471+
// openapi: '3.1.0',
472+
// paths: {
473+
// '/': {
474+
// get: {
475+
// parameters: [
476+
// {
477+
// in: 'query',
478+
// name: 'idempotent',
479+
// schema: { type: 'number', examples: [ 2 ] },
480+
// examples: { example1: { value: 2 } }
481+
// }
482+
// ]
483+
// }
484+
// }
485+
// },
486+
// 'x-normalized': { 'parameter-examples': [ '/paths/~1/get/parameters/0' ] }
487+
// }
488+
489+
// custom storage field name
490+
const openApiElementWithCustomField = OpenApi3_1Element.refract(apiDOM.result, {
491+
plugins: [refractorPluginNormalizeParameterExamples({ storageField: '$$my-normalized' })],
492+
});
493+
toValue(openApiElementWithCustomField);
494+
// =>
495+
// {
496+
// openapi: '3.1.0',
497+
// paths: {
498+
// '/': {
499+
// get: {
500+
// parameters: [
501+
// {
502+
// in: 'query',
503+
// name: 'idempotent',
504+
// schema: { type: 'number', examples: [2] },
505+
// examples: { example1: { value: 2 } }
506+
// }
507+
// ]
508+
// }
509+
// }
510+
// },
511+
// '$$my-normalized': { 'parameter-examples': [ '/paths/~1/get/parameters/0' ] }
512+
// }
513+
```
514+
515+
#### Normalize Header examples plugin
516+
517+
`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`.
518+
519+
- Does not apply to headers defined under `components`.
520+
- `header.examples` has precedence over deprecated `header.example`.
521+
522+
```js
523+
import { toValue } from '@swagger-api/apidom-core';
524+
import { OpenApi3_1Element, refractorPluginNormalizeHeaderExamples } from '@swagger-api/apidom-ns-openapi-3-1';
525+
import { parse } from '@swagger-api/apidom-parser-adapter-yaml-1-2';
526+
527+
const yamlDefinition = `
528+
openapi: 3.1.0
529+
paths:
530+
/:
531+
get:
532+
responses:
533+
"200":
534+
headers:
535+
content-type:
536+
schema:
537+
type: number
538+
example: 1
539+
examples:
540+
example1:
541+
value: 2
542+
`
543+
const apiDOM = await parse(yamlDefinition);
544+
545+
// default
546+
const openApiElement = OpenApi3_1Element.refract(apiDOM.result, {
547+
plugins: [refractorPluginNormalizeHeaderExamples()],
548+
});
549+
toValue(openApiElement);
550+
// =>
551+
// {
552+
// openapi: '3.1.0',
553+
// paths: {
554+
// '/': {
555+
// get: {
556+
// responses: {
557+
// '200': {
558+
// headers: {
559+
// 'content-type': {
560+
// schema: { type: 'number', example: 2 },
561+
// examples: { example1: { value: 2 } }
562+
// }
563+
// }
564+
// }
565+
// }
566+
// }
567+
// }
568+
// },
569+
// 'x-normalized': {
570+
// 'header-examples': [ '/paths/~1/get/responses/200/headers/content-type' ]
571+
// }
572+
// }
573+
574+
// custom storage field name
575+
576+
const openApiElementWithCustomField = OpenApi3_1Element.refract(apiDOM.result, {
577+
plugins: [refractorPluginNormalizeHeaderExamples({ storageField: '$$normalized' })],
578+
});
579+
toValue(openApiElementWithCustomField);
580+
// =>
581+
// {
582+
// openapi: '3.1.0',
583+
// paths: {
584+
// '/': {
585+
// get: {
586+
// responses: {
587+
// '200': {
588+
// headers: {
589+
// 'content-type': {
590+
// schema: { type: 'number', example: 2 },
591+
// examples: { example1: { value: 2 } }
592+
// }
593+
// }
594+
// }
595+
// }
596+
// }
597+
// }
598+
// },
599+
// '$$normalized': {
600+
// 'header-examples': [ '/paths/~1/get/responses/200/headers/content-type' ]
601+
// }
602+
// }
603+
```
604+
605+
#### Normalize Discriminator mapping plugin
606+
This plugin normalizes the `discriminator.mapping` field in a Schema Object by:
607+
608+
- Converting mapping values into inline Schema Objects when possible.
609+
- Adding missing mapping entries based on the schema's `oneOf`, `anyOf`, or a prepared `allOf` mapping. The `allOf` mapping
610+
is created based on the schemas defined in `components.schemas` during dereferencing.
611+
612+
The `discriminator.mapping` field is not modified by the plugin.
613+
614+
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`.
615+
616+
```json
617+
618+
// fixture-example.json
619+
{
620+
"openapi": "3.1.0",
621+
"components": {
622+
"schemas": {
623+
"MyResponse": {
624+
"type": "object",
625+
"oneOf": [
626+
{
627+
"$ref": "#/components/schemas/Cat"
628+
},
629+
{
630+
"$ref": "#/components/schemas/Dog"
631+
}
632+
],
633+
"discriminator": {
634+
"propertyName": "petType"
635+
}
636+
},
637+
"Pet": {
638+
"type": "object",
639+
"properties": {
640+
"petType": {
641+
"type": "string"
642+
}
643+
}
644+
},
645+
"Cat": {
646+
"allOf": [
647+
{
648+
"$ref": "#/components/schemas/Pet"
649+
},
650+
{
651+
"type": "object",
652+
"properties": {
653+
"meows": {
654+
"type": "boolean"
655+
}
656+
}
657+
}
658+
]
659+
},
660+
"Dog": {
661+
"allOf": [
662+
{
663+
"$ref": "#/components/schemas/Pet"
664+
},
665+
{
666+
"type": "object",
667+
"properties": {
668+
"barks": {
669+
"type": "boolean"
670+
}
671+
}
672+
}
673+
]
674+
}
675+
}
676+
}
677+
}
678+
```
679+
680+
```js
681+
import { toValue, dispatchRefractorPlugins } from '@swagger-api/apidom-core';
682+
import { dereference } from '@swagger-api/apidom-reference';
683+
import FileResolver from '@swagger-api/apidom-reference/resolve/resolvers/file';
684+
import {
685+
createToolbox,
686+
refractorPluginNormalizeDiscriminatorMapping,
687+
keyMap,
688+
getNodeType,
689+
mediaTypes,
690+
} from '@swagger-api/apidom-ns-openapi-3-1';
691+
const uri = 'path/to/fixture-example.json'; // the arbitrary file name shown above
692+
693+
// 1) dereference the document to annotate schemas with required metadata
694+
const dereferenced = await dereference(uri, {
695+
parse: { mediaType: mediaTypes.latest('json') },
696+
resolve: {
697+
baseURI: uri,
698+
resolvers: [ new FileResolver({ fileAllowList: [/\.json$/] }) ],
699+
},
700+
dereference: { strategyOpts: { 'openapi-3-1': { dereferenceDiscriminatorMapping: true } } },
701+
});
702+
703+
// 2) dispatch the plugin and pass the same baseURI
704+
const normalized = dispatchRefractorPlugins(
705+
dereferenced.result,
706+
[refractorPluginNormalizeDiscriminatorMapping({ baseURI: uri })],
707+
{ toolboxCreator: createToolbox, visitorOptions: { keyMap, nodeTypeGetter: getNodeType } }
708+
);
709+
toValue(normalized)
710+
711+
// =>
712+
// {
713+
// openapi: '3.1.0',
714+
// components: {
715+
// schemas: {
716+
// MyResponse: {
717+
// type: 'object',
718+
// oneOf: [
719+
// {
720+
// allOf: [
721+
// {
722+
// type: 'object',
723+
// properties: { petType: { type: 'string' } }
724+
// },
725+
// {
726+
// type: 'object',
727+
// properties: { meows: { type: 'boolean' } }
728+
// }
729+
// ]
730+
// },
731+
// {
732+
// allOf: [
733+
// {
734+
// type: 'object',
735+
// properties: { petType: { type: 'string' } }
736+
// },
737+
// {
738+
// type: 'object',
739+
// properties: { barks: { type: 'boolean' } }
740+
// }
741+
// ]
742+
// }
743+
// ],
744+
// discriminator: {
745+
// propertyName: 'petType',
746+
// 'x-normalized-mapping': {
747+
// Cat: {
748+
// allOf: [
749+
// {
750+
// type: 'object',
751+
// properties: { petType: { type: 'string' } }
752+
// },
753+
// {
754+
// type: 'object',
755+
// properties: { meows: { type: 'boolean' } }
756+
// }
757+
// ]
758+
// },
759+
// Dog: {
760+
// allOf: [
761+
// {
762+
// type: 'object',
763+
// properties: { petType: { type: 'string' } }
764+
// },
765+
// {
766+
// type: 'object',
767+
// properties: { barks: { type: 'boolean' } }
768+
// }
769+
// ]
770+
// }
771+
// }
772+
// }
773+
// },
774+
// Pet: { type: 'object', properties: { petType: { type: 'string' } } },
775+
// Cat: {
776+
// allOf: [
777+
// {
778+
// type: 'object',
779+
// properties: { petType: { type: 'string' } }
780+
// },
781+
// {
782+
// type: 'object',
783+
// properties: { meows: { type: 'boolean' } }
784+
// }
785+
// ]
786+
// },
787+
// Dog: {
788+
// allOf: [
789+
// {
790+
// type: 'object',
791+
// properties: { petType: { type: 'string' } }
792+
// },
793+
// {
794+
// type: 'object',
795+
// properties: { barks: { type: 'boolean' } }
796+
// }
797+
// ]
798+
// }
799+
// }
800+
// },
801+
// 'x-normalized': { 'discriminator-mapping': [ '/components/schemas/MyResponse' ] }
802+
// }
803+
```
434804

435805
## Implementation progress
436806

packages/apidom-ns-openapi-3-1/test/refractor/plugins/normalize-parameter-examples/storage-field.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('refractor', function () {
2525
example: 1
2626
examples:
2727
example1:
28-
value: 2
28+
value: 2
2929
`;
3030
const apiDOM = await parse(yamlDefinition);
3131
const openApiElement = OpenApi3_1Element.refract(apiDOM.result, {

0 commit comments

Comments
 (0)