diff --git a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js index db7852b73..3006a4112 100644 --- a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js +++ b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/index.js @@ -62,6 +62,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy = OpenApi3_1DereferenceStrategy if (typeof this.parameterMacro === 'function') { const parameterMacroVisitor = ParameterMacroVisitor({ parameterMacro: this.parameterMacro, + options, }); visitors.push(parameterMacroVisitor); } diff --git a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/parameters.js b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/parameters.js index 9e9cfad67..7f78fe51b 100644 --- a/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/parameters.js +++ b/src/helpers/apidom/reference/dereference/strategies/openapi-3-1-swagger-client/visitors/parameters.js @@ -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; diff --git a/src/specmap/lib/parameters.js b/src/specmap/lib/parameters.js index efe528570..624b8cba9 100644 --- a/src/specmap/lib/parameters.js +++ b/src/specmap/lib/parameters.js @@ -8,7 +8,9 @@ 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) { @@ -16,8 +18,7 @@ export default { err.fullPath = fullPath; return err; } - return undefined; - }); + } return lib.replace(fullPath, val); } diff --git a/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap b/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap index 1fcd08a9c..2f6bc5e47 100644 --- a/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap +++ b/test/resolver/strategies/openapi-3-1/__snapshots__/index.js.snap @@ -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": [], diff --git a/test/resolver/strategies/openapi-3-1/index.js b/test/resolver/strategies/openapi-3-1/index.js index 7ba1a09a6..be26841f7 100644 --- a/test/resolver/strategies/openapi-3-1/index.js +++ b/test/resolver/strategies/openapi-3-1/index.js @@ -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', () => {