Skip to content

Commit 29ea34d

Browse files
authored
feat(resolver): collect errors in ParameterMacroVisitor visitor hooks (#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
1 parent 6cef9ff commit 29ea34d

File tree

5 files changed

+268
-14
lines changed

5 files changed

+268
-14
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const OpenApi3_1SwaggerClientDereferenceStrategy = OpenApi3_1DereferenceStrategy
6262
if (typeof this.parameterMacro === 'function') {
6363
const parameterMacroVisitor = ParameterMacroVisitor({
6464
parameterMacro: this.parameterMacro,
65+
options,
6566
});
6667
visitors.push(parameterMacroVisitor);
6768
}
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,42 @@
11
import { toValue } from '@swagger-api/apidom-core';
22

3-
const ParameterMacroVisitor = ({ parameterMacro }) => {
4-
let macroOperation = null;
3+
import compose from '../utils/compose.js';
4+
import toPath from '../utils/to-path.js';
5+
6+
const ParameterMacroVisitor = compose({
7+
init({ parameterMacro, options }) {
8+
this.parameterMacro = parameterMacro;
9+
this.options = options;
10+
},
11+
props: {
12+
parameterMacro: null,
13+
options: null,
14+
macroOperation: null,
515

6-
return {
716
OperationElement: {
817
enter(operationElement) {
9-
macroOperation = operationElement;
18+
this.macroOperation = operationElement;
1019
},
1120
leave() {
12-
macroOperation = null;
21+
this.macroOperation = null;
1322
},
1423
},
1524
ParameterElement: {
16-
leave(parameterElement) {
17-
const pojoOperation = macroOperation === null ? null : toValue(macroOperation);
25+
leave(parameterElement, key, parent, path, ancestors) {
26+
const pojoOperation = this.macroOperation === null ? null : toValue(this.macroOperation);
1827
const pojoParameter = toValue(parameterElement);
19-
const defaultValue = parameterMacro(pojoOperation, pojoParameter);
2028

21-
parameterElement.set('default', defaultValue);
29+
try {
30+
const macroValue = this.parameterMacro(pojoOperation, pojoParameter);
31+
parameterElement.set('default', macroValue);
32+
} catch (error) {
33+
const macroError = new Error(error, { cause: error });
34+
macroError.fullPath = toPath([...ancestors, parent]);
35+
this.options.dereference.dereferenceOpts?.errors?.push?.(macroError);
36+
}
2237
},
2338
},
24-
};
25-
};
39+
},
40+
});
2641

2742
export default ParameterMacroVisitor;

src/specmap/lib/parameters.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ export default {
88
const opPath = fullPath.slice(0, -1);
99
const op = { ...lib.getIn(specmap.spec, opPath) };
1010

11-
parameters.forEach((param, i) => {
11+
for (let i = 0; i < parameters.length; i += 1) {
12+
const param = parameters[i];
13+
1214
try {
1315
val[i].default = specmap.parameterMacro(op, param);
1416
} catch (e) {
1517
const err = new Error(e);
1618
err.fullPath = fullPath;
1719
return err;
1820
}
19-
return undefined;
20-
});
21+
}
2122

2223
return lib.replace(fullPath, val);
2324
}

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

+218
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,224 @@ exports[`resolve OpenAPI 3.1.0 strategy given OpenAPI 3.1.0 definition via spec
17121712
}
17131713
`;
17141714

1715+
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`] = `
1716+
{
1717+
"$$normalized": true,
1718+
"components": {
1719+
"schemas": {
1720+
"Error": {
1721+
"properties": {
1722+
"code": {
1723+
"format": "int32",
1724+
"type": "integer",
1725+
},
1726+
"message": {
1727+
"type": "string",
1728+
},
1729+
},
1730+
"required": [
1731+
"code",
1732+
"message",
1733+
],
1734+
"type": "object",
1735+
},
1736+
"Pet": {
1737+
"properties": {
1738+
"id": {
1739+
"format": "int64",
1740+
"type": "integer",
1741+
},
1742+
"name": {
1743+
"type": "string",
1744+
},
1745+
"tag": {
1746+
"type": "string",
1747+
},
1748+
},
1749+
"required": [
1750+
"id",
1751+
"name",
1752+
],
1753+
"type": "object",
1754+
},
1755+
"Pets": {
1756+
"items": {
1757+
"properties": {
1758+
"id": {
1759+
"format": "int64",
1760+
"type": "integer",
1761+
},
1762+
"name": {
1763+
"type": "string",
1764+
},
1765+
"tag": {
1766+
"type": "string",
1767+
},
1768+
},
1769+
"required": [
1770+
"id",
1771+
"name",
1772+
],
1773+
"type": "object",
1774+
},
1775+
"maxItems": 100,
1776+
"type": "array",
1777+
},
1778+
},
1779+
},
1780+
"info": {
1781+
"license": {
1782+
"name": "MIT",
1783+
},
1784+
"title": "Swagger Petstore",
1785+
"version": "1.0.0",
1786+
},
1787+
"openapi": "3.1.0",
1788+
"paths": {
1789+
"/pets": {
1790+
"get": {
1791+
"operationId": "listPets",
1792+
"parameters": [
1793+
{
1794+
"description": "How many items to return at one time (max 100)",
1795+
"in": "query",
1796+
"name": "limit",
1797+
"required": false,
1798+
"schema": {
1799+
"format": "int32",
1800+
"maximum": 100,
1801+
"type": "integer",
1802+
},
1803+
},
1804+
],
1805+
"responses": {
1806+
"200": {
1807+
"content": {
1808+
"application/json": {
1809+
"schema": {
1810+
"items": {
1811+
"properties": {
1812+
"id": {
1813+
"format": "int64",
1814+
"type": "integer",
1815+
},
1816+
"name": {
1817+
"type": "string",
1818+
},
1819+
"tag": {
1820+
"type": "string",
1821+
},
1822+
},
1823+
"required": [
1824+
"id",
1825+
"name",
1826+
],
1827+
"type": "object",
1828+
},
1829+
"maxItems": 100,
1830+
"type": "array",
1831+
},
1832+
},
1833+
},
1834+
"description": "A paged array of pets",
1835+
"headers": {
1836+
"x-next": {
1837+
"description": "A link to the next page of responses",
1838+
"schema": {
1839+
"type": "string",
1840+
},
1841+
},
1842+
},
1843+
},
1844+
"default": {
1845+
"content": {
1846+
"application/json": {
1847+
"schema": {
1848+
"properties": {
1849+
"code": {
1850+
"format": "int32",
1851+
"type": "integer",
1852+
},
1853+
"message": {
1854+
"type": "string",
1855+
},
1856+
},
1857+
"required": [
1858+
"code",
1859+
"message",
1860+
],
1861+
"type": "object",
1862+
},
1863+
},
1864+
},
1865+
"description": "unexpected error",
1866+
},
1867+
},
1868+
"servers": [
1869+
{
1870+
"url": "http://petstore.swagger.io/v1",
1871+
},
1872+
],
1873+
"summary": "List all pets",
1874+
"tags": [
1875+
"pets",
1876+
],
1877+
},
1878+
"post": {
1879+
"operationId": "createPets",
1880+
"responses": {
1881+
"201": {
1882+
"description": "Null response",
1883+
},
1884+
"default": {
1885+
"content": {
1886+
"application/json": {
1887+
"schema": {
1888+
"properties": {
1889+
"code": {
1890+
"format": "int32",
1891+
"type": "integer",
1892+
},
1893+
"message": {
1894+
"type": "string",
1895+
},
1896+
},
1897+
"required": [
1898+
"code",
1899+
"message",
1900+
],
1901+
"type": "object",
1902+
},
1903+
},
1904+
},
1905+
"description": "unexpected error",
1906+
},
1907+
},
1908+
"servers": [
1909+
{
1910+
"url": "http://petstore.swagger.io/v1",
1911+
},
1912+
],
1913+
"summary": "Create a pet",
1914+
"tags": [
1915+
"pets",
1916+
],
1917+
},
1918+
"servers": [
1919+
{
1920+
"url": "http://petstore.swagger.io/v1",
1921+
},
1922+
],
1923+
},
1924+
},
1925+
"servers": [
1926+
{
1927+
"url": "http://petstore.swagger.io/v1",
1928+
},
1929+
],
1930+
}
1931+
`;
1932+
17151933
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`] = `
17161934
{
17171935
"errors": [],

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

+19
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,25 @@ describe('resolve', () => {
215215

216216
expect(resolvedSpec).toMatchSnapshot();
217217
});
218+
219+
describe('given the function throws error', () => {
220+
test('should collect error', async () => {
221+
const spec = globalThis.loadJsonFile(path.join(fixturePath, 'parameter-macro.json'));
222+
const { spec: resolvedSpec, errors } = await SwaggerClient.resolve({
223+
spec,
224+
parameterMacro: () => {
225+
throw new Error('this macro throws');
226+
},
227+
});
228+
229+
expect(resolvedSpec).toMatchSnapshot();
230+
expect(errors).toHaveLength(1);
231+
expect(errors[0]).toMatchObject({
232+
message: expect.stringMatching(/^Error: this macro throws/),
233+
fullPath: ['paths', '/pets', 'get', 'parameters'],
234+
});
235+
});
236+
});
218237
});
219238

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

0 commit comments

Comments
 (0)