Skip to content

Commit

Permalink
feat(resolver): collect errors in ParameterMacroVisitor visitor hooks (
Browse files Browse the repository at this point in the history
…#2813)

This change in specific to OpenAPI 3.1.0 resolution
strategy. Errors are now collected, instead of
thrown and visitor traversal is not interrupted.

Refs #2812
  • Loading branch information
char0n authored Feb 1, 2023
1 parent 6cef9ff commit 29ea34d
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy = OpenApi3_1DereferenceStrategy
if (typeof this.parameterMacro === 'function') {
const parameterMacroVisitor = ParameterMacroVisitor({
parameterMacro: this.parameterMacro,
options,
});
visitors.push(parameterMacroVisitor);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
import { toValue } from '@swagger-api/apidom-core';

const ParameterMacroVisitor = ({ parameterMacro }) => {
let macroOperation = null;
import compose from '../utils/compose.js';
import toPath from '../utils/to-path.js';

const ParameterMacroVisitor = compose({
init({ parameterMacro, options }) {
this.parameterMacro = parameterMacro;
this.options = options;
},
props: {
parameterMacro: null,
options: null,
macroOperation: null,

return {
OperationElement: {
enter(operationElement) {
macroOperation = operationElement;
this.macroOperation = operationElement;
},
leave() {
macroOperation = null;
this.macroOperation = null;
},
},
ParameterElement: {
leave(parameterElement) {
const pojoOperation = macroOperation === null ? null : toValue(macroOperation);
leave(parameterElement, key, parent, path, ancestors) {
const pojoOperation = this.macroOperation === null ? null : toValue(this.macroOperation);
const pojoParameter = toValue(parameterElement);
const defaultValue = parameterMacro(pojoOperation, pojoParameter);

parameterElement.set('default', defaultValue);
try {
const macroValue = this.parameterMacro(pojoOperation, pojoParameter);
parameterElement.set('default', macroValue);
} catch (error) {
const macroError = new Error(error, { cause: error });
macroError.fullPath = toPath([...ancestors, parent]);
this.options.dereference.dereferenceOpts?.errors?.push?.(macroError);
}
},
},
};
};
},
});

export default ParameterMacroVisitor;
7 changes: 4 additions & 3 deletions src/specmap/lib/parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ export default {
const opPath = fullPath.slice(0, -1);
const op = { ...lib.getIn(specmap.spec, opPath) };

parameters.forEach((param, i) => {
for (let i = 0; i < parameters.length; i += 1) {
const param = parameters[i];

try {
val[i].default = specmap.parameterMacro(op, param);
} catch (e) {
const err = new Error(e);
err.fullPath = fullPath;
return err;
}
return undefined;
});
}

return lib.replace(fullPath, val);
}
Expand Down
218 changes: 218 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 @@ -1712,6 +1712,224 @@ 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 parameterMacro is provided sa a function given the function throws error should collect error 1`] = `
{
"$$normalized": true,
"components": {
"schemas": {
"Error": {
"properties": {
"code": {
"format": "int32",
"type": "integer",
},
"message": {
"type": "string",
},
},
"required": [
"code",
"message",
],
"type": "object",
},
"Pet": {
"properties": {
"id": {
"format": "int64",
"type": "integer",
},
"name": {
"type": "string",
},
"tag": {
"type": "string",
},
},
"required": [
"id",
"name",
],
"type": "object",
},
"Pets": {
"items": {
"properties": {
"id": {
"format": "int64",
"type": "integer",
},
"name": {
"type": "string",
},
"tag": {
"type": "string",
},
},
"required": [
"id",
"name",
],
"type": "object",
},
"maxItems": 100,
"type": "array",
},
},
},
"info": {
"license": {
"name": "MIT",
},
"title": "Swagger Petstore",
"version": "1.0.0",
},
"openapi": "3.1.0",
"paths": {
"/pets": {
"get": {
"operationId": "listPets",
"parameters": [
{
"description": "How many items to return at one time (max 100)",
"in": "query",
"name": "limit",
"required": false,
"schema": {
"format": "int32",
"maximum": 100,
"type": "integer",
},
},
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"properties": {
"id": {
"format": "int64",
"type": "integer",
},
"name": {
"type": "string",
},
"tag": {
"type": "string",
},
},
"required": [
"id",
"name",
],
"type": "object",
},
"maxItems": 100,
"type": "array",
},
},
},
"description": "A paged array of pets",
"headers": {
"x-next": {
"description": "A link to the next page of responses",
"schema": {
"type": "string",
},
},
},
},
"default": {
"content": {
"application/json": {
"schema": {
"properties": {
"code": {
"format": "int32",
"type": "integer",
},
"message": {
"type": "string",
},
},
"required": [
"code",
"message",
],
"type": "object",
},
},
},
"description": "unexpected error",
},
},
"servers": [
{
"url": "http://petstore.swagger.io/v1",
},
],
"summary": "List all pets",
"tags": [
"pets",
],
},
"post": {
"operationId": "createPets",
"responses": {
"201": {
"description": "Null response",
},
"default": {
"content": {
"application/json": {
"schema": {
"properties": {
"code": {
"format": "int32",
"type": "integer",
},
"message": {
"type": "string",
},
},
"required": [
"code",
"message",
],
"type": "object",
},
},
},
"description": "unexpected error",
},
},
"servers": [
{
"url": "http://petstore.swagger.io/v1",
},
],
"summary": "Create a pet",
"tags": [
"pets",
],
},
"servers": [
{
"url": "http://petstore.swagger.io/v1",
},
],
},
},
"servers": [
{
"url": "http://petstore.swagger.io/v1",
},
],
}
`;

exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec option and parameterMacro is provided sa a function should call parameterMacro with Operation and Parameter Objects 1`] = `
{
"errors": [],
Expand Down
19 changes: 19 additions & 0 deletions test/resolver/strategies/openapi-3-1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,25 @@ describe('resolve', () => {

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

describe('given the function throws error', () => {
test('should collect error', async () => {
const spec = globalThis.loadJsonFile(path.join(fixturePath, 'parameter-macro.json'));
const { spec: resolvedSpec, errors } = await SwaggerClient.resolve({
spec,
parameterMacro: () => {
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: ['paths', '/pets', 'get', 'parameters'],
});
});
});
});

describe('and modelPropertyMacro is provided as a function', () => {
Expand Down

0 comments on commit 29ea34d

Please sign in to comment.