Skip to content

Commit 6fe3ac8

Browse files
authored
Handle parameters on paths as well as methods (#347)
* Handle parameters on paths as well as methods Factors out generating the parameters block into a `parseParameters` function. Calls it both on operations, and also on the parameters block at the path level if one was given. Prevents the parameters block being parsed as a method. Fixes #346 * Fix linting * Add a test to exercise fix for #346 Adds a test of specifying parameters on the path instead of each method.
1 parent 40496b3 commit 6fe3ac8

File tree

3 files changed

+142
-81
lines changed

3 files changed

+142
-81
lines changed

src/types/OpenAPI3.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface OpenAPI3Schemas {
1010

1111
export interface OpenAPI3Paths {
1212
[path: string]: {
13-
[method: string]: OpenAPI3Operation;
13+
[method: string]: OpenAPI3Operation | Parameter[];
1414
};
1515
}
1616

src/v3.ts

Lines changed: 99 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
OpenAPI3Paths,
66
OpenAPI3SchemaObject,
77
OpenAPI3Schemas,
8+
OpenAPI3Operation,
9+
Parameter,
810
SwaggerToTSOptions,
911
} from "./types";
1012
import {
@@ -156,100 +158,117 @@ export default function generateTypesV3(
156158
return output;
157159
}
158160

161+
function transformParameters(parameters: Parameter[]): string {
162+
const allParameters: Record<
163+
string,
164+
Record<string, OpenAPI3Parameter | string>
165+
> = {};
166+
167+
let output = `parameters: {\n`;
168+
169+
parameters.forEach((p) => {
170+
if ("$ref" in p) {
171+
const referencedValue = (p.$ref
172+
.substr(2)
173+
.split("/")
174+
.reduce(
175+
(value, property) => value[property],
176+
input
177+
) as unknown) as OpenAPI3Parameter;
178+
179+
if (!allParameters[referencedValue.in])
180+
allParameters[referencedValue.in] = {};
181+
182+
allParameters[referencedValue.in][referencedValue.name] = transformRef(
183+
p.$ref
184+
);
185+
return;
186+
}
187+
188+
if (!allParameters[p.in]) allParameters[p.in] = {};
189+
allParameters[p.in][p.name] = p;
190+
});
191+
192+
Object.entries(allParameters).forEach(([loc, locParams]) => {
193+
output += `"${loc}": {\n`;
194+
Object.entries(locParams).forEach(([paramName, paramProps]) => {
195+
if (typeof paramProps === "string") {
196+
output += `"${paramName}": ${paramProps}\n`;
197+
return;
198+
}
199+
if (paramProps.description) output += comment(paramProps.description);
200+
output += `"${paramName}"${
201+
paramProps.required === true ? "" : "?"
202+
}: ${transform(paramProps.schema)};\n`;
203+
});
204+
output += `}\n`;
205+
});
206+
output += `}\n`;
207+
208+
return output;
209+
}
210+
159211
function transformPaths(paths: OpenAPI3Paths): string {
160212
let output = "";
161213
Object.entries(paths).forEach(([path, methods]) => {
162214
output += `"${path}": {\n`;
163-
Object.entries(methods).forEach(([method, operation]) => {
164-
if (operation.description) output += comment(operation.description);
165-
output += `"${method}": {\n`;
166-
167-
// handle parameters
168-
if (operation.parameters) {
169-
output += `parameters: {\n`;
170-
const allParameters: Record<
171-
string,
172-
Record<string, OpenAPI3Parameter | string>
173-
> = {};
174-
operation.parameters.forEach((p) => {
175-
if ("$ref" in p) {
176-
const referencedValue = (p.$ref
177-
.substr(2)
178-
.split("/")
179-
.reduce(
180-
(value, property) => value[property],
181-
input
182-
) as unknown) as OpenAPI3Parameter;
183-
184-
if (!allParameters[referencedValue.in])
185-
allParameters[referencedValue.in] = {};
186-
187-
allParameters[referencedValue.in][
188-
referencedValue.name
189-
] = transformRef(p.$ref);
190-
return;
191-
}
192215

193-
if (!allParameters[p.in]) allParameters[p.in] = {};
194-
allParameters[p.in][p.name] = p;
195-
});
216+
Object.entries(methods).forEach(([method, operation]) => {
217+
// skip the parameters "method" for shared parameters - we'll handle it later
218+
if (method !== "parameters") {
219+
operation = operation as OpenAPI3Operation;
220+
if (operation.description) output += comment(operation.description);
221+
output += `"${method}": {\n`;
222+
223+
// handle operation parameters
224+
if (operation.parameters) {
225+
output += transformParameters(operation.parameters);
226+
}
196227

197-
Object.entries(allParameters).forEach(([loc, locParams]) => {
198-
output += `"${loc}": {\n`;
199-
Object.entries(locParams).forEach(([paramName, paramProps]) => {
200-
if (typeof paramProps === "string") {
201-
output += `"${paramName}": ${paramProps}\n`;
202-
return;
228+
// handle requestBody
229+
if (operation.requestBody) {
230+
output += `requestBody: {\n`;
231+
Object.entries(operation.requestBody.content).forEach(
232+
([contentType, { schema }]) => {
233+
output += `"${contentType}": ${transform(schema)};\n`;
203234
}
204-
if (paramProps.description)
205-
output += comment(paramProps.description);
206-
output += `"${paramName}"${
207-
paramProps.required === true ? "" : "?"
208-
}: ${transform(paramProps.schema)};\n`;
209-
});
235+
);
210236
output += `}\n`;
211-
});
212-
output += `}\n`;
213-
}
237+
}
214238

215-
// handle requestBody
216-
if (operation.requestBody) {
217-
output += `requestBody: {\n`;
218-
Object.entries(operation.requestBody.content).forEach(
219-
([contentType, { schema }]) => {
220-
output += `"${contentType}": ${transform(schema)};\n`;
239+
// handle responses
240+
output += `responses: {\n`;
241+
Object.entries(operation.responses).forEach(
242+
([statusCode, response]) => {
243+
if (response.description) output += comment(response.description);
244+
if (!response.content || !Object.keys(response.content).length) {
245+
const type =
246+
statusCode === "204" || Math.floor(+statusCode / 100) === 3
247+
? "never"
248+
: "unknown";
249+
output += `"${statusCode}": ${type};\n`;
250+
return;
251+
}
252+
output += `"${statusCode}": {\n`;
253+
Object.entries(response.content).forEach(
254+
([contentType, encodedResponse]) => {
255+
output += `"${contentType}": ${transform(
256+
encodedResponse.schema
257+
)};\n`;
258+
}
259+
);
260+
output += `}\n`;
221261
}
222262
);
223263
output += `}\n`;
264+
output += `}\n`;
224265
}
225-
226-
// handle responses
227-
output += `responses: {\n`;
228-
Object.entries(operation.responses).forEach(
229-
([statusCode, response]) => {
230-
if (response.description) output += comment(response.description);
231-
if (!response.content || !Object.keys(response.content).length) {
232-
const type =
233-
statusCode === "204" || Math.floor(+statusCode / 100) === 3
234-
? "never"
235-
: "unknown";
236-
output += `"${statusCode}": ${type};\n`;
237-
return;
238-
}
239-
output += `"${statusCode}": {\n`;
240-
Object.entries(response.content).forEach(
241-
([contentType, encodedResponse]) => {
242-
output += `"${contentType}": ${transform(
243-
encodedResponse.schema
244-
)};\n`;
245-
}
246-
);
247-
output += `}\n`;
248-
}
249-
);
250-
output += `}\n`;
251-
output += `}\n`;
252266
});
267+
268+
if (methods.parameters) {
269+
// Handle shared parameters
270+
output += transformParameters(methods.parameters as Parameter[]);
271+
}
253272
output += `}\n`;
254273
});
255274
return output;

tests/v3/index.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,4 +824,46 @@ describe("OpenAPI3 features", () => {
824824
`)
825825
);
826826
});
827+
it("parameters on entire path (#346)", () => {
828+
const schema: OpenAPI3 = {
829+
openapi: "3.0.1",
830+
paths: {
831+
"/{example}": {
832+
get: {
833+
responses: {},
834+
},
835+
parameters: [
836+
{
837+
name: "example",
838+
in: "path",
839+
required: true,
840+
schema: {
841+
type: "string"
842+
}
843+
},
844+
],
845+
},
846+
},
847+
};
848+
849+
expect(swaggerToTS(schema)).toEqual(
850+
format(`
851+
export interface paths {
852+
853+
"/{example}": {
854+
get: {
855+
responses: {};
856+
};
857+
parameters: {
858+
path: {
859+
example: string;
860+
};
861+
};
862+
};
863+
}
864+
865+
export interface components {}
866+
`)
867+
);
868+
});
827869
});

0 commit comments

Comments
 (0)