Skip to content

Commit 2eb34c9

Browse files
glowcloudchar0n
andauthored
fix(resolver): apply propertyMacro, parameterMacro and allOf plugins for OpenAPI 3.1.0 (#3521)
Refs #3520 --------- Co-authored-by: Vladimir Gorej <[email protected]>
1 parent 75cf1ad commit 2eb34c9

File tree

13 files changed

+1997
-2306
lines changed

13 files changed

+1997
-2306
lines changed

config/jest/jest.unit.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ module.exports = {
1515
'/__fixtures__/',
1616
'/__utils__/',
1717
],
18-
silent: false,
18+
silent: true,
1919
};

package-lock.json

Lines changed: 1394 additions & 2222 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@
7373
"license": "Apache-2.0",
7474
"dependencies": {
7575
"@babel/runtime-corejs3": "^7.22.15",
76-
"@swagger-api/apidom-core": ">=1.0.0-alpha.1 <1.0.0-beta.0",
76+
"@swagger-api/apidom-core": ">=1.0.0-alpha.3 <1.0.0-beta.0",
7777
"@swagger-api/apidom-error": ">=1.0.0-alpha.1 <1.0.0-beta.0",
78-
"@swagger-api/apidom-json-pointer": ">=1.0.0-alpha.1 <1.0.0-beta.0",
79-
"@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-alpha.1 <1.0.0-beta.0",
80-
"@swagger-api/apidom-reference": ">=1.0.0-alpha.1 <1.0.0-beta.0",
78+
"@swagger-api/apidom-json-pointer": ">=1.0.0-alpha.3 <1.0.0-beta.0",
79+
"@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-alpha.3 <1.0.0-beta.0",
80+
"@swagger-api/apidom-reference": ">=1.0.0-alpha.3 <1.0.0-beta.0",
8181
"cookie": "~0.6.0",
8282
"deepmerge": "~4.3.0",
8383
"fast-json-patch": "^3.0.0-1",

src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
/* eslint-disable camelcase */
2-
import { createNamespace, visit, mergeAllVisitors, cloneDeep } from '@swagger-api/apidom-core';
2+
import { createNamespace, visit, cloneDeep } from '@swagger-api/apidom-core';
33
import { ReferenceSet, Reference } from '@swagger-api/apidom-reference/configuration/empty';
44
import OpenAPI3_1DereferenceStrategy from '@swagger-api/apidom-reference/dereference/strategies/openapi-3-1';
55
import openApi3_1Namespace, { getNodeType, keyMap } from '@swagger-api/apidom-ns-openapi-3-1';
66

7-
import OpenAPI3_1SwaggerClientDereferenceVisitor from './visitors/dereference.js';
8-
import ParameterMacroVisitor from './visitors/parameters.js';
9-
import ModelPropertyMacroVisitor from './visitors/properties.js';
10-
import AllOfVisitor from './visitors/all-of.js';
7+
import RootVisitor from './visitors/root.js';
118

129
const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
13-
const mergeAllVisitorsAsync = mergeAllVisitors[Symbol.for('nodejs.util.promisify.custom')];
1410

1511
class OpenAPI3_1SwaggerClientDereferenceStrategy extends OpenAPI3_1DereferenceStrategy {
1612
allowMetaPatches;
@@ -42,7 +38,6 @@ class OpenAPI3_1SwaggerClientDereferenceStrategy extends OpenAPI3_1DereferenceSt
4238
}
4339

4440
async dereference(file, options) {
45-
const visitors = [];
4641
const namespace = createNamespace(openApi3_1Namespace);
4742
const immutableRefSet = options.dereference.refSet ?? new ReferenceSet();
4843
const mutableRefsSet = new ReferenceSet();
@@ -75,42 +70,16 @@ class OpenAPI3_1SwaggerClientDereferenceStrategy extends OpenAPI3_1DereferenceSt
7570
refSet = mutableRefsSet;
7671
}
7772

78-
// create main dereference visitor
79-
const dereferenceVisitor = new OpenAPI3_1SwaggerClientDereferenceVisitor({
73+
const rootVisitor = new RootVisitor({
8074
reference,
8175
namespace,
8276
options,
8377
allowMetaPatches: this.allowMetaPatches,
8478
ancestors: this.ancestors,
79+
modelPropertyMacro: this.modelPropertyMacro,
80+
mode: this.mode,
81+
parameterMacro: this.parameterMacro,
8582
});
86-
visitors.push(dereferenceVisitor);
87-
88-
// create parameter macro visitor (if necessary)
89-
if (typeof this.parameterMacro === 'function') {
90-
const parameterMacroVisitor = new ParameterMacroVisitor({
91-
parameterMacro: this.parameterMacro,
92-
options,
93-
});
94-
visitors.push(parameterMacroVisitor);
95-
}
96-
97-
// create model property macro visitor (if necessary)
98-
if (typeof this.modelPropertyMacro === 'function') {
99-
const modelPropertyMacroVisitor = new ModelPropertyMacroVisitor({
100-
modelPropertyMacro: this.modelPropertyMacro,
101-
options,
102-
});
103-
visitors.push(modelPropertyMacroVisitor);
104-
}
105-
106-
// create allOf visitor (if necessary)
107-
if (this.mode !== 'strict') {
108-
const allOfVisitor = new AllOfVisitor({ options });
109-
visitors.push(allOfVisitor);
110-
}
111-
112-
// establish root visitor by visitor merging
113-
const rootVisitor = mergeAllVisitorsAsync(visitors, { nodeTypeGetter: getNodeType });
11483

11584
const dereferencedElement = await visitAsync(refSet.rootRef.value, rootVisitor, {
11685
keyMap,

src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/dereference.js

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
5858
// initialize element identity manager
5959
const identityManager = new IdentityManager();
6060

61+
// custom mutation replacer
62+
const mutationReplacer = (newElement, oldElement, key, parent) => {
63+
if (isMemberElement(parent)) {
64+
parent.value = newElement; // eslint-disable-line no-param-reassign
65+
} else if (Array.isArray(parent)) {
66+
parent[key] = newElement; // eslint-disable-line no-param-reassign
67+
}
68+
};
69+
6170
class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVisitor {
6271
useCircularStructures;
6372

@@ -78,7 +87,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
7887
this.basePath = basePath;
7988
}
8089

81-
async ReferenceElement(referencingElement, key, parent, path, ancestors) {
90+
async ReferenceElement(referencingElement, key, parent, path, ancestors, link) {
8291
try {
8392
// skip current referencing element as it's already been access
8493
if (this.indirections.includes(referencingElement)) {
@@ -162,11 +171,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
162171
this.options.dereference.circularReplacer;
163172
const replacement = replacer(refElement);
164173

165-
if (isMemberElement(parent)) {
166-
parent.value = replacement; // eslint-disable-line no-param-reassign
167-
} else if (Array.isArray(parent)) {
168-
parent[key] = replacement; // eslint-disable-line no-param-reassign
169-
}
174+
link.replaceWith(refElement, mutationReplacer);
170175

171176
return !parent ? replacement : false;
172177
}
@@ -258,11 +263,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
258263
/**
259264
* Transclude referencing element with merged referenced element.
260265
*/
261-
if (isMemberElement(parent)) {
262-
parent.value = mergedElement; // eslint-disable-line no-param-reassign
263-
} else if (Array.isArray(parent)) {
264-
parent[key] = mergedElement; // eslint-disable-line no-param-reassign
265-
}
266+
link.replaceWith(mergedElement, mutationReplacer);
266267

267268
/**
268269
* We're at the root of the tree, so we're just replacing the entire tree.
@@ -282,7 +283,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
282283
}
283284
}
284285

285-
async PathItemElement(pathItemElement, key, parent, path, ancestors) {
286+
async PathItemElement(pathItemElement, key, parent, path, ancestors, link) {
286287
try {
287288
// ignore PathItemElement without $ref field
288289
if (!isStringElement(pathItemElement.$ref)) {
@@ -368,11 +369,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
368369
this.options.dereference.circularReplacer;
369370
const replacement = replacer(refElement);
370371

371-
if (isMemberElement(parent)) {
372-
parent.value = replacement; // eslint-disable-line no-param-reassign
373-
} else if (Array.isArray(parent)) {
374-
parent[key] = replacement; // eslint-disable-line no-param-reassign
375-
}
372+
link.replaceWith(refElement, mutationReplacer);
376373

377374
return !parent ? replacement : false;
378375
}
@@ -464,11 +461,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
464461
/**
465462
* Transclude referencing element with merged referenced element.
466463
*/
467-
if (isMemberElement(parent)) {
468-
parent.value = referencedElement; // eslint-disable-line no-param-reassign
469-
} else if (Array.isArray(parent)) {
470-
parent[key] = referencedElement; // eslint-disable-line no-param-reassign
471-
}
464+
link.replaceWith(referencedElement, mutationReplacer);
472465

473466
/**
474467
* We're at the root of the tree, so we're just replacing the entire tree.
@@ -488,7 +481,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
488481
}
489482
}
490483

491-
async SchemaElement(referencingElement, key, parent, path, ancestors) {
484+
async SchemaElement(referencingElement, key, parent, path, ancestors, link) {
492485
try {
493486
// skip current referencing schema as $ref keyword was not defined
494487
if (!isStringElement(referencingElement.$ref)) {
@@ -651,11 +644,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
651644
this.options.dereference.circularReplacer;
652645
const replacement = replacer(refElement);
653646

654-
if (isMemberElement(parent)) {
655-
parent.value = replacement; // eslint-disable-line no-param-reassign
656-
} else if (Array.isArray(parent)) {
657-
parent[key] = replacement; // eslint-disable-line no-param-reassign
658-
}
647+
link.replaceWith(replacement, mutationReplacer);
659648

660649
return !parent ? replacement : false;
661650
}
@@ -721,11 +710,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
721710
cloneDeep(identityManager.identify(referencingElement))
722711
);
723712

724-
if (isMemberElement(parent)) {
725-
parent.value = booleanJsonSchemaElement; // eslint-disable-line no-param-reassign
726-
} else if (Array.isArray(parent)) {
727-
parent[key] = booleanJsonSchemaElement; // eslint-disable-line no-param-reassign
728-
}
713+
link.replaceWith(booleanJsonSchemaElement, mutationReplacer);
729714

730715
return !parent ? booleanJsonSchemaElement : false;
731716
}
@@ -773,11 +758,7 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
773758
/**
774759
* Transclude referencing element with merged referenced element.
775760
*/
776-
if (isMemberElement(parent)) {
777-
parent.value = referencedElement; // eslint-disable-line no-param-reassign
778-
} else if (Array.isArray(parent)) {
779-
parent[key] = referencedElement; // eslint-disable-line no-param-reassign
780-
}
761+
link.replaceWith(referencedElement, mutationReplacer);
781762

782763
/**
783764
* We're at the root of the tree, so we're just replacing the entire tree.
@@ -807,9 +788,9 @@ class OpenAPI3_1SwaggerClientDereferenceVisitor extends OpenAPI3_1DereferenceVis
807788
return undefined;
808789
}
809790

810-
async ExampleElement(exampleElement, key, parent, path, ancestors) {
791+
async ExampleElement(exampleElement, key, parent, path, ancestors, link) {
811792
try {
812-
return await super.ExampleElement(exampleElement, key, parent, path, ancestors);
793+
return await super.ExampleElement(exampleElement, key, parent, path, ancestors, link);
813794
} catch (error) {
814795
const rootCause = getRootCause(error);
815796
const wrappedError = wrapError(rootCause, {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { mergeAllVisitors } from '@swagger-api/apidom-core';
2+
import { getNodeType } from '@swagger-api/apidom-ns-openapi-3-1';
3+
4+
import ModelPropertyMacroVisitor from './properties.js';
5+
import AllOfVisitor from './all-of.js';
6+
import ParameterMacroVisitor from './parameters.js';
7+
import OpenAPI3_1SwaggerClientDereferenceVisitor from './dereference.js'; // eslint-disable-line camelcase
8+
9+
const mergeAllVisitorsAsync = mergeAllVisitors[Symbol.for('nodejs.util.promisify.custom')];
10+
11+
class RootVisitor {
12+
constructor({ parameterMacro, modelPropertyMacro, mode, options, ...rest }) {
13+
const visitors = [];
14+
15+
visitors.push(
16+
new OpenAPI3_1SwaggerClientDereferenceVisitor({
17+
...rest,
18+
options,
19+
})
20+
);
21+
22+
if (typeof modelPropertyMacro === 'function') {
23+
visitors.push(new ModelPropertyMacroVisitor({ modelPropertyMacro, options }));
24+
}
25+
26+
if (mode !== 'strict') {
27+
visitors.push(new AllOfVisitor({ options }));
28+
}
29+
30+
if (typeof parameterMacro === 'function') {
31+
visitors.push(new ParameterMacroVisitor({ parameterMacro, options }));
32+
}
33+
34+
const mergedVisitor = mergeAllVisitorsAsync(visitors, { nodeTypeGetter: getNodeType });
35+
Object.assign(this, mergedVisitor);
36+
}
37+
}
38+
39+
export default RootVisitor;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/* eslint-disable camelcase */
2+
import { toValue } from '@swagger-api/apidom-core';
3+
import { mediaTypes, OpenApi3_1Element } from '@swagger-api/apidom-ns-openapi-3-1';
4+
import { dereferenceApiDOM } from '@swagger-api/apidom-reference/configuration/empty';
5+
6+
import * as jestSetup from '../__utils__/jest.local.setup.js';
7+
import OpenAPI3_1SwaggerClientDereferenceStrategy from '../../../../../../../../src/resolver/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js';
8+
9+
describe('dereference', () => {
10+
beforeAll(() => {
11+
jestSetup.beforeAll();
12+
});
13+
14+
afterAll(() => {
15+
jestSetup.afterAll();
16+
});
17+
18+
describe('strategies', () => {
19+
describe('openapi-3-1-swagger-client', () => {
20+
describe('Parametr Object', () => {
21+
test('should resolve reference and apply modelPropertyMacro function', async () => {
22+
const spec = OpenApi3_1Element.refract({
23+
openapi: '3.1.0',
24+
paths: {
25+
'/': {
26+
get: {
27+
operationId: 'test',
28+
parameters: [
29+
{
30+
$ref: '#/components/parameters/Baz',
31+
},
32+
],
33+
},
34+
},
35+
},
36+
components: {
37+
parameters: {
38+
Baz: {
39+
name: 'baz',
40+
in: 'query',
41+
schema: {
42+
type: 'object',
43+
},
44+
},
45+
},
46+
},
47+
});
48+
49+
const dereferenced = await dereferenceApiDOM(spec, {
50+
parse: { mediaType: mediaTypes.latest('json') },
51+
dereference: {
52+
strategies: [
53+
new OpenAPI3_1SwaggerClientDereferenceStrategy({
54+
parameterMacro: (operation, parameter) =>
55+
`${operation.operationId}-${parameter.name}`,
56+
}),
57+
],
58+
},
59+
});
60+
61+
expect(toValue(dereferenced)).toEqual({
62+
openapi: '3.1.0',
63+
paths: {
64+
'/': {
65+
get: {
66+
operationId: 'test',
67+
parameters: [
68+
{
69+
name: 'baz',
70+
in: 'query',
71+
schema: { type: 'object' },
72+
default: 'test-baz',
73+
},
74+
],
75+
},
76+
},
77+
},
78+
components: {
79+
parameters: {
80+
Baz: {
81+
name: 'baz',
82+
in: 'query',
83+
schema: {
84+
type: 'object',
85+
},
86+
},
87+
},
88+
},
89+
});
90+
});
91+
});
92+
});
93+
});
94+
});
95+
/* eslint-enable camelcase */

0 commit comments

Comments
 (0)