Skip to content

Commit

Permalink
feat(resolver): collect errors in ModelPropertyMacro visitor hooks
Browse files Browse the repository at this point in the history
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
  • Loading branch information
char0n committed Feb 1, 2023
1 parent 627ee8d commit 54a512e
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy = OpenApi3_1DereferenceStrategy
if (typeof this.modelPropertyMacro === 'function') {
const modelPropertyMacroVisitor = ModelPropertyMacroVisitor({
modelPropertyMacro: this.modelPropertyMacro,
options,
});
visitors.push(modelPropertyMacroVisitor);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
import { isObjectElement, toValue } from '@swagger-api/apidom-core';

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

schemaElement.properties.forEach((property) => {
if (!isObjectElement(property)) return;
const ModelPropertyMacroVisitor = compose({
init({ modelPropertyMacro, options }) {
this.modelPropertyMacro = modelPropertyMacro;
this.options = options;
},
props: {
modelPropertyMacro: null,
options: null,
SchemaElement: {
leave(schemaElement, key, parent, path, ancestors) {
if (typeof schemaElement.properties === 'undefined') return;
if (!isObjectElement(schemaElement.properties)) return;

schemaElement.properties.forEach((property) => {
if (!isObjectElement(property)) return;

property.set('default', modelPropertyMacro(toValue(property)));
});
try {
const macroValue = this.modelPropertyMacro(toValue(property));
property.set('default', macroValue);
} catch (error) {
const macroError = new Error(error, { cause: error });
macroError.fullPath = [...toPath([...ancestors, parent, schemaElement]), 'properties'];
this.options.dereference.dereferenceOpts?.errors?.push?.(macroError);
}
});
},
},
},
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"openapi": "3.1.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"license": {
"name": "MIT"
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
}
}
}
31 changes: 31 additions & 0 deletions test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,37 @@ exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec
}
`;

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`] = `
{
"$$normalized": true,
"components": {
"schemas": {
"Pet": {
"properties": {
"id": {
"format": "int64",
"type": "integer",
},
},
"required": [
"id",
"name",
],
"type": "object",
},
},
},
"info": {
"license": {
"name": "MIT",
},
"title": "Swagger Petstore",
"version": "1.0.0",
},
"openapi": "3.1.0",
}
`;

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`] = `
{
"errors": [],
Expand Down
21 changes: 21 additions & 0 deletions test/resolver/strategies/openapi-3-1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ describe('resolve', () => {

expect(resolvedSpec).toMatchSnapshot();
});

describe('given the function throws error', () => {
test('should collect error', async () => {
const spec = globalThis.loadJsonFile(
path.join(fixturePath, 'model-property-macro-error.json')
);
const { spec: resolvedSpec, errors } = await SwaggerClient.resolve({
spec,
modelPropertyMacro: () => {
throw new Error('this macro throws');
},
});

expect(resolvedSpec).toMatchSnapshot();
expect(errors).toHaveLength(1);
expect(errors[0]).toMatchObject({
message: expect.stringMatching(/^Error: this macro throws/),
fullPath: ['components', 'schemas', 'Pet', 'properties'],
});
});
});
});
});
});
Expand Down

0 comments on commit 54a512e

Please sign in to comment.