diff --git a/packages/apidom-converter/src/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type.ts b/packages/apidom-converter/src/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type.ts index 8d4ee74c3..0088d4580 100644 --- a/packages/apidom-converter/src/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type.ts +++ b/packages/apidom-converter/src/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type.ts @@ -5,8 +5,18 @@ import { SecuritySchemeElement, isSecuritySchemeElement, isComponentsElement, + isReferenceElement, + mediaTypes, } from '@swagger-api/apidom-ns-openapi-3-1'; -import { AnnotationElement, toValue, isObjectElement, Element } from '@swagger-api/apidom-core'; +import { + Element, + ParseResultElement, + AnnotationElement, + isObjectElement, + toValue, + cloneDeep, +} from '@swagger-api/apidom-core'; +import { dereferenceApiDOM, ReferenceSet, Reference, url } from '@swagger-api/apidom-reference'; import type { Toolbox } from '../toolbox'; @@ -17,6 +27,9 @@ type SecuritySchemeTypePluginOptions = { const securitySchemeTypeRefractorPlugin = ({ annotations }: SecuritySchemeTypePluginOptions) => (toolbox: Toolbox) => { + let parseResultElement: ParseResultElement | undefined; + const isRemovableSecuritySchemeElement = (value: unknown): value is SecuritySchemeElement => + isSecuritySchemeElement(value) && toValue(value.type) === 'mutualTLS'; const removedSecuritySchemes: SecuritySchemeElement[] = []; const createAnnotation = (element: T) => toolbox.createAnnotation.fromElement( @@ -28,6 +41,9 @@ const securitySchemeTypeRefractorPlugin = return { visitor: { + ParseResultElement(element: ParseResultElement) { + parseResultElement = element; + }, OpenApi3_1Element(element: OpenApi3_1Element) { if (!isComponentsElement(element.components)) return undefined; if (!isObjectElement(element.components.securitySchemes)) return undefined; @@ -40,11 +56,43 @@ const securitySchemeTypeRefractorPlugin = return undefined; }, - ComponentsElement(element: ComponentsElement) { + async ComponentsElement(element: ComponentsElement) { if (!isObjectElement(element.securitySchemes)) return undefined; + /** + * Removing Reference Objects pointing to removable Security Scheme Objects. + * We need to remove Reference Objects first as they might be pointing + * to Security Scheme Objects that are going to be removed. + */ + const baseURI = url.cwd(); + const rootReference = Reference({ uri: baseURI, value: cloneDeep(parseResultElement!) }); + for (const memberElement of element.securitySchemes) { + if (!isReferenceElement(memberElement.value)) continue; // eslint-disable-line no-continue + + const { value: referenceElement } = memberElement; + const reference = Reference({ + uri: `${baseURI}#reference`, + value: new ParseResultElement([referenceElement]), + }); + const refSet = ReferenceSet({ refs: [reference, rootReference] }); + // eslint-disable-next-line no-await-in-loop + const dereferenced = await dereferenceApiDOM(referenceElement, { + resolve: { baseURI: reference.uri }, + parse: { mediaType: mediaTypes.latest() }, + dereference: { refSet, immutable: false }, + }); + + if (isRemovableSecuritySchemeElement(dereferenced)) { + element.securitySchemes.remove(toValue(memberElement.key)); + annotations.push(createAnnotation(referenceElement)); + } + } + + /** + * Removing Security Scheme Objects. + */ element.securitySchemes.forEach((value, key) => { - if (isSecuritySchemeElement(value) && toValue(value.type) === 'mutualTLS') { + if (isRemovableSecuritySchemeElement(value)) { if (!removedSecuritySchemes.includes(value)) removedSecuritySchemes.push(value); (element.securitySchemes as SecuritySchemeElement).remove(toValue(key)); annotations.push(createAnnotation(value)); @@ -80,6 +128,7 @@ const securitySchemeTypeRefractorPlugin = }, post() { removedSecuritySchemes.length = 0; + parseResultElement = undefined; }, }; }; diff --git a/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/__snapshots__/index.ts.snap b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/__snapshots__/index.ts.snap index a7b5072af..5492069e5 100644 --- a/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/__snapshots__/index.ts.snap +++ b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/__snapshots__/index.ts.snap @@ -1,5 +1,50 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`converter strategies openapi-3-1-to-openapi-3-0-3 security-scheme-type should remove Reference Objects pointing to removable Security Scheme Objects 1`] = ` +{ + "openapi": "3.0.3", + "paths": { + "/foo": { + "get": { + "summary": "foo", + "operationId": "foo", + "security": [ + { + "mutualTLS-scheme2": [ + "read", + "write" + ] + }, + { + "oauth2-scheme2": [ + "read", + "write" + ] + } + ], + "responses": { + "200": { + "description": "foo" + } + } + } + } + }, + "components": { + "securitySchemes": { + "apiKey-scheme1": { + "$ref": "#/components/securitySchemes/apiKey-scheme2" + }, + "apiKey-scheme2": { + "type": "oauth2", + "name": "oauth2-scheme", + "in": "header" + } + } + } +} +`; + exports[`converter strategies openapi-3-1-to-openapi-3-0-3 security-scheme-type should remove SecurityScheme object if it has "mutualTLS" type 1`] = ` { "openapi": "3.0.3", diff --git a/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/fixtures/reference-objects.json b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/fixtures/reference-objects.json new file mode 100644 index 000000000..1d3119858 --- /dev/null +++ b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/fixtures/reference-objects.json @@ -0,0 +1,50 @@ +{ + "openapi": "3.1.0", + "paths": { + "/foo": { + "get": { + "summary": "foo", + "operationId": "foo", + "security": [ + { + "mutualTLS-scheme2": [ + "read", + "write" + ] + }, + { + "oauth2-scheme2": [ + "read", + "write" + ] + } + ], + "responses": { + "200": { + "description": "foo" + } + } + } + } + }, + "components": { + "securitySchemes": { + "mutualTLS-scheme1": { + "$ref": "#/components/securitySchemes/mutualTLS-scheme2" + }, + "apiKey-scheme1": { + "$ref": "#/components/securitySchemes/apiKey-scheme2" + }, + "mutualTLS-scheme2": { + "type": "mutualTLS", + "name": "mutualTLS-scheme", + "in": "header" + }, + "apiKey-scheme2": { + "type": "oauth2", + "name": "oauth2-scheme", + "in": "header" + } + } + } +} diff --git a/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/index.ts b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/index.ts index 425d7b595..891ffbcc8 100644 --- a/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/index.ts +++ b/packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/index.ts @@ -73,6 +73,21 @@ describe('converter', function () { assert.strictEqual(endColumn, 13); assert.strictEqual(endChar, 247); }); + + specify( + 'should remove Reference Objects pointing to removable Security Scheme Objects', + async function () { + const fixturePath = path.join(__dirname, 'fixtures', 'reference-objects.json'); + const convertedParseResult = await convert(fixturePath, { + convert: { + sourceMediaType: openAPI31MediaTypes.findBy('3.1.0', 'json'), + targetMediaType: openAPI30MediaTypes.findBy('3.0.3', 'json'), + }, + }); + + expect(toJSON(convertedParseResult.api!, undefined, 2)).toMatchSnapshot(); + }, + ); }); }); });