Skip to content

Commit

Permalink
Merge pull request #224 from asteasolutions/allow-effects-on-objects-…
Browse files Browse the repository at this point in the history
…in-request

Allow effects on objects in request
  • Loading branch information
AGalabov authored Apr 5, 2024
2 parents cc5ca8e + d4dad12 commit 86905f0
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 67 deletions.
48 changes: 24 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 91 additions & 13 deletions spec/routes/parameters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,41 @@ describe('parameters', () => {
]);
});

it('generates a deepPartial object query parameter for route', () => {
it('generates query parameter for route from object with refine', () => {
const { parameters } = generateDataForRoute({
request: {
query: z
.object({
filter: z
.object({ test: z.string() })
.openapi({ param: { style: 'deepObject' } }),
filter: z.string(),
})
.deepPartial(),
.refine(({ filter }) => filter.length > 3),
},
});

expect(parameters).toEqual([
{
in: 'query',
name: 'filter',
required: false,
style: 'deepObject',
required: true,
schema: {
type: 'object',
properties: {
test: {
type: 'string',
},
},
type: 'string',
},
},
]);
});

it('generates a query parameter for route', () => {
const { parameters } = generateDataForRoute({
request: { query: z.object({ test: z.string() }) },
});

expect(parameters).toEqual([
{
in: 'query',
name: 'test',
required: true,
schema: {
type: 'string',
},
},
]);
Expand Down Expand Up @@ -133,6 +142,29 @@ describe('parameters', () => {
]);
});

it('generates path parameter for route from object with refine', () => {
const { parameters } = generateDataForRoute({
request: {
params: z
.object({
filter: z.string(),
})
.refine(({ filter }) => filter.length > 3),
},
});

expect(parameters).toEqual([
{
in: 'path',
name: 'filter',
required: true,
schema: {
type: 'string',
},
},
]);
});

it('generates a reference path parameter for route', () => {
const TestParam = registerParameter(
'TestParam',
Expand Down Expand Up @@ -212,6 +244,29 @@ describe('parameters', () => {
]);
});

it('generates cookie parameter for route from object with refine', () => {
const { parameters } = generateDataForRoute({
request: {
cookies: z
.object({
filter: z.string(),
})
.refine(({ filter }) => filter.length > 3),
},
});

expect(parameters).toEqual([
{
in: 'cookie',
name: 'filter',
required: true,
schema: {
type: 'string',
},
},
]);
});

it('generates a reference cookie parameter for route', () => {
const TestParam = registerParameter(
'TestParam',
Expand Down Expand Up @@ -312,6 +367,29 @@ describe('parameters', () => {
]);
});

it('generates header parameter for route from object with refine', () => {
const { parameters } = generateDataForRoute({
request: {
headers: z
.object({
filter: z.string(),
})
.refine(({ filter }) => filter.length > 3),
},
});

expect(parameters).toEqual([
{
in: 'header',
name: 'filter',
required: true,
schema: {
type: 'string',
},
},
]);
});

it('generates a reference header parameter for route', () => {
const TestHeader = registerParameter(
'TestHeader',
Expand Down
53 changes: 28 additions & 25 deletions src/openapi-generator.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
import type {
AnyZodObject,
ZodRawShape,
ZodTuple,
ZodType,
ZodTypeAny,
} from 'zod';
import type { AnyZodObject, ZodRawShape, ZodType, ZodTypeAny } from 'zod';
import {
ConflictError,
MissingParameterDataError,
enhanceMissingParametersError,
} from './errors';
import {
compact,
isNil,
mapValues,
objectEquals,
omit,
omitBy,
} from './lib/lodash';
import { compact, isNil, mapValues, objectEquals, omitBy } from './lib/lodash';
import { isAnyZodType, isZodType } from './lib/zod-is-type';
import {
OpenAPIComponentObject,
OpenAPIDefinitions,
ResponseConfig,
RouteConfig,
RouteParameter,
ZodContentObject,
ZodRequestBody,
} from './openapi-registry';
import { ZodOpenApiFullMetadata, ZodOpenAPIMetadata } from './zod-extensions';
import { ZodOpenApiFullMetadata } from './zod-extensions';
import {
BaseParameterObject,
ComponentsObject,
Expand Down Expand Up @@ -513,7 +501,11 @@ export class OpenAPIGenerator {
return [];
}

const { query, params, headers, cookies } = request;
const { headers } = request;

const query = this.cleanParameter(request.query);
const params = this.cleanParameter(request.params);
const cookies = this.cleanParameter(request.cookies);

const queryParameters = enhanceMissingParametersError(
() => (query ? this.generateInlineParameters(query, 'query') : []),
Expand All @@ -531,14 +523,17 @@ export class OpenAPIGenerator {
);

const headerParameters = enhanceMissingParametersError(
() =>
headers
? isZodType(headers, 'ZodObject')
? this.generateInlineParameters(headers, 'header')
: headers.flatMap(header =>
this.generateInlineParameters(header, 'header')
)
: [],
() => {
if (Array.isArray(headers)) {
return headers.flatMap(header =>
this.generateInlineParameters(header, 'header')
);
}
const cleanHeaders = this.cleanParameter(headers);
return cleanHeaders
? this.generateInlineParameters(cleanHeaders, 'header')
: [];
},
{ location: 'header' }
);

Expand All @@ -550,6 +545,14 @@ export class OpenAPIGenerator {
];
}

private cleanParameter(schema: RouteParameter) {
if (!schema) {
return undefined;
}

return isZodType(schema, 'ZodEffects') ? schema._def.schema : schema;
}

generatePath(route: RouteConfig): PathItemObject {
const { method, path, request, responses, ...pathItemConfig } = route;

Expand Down
15 changes: 10 additions & 5 deletions src/openapi-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type ResponseObject = ResponseObject30 | ResponseObject31;
type SchemaObject = SchemaObject30 | SchemaObject31;
type SecuritySchemeObject = SecuritySchemeObject30 | SecuritySchemeObject31;

import type { AnyZodObject, ZodType, ZodTypeAny } from 'zod';
import type { AnyZodObject, ZodEffects, ZodType, ZodTypeAny } from 'zod';

type Method =
| 'get'
Expand Down Expand Up @@ -102,15 +102,20 @@ export interface ResponseConfig {
content?: ZodContentObject;
}

export type RouteParameter =
| AnyZodObject
| ZodEffects<AnyZodObject, unknown, unknown>
| undefined;

export type RouteConfig = Omit<OperationObject, 'responses'> & {
method: Method;
path: string;
request?: {
body?: ZodRequestBody;
params?: AnyZodObject;
query?: AnyZodObject;
cookies?: AnyZodObject;
headers?: AnyZodObject | ZodType<unknown>[];
params?: RouteParameter;
query?: RouteParameter;
cookies?: RouteParameter;
headers?: RouteParameter | ZodType<unknown>[];
};
responses: {
[statusCode: string]: ResponseConfig;
Expand Down

0 comments on commit 86905f0

Please sign in to comment.