Skip to content

Commit 6cef9ff

Browse files
authored
feat(resolver): collect errors in ModelPropertyMacro visitor hooks (#2811)
This change is specific to OpenAPI 3.1.0 resolution strategy. Errors are now collected, instead of thrown and visitor traversal is not interrupted. Refs #2810
1 parent 627ee8d commit 6cef9ff

File tree

5 files changed

+107
-9
lines changed

5 files changed

+107
-9
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy = OpenApi3_1DereferenceStrategy
7070
if (typeof this.modelPropertyMacro === 'function') {
7171
const modelPropertyMacroVisitor = ModelPropertyMacroVisitor({
7272
modelPropertyMacro: this.modelPropertyMacro,
73+
options,
7374
});
7475
visitors.push(modelPropertyMacroVisitor);
7576
}

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

+27-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
import { isObjectElement, toValue } from '@swagger-api/apidom-core';
22

3-
const ModelPropertyMacroVisitor = ({ modelPropertyMacro }) => ({
4-
SchemaElement: {
5-
leave(schemaElement) {
6-
if (typeof schemaElement.properties === 'undefined') return;
7-
if (!isObjectElement(schemaElement.properties)) return;
3+
import compose from '../utils/compose.js';
4+
import toPath from '../utils/to-path.js';
85

9-
schemaElement.properties.forEach((property) => {
10-
if (!isObjectElement(property)) return;
6+
const ModelPropertyMacroVisitor = compose({
7+
init({ modelPropertyMacro, options }) {
8+
this.modelPropertyMacro = modelPropertyMacro;
9+
this.options = options;
10+
},
11+
props: {
12+
modelPropertyMacro: null,
13+
options: null,
14+
SchemaElement: {
15+
leave(schemaElement, key, parent, path, ancestors) {
16+
if (typeof schemaElement.properties === 'undefined') return;
17+
if (!isObjectElement(schemaElement.properties)) return;
18+
19+
schemaElement.properties.forEach((property) => {
20+
if (!isObjectElement(property)) return;
1121

12-
property.set('default', modelPropertyMacro(toValue(property)));
13-
});
22+
try {
23+
const macroValue = this.modelPropertyMacro(toValue(property));
24+
property.set('default', macroValue);
25+
} catch (error) {
26+
const macroError = new Error(error, { cause: error });
27+
macroError.fullPath = [...toPath([...ancestors, parent, schemaElement]), 'properties'];
28+
this.options.dereference.dereferenceOpts?.errors?.push?.(macroError);
29+
}
30+
});
31+
},
1432
},
1533
},
1634
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"version": "1.0.0",
5+
"title": "Swagger Petstore",
6+
"license": {
7+
"name": "MIT"
8+
}
9+
},
10+
"components": {
11+
"schemas": {
12+
"Pet": {
13+
"type": "object",
14+
"required": [
15+
"id",
16+
"name"
17+
],
18+
"properties": {
19+
"id": {
20+
"type": "integer",
21+
"format": "int64"
22+
}
23+
}
24+
}
25+
}
26+
}
27+
}

test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap

+31
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,37 @@ exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec
12921292
}
12931293
`;
12941294

1295+
exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec option and modelPropertyMacro is provided as a function given the function throws error should collect error 1`] = `
1296+
{
1297+
"$$normalized": true,
1298+
"components": {
1299+
"schemas": {
1300+
"Pet": {
1301+
"properties": {
1302+
"id": {
1303+
"format": "int64",
1304+
"type": "integer",
1305+
},
1306+
},
1307+
"required": [
1308+
"id",
1309+
"name",
1310+
],
1311+
"type": "object",
1312+
},
1313+
},
1314+
},
1315+
"info": {
1316+
"license": {
1317+
"name": "MIT",
1318+
},
1319+
"title": "Swagger Petstore",
1320+
"version": "1.0.0",
1321+
},
1322+
"openapi": "3.1.0",
1323+
}
1324+
`;
1325+
12951326
exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec option and modelPropertyMacro is provided as a function should call modelPropertyMacro with Schema Object property 1`] = `
12961327
{
12971328
"errors": [],

test/resolver/strategies/openapi-3-1/index.js

+21
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,27 @@ describe('resolve', () => {
227227

228228
expect(resolvedSpec).toMatchSnapshot();
229229
});
230+
231+
describe('given the function throws error', () => {
232+
test('should collect error', async () => {
233+
const spec = globalThis.loadJsonFile(
234+
path.join(fixturePath, 'model-property-macro-error.json')
235+
);
236+
const { spec: resolvedSpec, errors } = await SwaggerClient.resolve({
237+
spec,
238+
modelPropertyMacro: () => {
239+
throw new Error('this macro throws');
240+
},
241+
});
242+
243+
expect(resolvedSpec).toMatchSnapshot();
244+
expect(errors).toHaveLength(1);
245+
expect(errors[0]).toMatchObject({
246+
message: expect.stringMatching(/^Error: this macro throws/),
247+
fullPath: ['components', 'schemas', 'Pet', 'properties'],
248+
});
249+
});
250+
});
230251
});
231252
});
232253
});

0 commit comments

Comments
 (0)