diff --git a/spec/modifiers/describe.spec.ts b/spec/modifiers/describe.spec.ts index 7bb448a..34e2dc5 100644 --- a/spec/modifiers/describe.spec.ts +++ b/spec/modifiers/describe.spec.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { expectSchema } from '../lib/helpers'; +import { expectSchema, generateDataForRoute } from '../lib/helpers'; describe('describe', () => { it('generates OpenAPI schema with description when the .describe method is used', () => { @@ -78,4 +78,78 @@ describe('describe', () => { }, }); }); + + it('generates an optional query parameter with a provided description', () => { + const { parameters } = generateDataForRoute({ + request: { + query: z.object({ + test: z.string().optional().describe('Some parameter'), + }), + }, + }); + + expect(parameters).toEqual([ + { + in: 'query', + name: 'test', + description: 'Some parameter', + required: false, + schema: { + description: 'Some parameter', + type: 'string', + }, + }, + ]); + }); + + it('generates a query parameter with a description made optional', () => { + const { parameters } = generateDataForRoute({ + request: { + query: z.object({ + test: z.string().describe('Some parameter').optional(), + }), + }, + }); + + expect(parameters).toEqual([ + { + in: 'query', + name: 'test', + description: 'Some parameter', + required: false, + schema: { + description: 'Some parameter', + type: 'string', + }, + }, + ]); + }); + + it('generates a query parameter with description from a registered schema', () => { + const schema = z.string().describe('Some parameter').openapi('SomeString'); + const { parameters, documentSchemas } = generateDataForRoute({ + request: { + query: z.object({ test: schema }), + }, + }); + + expect(documentSchemas).toEqual({ + SomeString: { + type: 'string', + description: 'Some parameter', + }, + }); + + expect(parameters).toEqual([ + { + in: 'query', + name: 'test', + description: 'Some parameter', + required: true, + schema: { + $ref: '#/components/schemas/SomeString', + }, + }, + ]); + }); }); diff --git a/src/openapi-generator.ts b/src/openapi-generator.ts index 964768d..5af5dc8 100644 --- a/src/openapi-generator.ts +++ b/src/openapi-generator.ts @@ -359,7 +359,7 @@ export class OpenAPIGenerator { } private generateSimpleParameter(zodSchema: ZodTypeAny): BaseParameterObject { - const metadata = this.getMetadata(zodSchema); + const metadata = this.getParamMetadata(zodSchema); const paramMetadata = metadata?.metadata?.param; const required = @@ -1222,6 +1222,37 @@ export class OpenAPIGenerator { return omitBy(metadata, isNil); } + private getParamMetadata( + zodSchema: ZodType + ): ZodOpenApiFullMetadata | undefined { + const innerSchema = this.unwrapChained(zodSchema); + + const metadata = zodSchema._def.openapi + ? zodSchema._def.openapi + : innerSchema._def.openapi; + + /** + * Every zod schema can receive a `description` by using the .describe method. + * That description should be used when generating an OpenApi schema. + * The `??` bellow makes sure we can handle both: + * - schema.describe('Test').optional() + * - schema.optional().describe('Test') + */ + const zodDescription = zodSchema.description ?? innerSchema.description; + + return { + _internal: metadata?._internal, + metadata: { + ...metadata?.metadata, + // A description provided from .openapi() should be taken with higher precedence + param: { + description: zodDescription, + ...metadata?.metadata.param, + }, + }, + }; + } + private getMetadata( zodSchema: ZodType ): ZodOpenApiFullMetadata | undefined {