Skip to content

Commit

Permalink
fix(type-safe-api): generate models for inline request body schemas
Browse files Browse the repository at this point in the history
Inline request body schemas were typed as `any` and no model was generated for them.

Address this by hoisting inline request schemas in the same way we do for responses.

Note that OpenAPI generator named these hoisted inline request models `<OperationID>Request` which
also clashes with the overall operation request model - and resolved it by appending `Operation` to
the Operation ID (see #789).

We deviate from OpenAPI generator behaviour to avoid this issue recurring by naming the type
`<OperationID>RequestContent`.
  • Loading branch information
cogwirrel committed Oct 24, 2024
1 parent ab61148 commit 1df132d
Show file tree
Hide file tree
Showing 5 changed files with 1,757 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -718,21 +718,34 @@ const buildData = async (inSpec: OpenAPIV3.Document, metadata: any) => {
};
}

// "Hoist" inline response schemas
// "Hoist" inline request and response schemas
Object.entries(spec.paths ?? {}).forEach(([path, pathOps]) => Object.entries(pathOps ?? {}).forEach(([method, op]) => {
const operation = resolveIfRef(spec, op);
if (operation && typeof operation === "object" && "responses" in operation) {
Object.entries(operation.responses ?? {}).forEach(([code, res]) => {
const response = resolveIfRef(spec, res);
const jsonResponseSchema = response?.content?.['application/json']?.schema;
if (jsonResponseSchema && !isRef(jsonResponseSchema) && ["object", "array"].includes(jsonResponseSchema.type!)) {
const schemaName = `${_upperFirst(_camelCase(operation.operationId ?? `${path}-${method}`))}${code}Response`;
spec.components!.schemas![schemaName] = jsonResponseSchema;
response!.content!['application/json'].schema = {
$ref: `#/components/schemas/${schemaName}`,
};
if (operation && typeof operation === "object") {
if ("responses" in operation) {
Object.entries(operation.responses ?? {}).forEach(([code, res]) => {
const response = resolveIfRef(spec, res);
const jsonResponseSchema = response?.content?.['application/json']?.schema;
if (jsonResponseSchema && !isRef(jsonResponseSchema) && ["object", "array"].includes(jsonResponseSchema.type!)) {
const schemaName = `${_upperFirst(_camelCase(operation.operationId ?? `${path}-${method}`))}${code}Response`;
spec.components!.schemas![schemaName] = jsonResponseSchema;
response!.content!['application/json'].schema = {
$ref: `#/components/schemas/${schemaName}`,
};
}
});
}
if ("requestBody" in operation) {
const requestBody = resolveIfRef(spec, operation.requestBody);
const jsonRequestSchema = requestBody?.content?.['application/json']?.schema;
if (jsonRequestSchema && !isRef(jsonRequestSchema) && ["object", "array"].includes(jsonRequestSchema.type!)) {
const schemaName = `${_upperFirst(_camelCase(operation.operationId ?? `${path}-${method}`))}RequestContent`;
spec.components!.schemas![schemaName] = jsonRequestSchema;
requestBody!.content!['application/json'].schema = {
$ref: `#/components/schemas/${schemaName}`,
};
}
});
}
}
}));

Expand Down
17 changes: 17 additions & 0 deletions packages/type-safe-api/test/resources/specs/edge-cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,23 @@ paths:
enum:
- fruit
- vegetable
/inline-request-body:
post:
operationId: inlineRequestBody
responses:
204:
description: ok
requestBody:
content:
application/json:
schema:
type: object
properties:
someProperty:
type:
string
required:
- someProperty
components:
schemas:
MyEnum:
Expand Down
Loading

0 comments on commit 1df132d

Please sign in to comment.