Skip to content

Commit

Permalink
feat(resolver): collect errors in ParameterMacroVisitor visitor hooks
Browse files Browse the repository at this point in the history
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 committed Feb 1, 2023
1 parent 6cef9ff commit 3cae00d
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 3cae00d

Please sign in to comment.