From 3f63c46fa66bfb7f1d80174bfb160cccfa69f0bc Mon Sep 17 00:00:00 2001 From: Jani <9337138+jstri@users.noreply.github.com> Date: Fri, 29 Nov 2024 13:07:33 +0100 Subject: [PATCH] fix(zod-openapi): support default response (#855) * fix(zod-openapi): support default response * chore: changeset --- .changeset/curly-eagles-relate.md | 5 ++ packages/zod-openapi/src/index.ts | 30 +++++--- packages/zod-openapi/test/handler.test-d.ts | 82 +++++++++++++++++++++ 3 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 .changeset/curly-eagles-relate.md diff --git a/.changeset/curly-eagles-relate.md b/.changeset/curly-eagles-relate.md new file mode 100644 index 00000000..56b2edb8 --- /dev/null +++ b/.changeset/curly-eagles-relate.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +fix: support default response diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 62cb592f..58ee6e55 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -194,16 +194,26 @@ type RouteConfigStatusCode = keyof StatusCodeRangeDefinitions | StatusCode type ExtractStatusCode = T extends keyof StatusCodeRangeDefinitions ? StatusCodeRangeDefinitions[T] : T -export type RouteConfigToTypedResponse = { - [Status in keyof R['responses'] & - RouteConfigStatusCode]: undefined extends R['responses'][Status]['content'] - ? TypedResponse<{}, ExtractStatusCode, string> - : ReturnJsonOrTextOrResponse< - keyof R['responses'][Status]['content'], - ExtractContent, - Status - > -}[keyof R['responses'] & RouteConfigStatusCode] +type DefinedStatusCodes = keyof R['responses'] & RouteConfigStatusCode +export type RouteConfigToTypedResponse = + | { + [Status in DefinedStatusCodes]: undefined extends R['responses'][Status]['content'] + ? TypedResponse<{}, ExtractStatusCode, string> + : ReturnJsonOrTextOrResponse< + keyof R['responses'][Status]['content'], + ExtractContent, + Status + > + }[DefinedStatusCodes] + | ('default' extends keyof R['responses'] + ? undefined extends R['responses']['default']['content'] + ? TypedResponse<{}, Exclude>>, string> + : ReturnJsonOrTextOrResponse< + keyof R['responses']['default']['content'], + ExtractContent, + Exclude>> + > + : never) export type Hook = ( result: { target: keyof ValidationTargets } & ( diff --git a/packages/zod-openapi/test/handler.test-d.ts b/packages/zod-openapi/test/handler.test-d.ts index f8b56172..c79843e3 100644 --- a/packages/zod-openapi/test/handler.test-d.ts +++ b/packages/zod-openapi/test/handler.test-d.ts @@ -147,4 +147,86 @@ describe('supports async handler', () => { const hono = new OpenAPIHono() hono.openapi(route, handler) }) + + test('handler should support default response for unspecified status codes', () => { + const routeWithDefault = createRoute({ + method: 'get', + path: '/users', + responses: { + 200: { + content: { + 'application/json': { + schema: z.object({ id: z.string() }), + }, + }, + description: 'Success response', + }, + default: { + content: { + 'application/json': { + schema: z.object({ error: z.string() }), + }, + }, + description: 'Error response', + }, + }, + }) + + const errorHandler: RouteHandler = (c) => { + return c.json({ error: 'Server Error' }, 500) + } + + const hono = new OpenAPIHono() + hono.openapi(routeWithDefault, errorHandler) + }) + + test('handler should respect explicitly defined status codes with default fallback', () => { + const routeWithDefault = createRoute({ + method: 'get', + path: '/users', + responses: { + 200: { + content: { + 'application/json': { + schema: z.object({ id: z.string() }), + }, + }, + description: 'Success response', + }, + 404: { + content: { + 'application/json': { + schema: z.object({ message: z.string() }), + }, + }, + description: 'Not found response', + }, + default: { + content: { + 'application/json': { + schema: z.object({ error: z.string() }), + }, + }, + description: 'Error response', + }, + }, + }) + + const successHandler: RouteHandler = (c) => { + return c.json({ id: '123' }, 200) + } + + const notFoundHandler: RouteHandler = (c) => { + return c.json({ message: 'Not Found' }, 404) + } + + const errorHandler: RouteHandler = (c) => { + return c.json({ error: 'Server Error' }, 500) + } + + const hono = new OpenAPIHono() + hono.openapi(routeWithDefault, successHandler) + hono.openapi(routeWithDefault, notFoundHandler) + hono.openapi(routeWithDefault, errorHandler) + }) })