Skip to content

Commit

Permalink
feat: support allOf for query params in toOpenApiSpec
Browse files Browse the repository at this point in the history
  • Loading branch information
SchaeStewart committed Mar 22, 2024
1 parent 4480b32 commit 7ac7acc
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 10 deletions.
132 changes: 132 additions & 0 deletions src/openapi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,4 +313,136 @@ describe('toOpenAPISpec', () => {
},
});
});
test("handles spec with 'allOf' query parameters", () => {
const spec: OneSchemaDefinition = withAssumptions(
{
Resources: {
Post: {
type: 'object',
properties: {
id: { type: 'number' },
message: { type: 'string' },
},
},
},
Endpoints: {
'GET /posts/:id': {
Name: 'getPostByIdWithParams',
Request: {
allOf: [
{
type: 'object',
properties: {
project: {
type: 'string',
},
},
required: ['project'],
},
{
type: 'object',
properties: {
count: {
type: 'string',
},
},
},
{
type: 'string', // handles a code coverage for a non-object allOf entry
},
],
},
Response: {
$ref: '#/definitions/Post',
},
},
},
},
{
objectPropertiesRequiredByDefault: false,
noAdditionalPropertiesOnObjects: true,
},
);
const result = toOpenAPISpec(spec, {
info: { title: 'test title', version: '1.2.3' },
});

// Ensure result is a valid OpenAPI spec
const { errors } = new OpenAPIValidator({
version: '3.0.0',
}).validate(result);

expect(errors).toHaveLength(0);

console.log(JSON.stringify(result));

expect(result).toStrictEqual({
openapi: '3.0.0',
info: {
title: 'test title',
version: '1.2.3',
},
components: {
schemas: {
Post: {
additionalProperties: false,
properties: {
id: {
type: 'number',
},
message: {
type: 'string',
},
},
type: 'object',
},
},
},
paths: {
'/posts/{id}': {
get: {
operationId: 'getPostByIdWithParams',
parameters: [
{
in: 'path',
name: 'id',
required: true,
schema: {
type: 'string',
},
},
{
in: 'query',
name: 'project',
required: true,
schema: {
type: 'string',
},
},
{
in: 'query',
name: 'count',
required: false,
schema: {
type: 'string',
},
},
],
responses: {
'200': {
content: {
'application/json': {
schema: {
$ref: '#/components/schemas/Post',
},
},
},
description: 'A successful response',
},
},
},
},
},
});
});
});
35 changes: 25 additions & 10 deletions src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { OpenAPIV3 } from 'openapi-types';
import type { OneSchemaDefinition } from './types';
import { deepCopy } from './generate-endpoints';
import { validateSchema } from './meta-schema';
import { JSONSchema4 } from 'json-schema';

/**
* Converts e.g. `/users/:id/profile` to `/users/{id}/profile`.
Expand All @@ -18,6 +19,18 @@ const getPathParameters = (koaPath: string) =>
.filter((part) => part.startsWith(':'))
.map((part) => part.slice(1));

const toQueryParam = (
item: JSONSchema4,
name: string,
schema: JSONSchema4,
) => ({
in: 'query',
name,
description: schema.description,
schema: schema,
required: Array.isArray(item.required) && item.required.includes(name),
});

export const toOpenAPISpec = (
schema: OneSchemaDefinition,
config: {
Expand Down Expand Up @@ -82,19 +95,21 @@ export const toOpenAPISpec = (
if (Request) {
if (['GET', 'DELETE'].includes(method)) {
// Add the query parameters for GET/DELETE methods
for (const [name, schema] of Object.entries(Request.properties ?? {}))
parameters.push({
in: 'query',
name,
description: schema.description,
for (const [name, schema] of Object.entries(Request.properties ?? {})) {
// @ts-expect-error TS detects a mismatch between the JSONSchema types
// between openapi-types and json-schema. Ignore and assume everything
// is cool.
parameters.push(toQueryParam(Request, name, schema));
}

for (const item of Request.allOf ?? []) {
for (const [name, schema] of Object.entries(item.properties ?? {})) {
// @ts-expect-error TS detects a mismatch between the JSONSchema types
// between openapi-types and json-schema. Ignore and assume everything
// is cool.
schema: schema,
required:
Array.isArray(Request.required) &&
Request.required.includes(name),
});
parameters.push(toQueryParam(item, name, schema));
}
}
} else {
// Add the body spec parameters for non-GET/DELETE methods
operation.requestBody = {
Expand Down

0 comments on commit 7ac7acc

Please sign in to comment.