Skip to content

Commit 8266f5a

Browse files
authored
feat(converter): add Reference Object removal support in security-scheme-type plugin (#4001)
This change is specific to openapi-3-1-to-openapi-3-0-3 strategy. Refs #4000
1 parent 833a918 commit 8266f5a

File tree

4 files changed

+162
-3
lines changed

4 files changed

+162
-3
lines changed

packages/apidom-converter/src/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type.ts

+52-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@ import {
55
SecuritySchemeElement,
66
isSecuritySchemeElement,
77
isComponentsElement,
8+
isReferenceElement,
9+
mediaTypes,
810
} from '@swagger-api/apidom-ns-openapi-3-1';
9-
import { AnnotationElement, toValue, isObjectElement, Element } from '@swagger-api/apidom-core';
11+
import {
12+
Element,
13+
ParseResultElement,
14+
AnnotationElement,
15+
isObjectElement,
16+
toValue,
17+
cloneDeep,
18+
} from '@swagger-api/apidom-core';
19+
import { dereferenceApiDOM, ReferenceSet, Reference, url } from '@swagger-api/apidom-reference';
1020

1121
import type { Toolbox } from '../toolbox';
1222

@@ -17,6 +27,9 @@ type SecuritySchemeTypePluginOptions = {
1727
const securitySchemeTypeRefractorPlugin =
1828
({ annotations }: SecuritySchemeTypePluginOptions) =>
1929
(toolbox: Toolbox) => {
30+
let parseResultElement: ParseResultElement | undefined;
31+
const isRemovableSecuritySchemeElement = (value: unknown): value is SecuritySchemeElement =>
32+
isSecuritySchemeElement(value) && toValue(value.type) === 'mutualTLS';
2033
const removedSecuritySchemes: SecuritySchemeElement[] = [];
2134
const createAnnotation = <T extends Element>(element: T) =>
2235
toolbox.createAnnotation.fromElement(
@@ -28,6 +41,9 @@ const securitySchemeTypeRefractorPlugin =
2841

2942
return {
3043
visitor: {
44+
ParseResultElement(element: ParseResultElement) {
45+
parseResultElement = element;
46+
},
3147
OpenApi3_1Element(element: OpenApi3_1Element) {
3248
if (!isComponentsElement(element.components)) return undefined;
3349
if (!isObjectElement(element.components.securitySchemes)) return undefined;
@@ -40,11 +56,43 @@ const securitySchemeTypeRefractorPlugin =
4056

4157
return undefined;
4258
},
43-
ComponentsElement(element: ComponentsElement) {
59+
async ComponentsElement(element: ComponentsElement) {
4460
if (!isObjectElement(element.securitySchemes)) return undefined;
4561

62+
/**
63+
* Removing Reference Objects pointing to removable Security Scheme Objects.
64+
* We need to remove Reference Objects first as they might be pointing
65+
* to Security Scheme Objects that are going to be removed.
66+
*/
67+
const baseURI = url.cwd();
68+
const rootReference = Reference({ uri: baseURI, value: cloneDeep(parseResultElement!) });
69+
for (const memberElement of element.securitySchemes) {
70+
if (!isReferenceElement(memberElement.value)) continue; // eslint-disable-line no-continue
71+
72+
const { value: referenceElement } = memberElement;
73+
const reference = Reference({
74+
uri: `${baseURI}#reference`,
75+
value: new ParseResultElement([referenceElement]),
76+
});
77+
const refSet = ReferenceSet({ refs: [reference, rootReference] });
78+
// eslint-disable-next-line no-await-in-loop
79+
const dereferenced = await dereferenceApiDOM(referenceElement, {
80+
resolve: { baseURI: reference.uri },
81+
parse: { mediaType: mediaTypes.latest() },
82+
dereference: { refSet, immutable: false },
83+
});
84+
85+
if (isRemovableSecuritySchemeElement(dereferenced)) {
86+
element.securitySchemes.remove(toValue(memberElement.key));
87+
annotations.push(createAnnotation(referenceElement));
88+
}
89+
}
90+
91+
/**
92+
* Removing Security Scheme Objects.
93+
*/
4694
element.securitySchemes.forEach((value, key) => {
47-
if (isSecuritySchemeElement(value) && toValue(value.type) === 'mutualTLS') {
95+
if (isRemovableSecuritySchemeElement(value)) {
4896
if (!removedSecuritySchemes.includes(value)) removedSecuritySchemes.push(value);
4997
(element.securitySchemes as SecuritySchemeElement).remove(toValue(key));
5098
annotations.push(createAnnotation(value));
@@ -80,6 +128,7 @@ const securitySchemeTypeRefractorPlugin =
80128
},
81129
post() {
82130
removedSecuritySchemes.length = 0;
131+
parseResultElement = undefined;
83132
},
84133
};
85134
};

packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/__snapshots__/index.ts.snap

+45
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,50 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
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`] = `
4+
{
5+
"openapi": "3.0.3",
6+
"paths": {
7+
"/foo": {
8+
"get": {
9+
"summary": "foo",
10+
"operationId": "foo",
11+
"security": [
12+
{
13+
"mutualTLS-scheme2": [
14+
"read",
15+
"write"
16+
]
17+
},
18+
{
19+
"oauth2-scheme2": [
20+
"read",
21+
"write"
22+
]
23+
}
24+
],
25+
"responses": {
26+
"200": {
27+
"description": "foo"
28+
}
29+
}
30+
}
31+
}
32+
},
33+
"components": {
34+
"securitySchemes": {
35+
"apiKey-scheme1": {
36+
"$ref": "#/components/securitySchemes/apiKey-scheme2"
37+
},
38+
"apiKey-scheme2": {
39+
"type": "oauth2",
40+
"name": "oauth2-scheme",
41+
"in": "header"
42+
}
43+
}
44+
}
45+
}
46+
`;
47+
348
exports[`converter strategies openapi-3-1-to-openapi-3-0-3 security-scheme-type should remove SecurityScheme object if it has "mutualTLS" type 1`] = `
449
{
550
"openapi": "3.0.3",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"openapi": "3.1.0",
3+
"paths": {
4+
"/foo": {
5+
"get": {
6+
"summary": "foo",
7+
"operationId": "foo",
8+
"security": [
9+
{
10+
"mutualTLS-scheme2": [
11+
"read",
12+
"write"
13+
]
14+
},
15+
{
16+
"oauth2-scheme2": [
17+
"read",
18+
"write"
19+
]
20+
}
21+
],
22+
"responses": {
23+
"200": {
24+
"description": "foo"
25+
}
26+
}
27+
}
28+
}
29+
},
30+
"components": {
31+
"securitySchemes": {
32+
"mutualTLS-scheme1": {
33+
"$ref": "#/components/securitySchemes/mutualTLS-scheme2"
34+
},
35+
"apiKey-scheme1": {
36+
"$ref": "#/components/securitySchemes/apiKey-scheme2"
37+
},
38+
"mutualTLS-scheme2": {
39+
"type": "mutualTLS",
40+
"name": "mutualTLS-scheme",
41+
"in": "header"
42+
},
43+
"apiKey-scheme2": {
44+
"type": "oauth2",
45+
"name": "oauth2-scheme",
46+
"in": "header"
47+
}
48+
}
49+
}
50+
}

packages/apidom-converter/test/strategies/openapi-3-1-to-openapi-3-0-3/refractor-plugins/security-scheme-type/index.ts

+15
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,21 @@ describe('converter', function () {
7373
assert.strictEqual(endColumn, 13);
7474
assert.strictEqual(endChar, 247);
7575
});
76+
77+
specify(
78+
'should remove Reference Objects pointing to removable Security Scheme Objects',
79+
async function () {
80+
const fixturePath = path.join(__dirname, 'fixtures', 'reference-objects.json');
81+
const convertedParseResult = await convert(fixturePath, {
82+
convert: {
83+
sourceMediaType: openAPI31MediaTypes.findBy('3.1.0', 'json'),
84+
targetMediaType: openAPI30MediaTypes.findBy('3.0.3', 'json'),
85+
},
86+
});
87+
88+
expect(toJSON(convertedParseResult.api!, undefined, 2)).toMatchSnapshot();
89+
},
90+
);
7691
});
7792
});
7893
});

0 commit comments

Comments
 (0)