From c3d48868003ebd215074777a4846af208ddab123 Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Sun, 11 Aug 2024 11:33:09 +0900 Subject: [PATCH] fix(zod-openapi): supports `required` for JSON and Form body (#689) * fix(zod-openapi): supports `required` for JSON and Form body * changeset --- .changeset/twelve-singers-watch.md | 5 ++ packages/zod-openapi/src/index.ts | 36 ++++++++----- packages/zod-openapi/test/index.test.ts | 68 ++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 .changeset/twelve-singers-watch.md diff --git a/.changeset/twelve-singers-watch.md b/.changeset/twelve-singers-watch.md new file mode 100644 index 00000000..a15dbdd1 --- /dev/null +++ b/.changeset/twelve-singers-watch.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +fix: supports `required` for JSON and Form body diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 8ba1a585..1b3df225 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -392,29 +392,37 @@ export class OpenAPIHono< } if (isJSONContentType(mediaType)) { const validator = zValidator('json', schema, hook as any) - const mw: MiddlewareHandler = async (c, next) => { - if (c.req.header('content-type')) { - if (isJSONContentType(c.req.header('content-type')!)) { - return await validator(c, next) + if (route.request?.body?.required) { + validators.push(validator) + } else { + const mw: MiddlewareHandler = async (c, next) => { + if (c.req.header('content-type')) { + if (isJSONContentType(c.req.header('content-type')!)) { + return await validator(c, next) + } } + c.req.addValidatedData('json', {}) + await next() } - c.req.addValidatedData('json', {}) - await next() + validators.push(mw) } - validators.push(mw) } if (isFormContentType(mediaType)) { const validator = zValidator('form', schema, hook as any) - const mw: MiddlewareHandler = async (c, next) => { - if (c.req.header('content-type')) { - if (isFormContentType(c.req.header('content-type')!)) { - return await validator(c, next) + if (route.request?.body?.required) { + validators.push(validator) + } else { + const mw: MiddlewareHandler = async (c, next) => { + if (c.req.header('content-type')) { + if (isFormContentType(c.req.header('content-type')!)) { + return await validator(c, next) + } } + c.req.addValidatedData('form', {}) + await next() } - c.req.addValidatedData('form', {}) - await next() + validators.push(mw) } - validators.push(mw) } } } diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index 15fed599..3c819488 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -579,8 +579,41 @@ describe('JSON', () => { expect(res.status).toBe(400) }) }) + + describe('required', () => { + const route = createRoute({ + method: 'post', + path: '/posts', + request: { + body: { + content: { + 'application/json': { + schema: RequestSchema, + }, + }, + required: true, + }, + }, + responses: { + 200: { + description: 'Post a post', + }, + }, + }) + const app = new OpenAPIHono() + app.openapi(route, (c) => { + return c.text('Post success') + }) + + it('Should return 400 response since body is required', async () => { + const res = await app.request('/posts', { + method: 'POST', + }) + expect(res.status).toBe(400) + }) + }) }) -// application/vnd.api+json + describe('Form', () => { const RequestSchema = z.object({ id: z.string().openapi({}), @@ -656,6 +689,39 @@ describe('Form', () => { const res = await app.request(req) expect(res.status).toBe(200) }) + + describe('required', () => { + const route = createRoute({ + method: 'post', + path: '/posts', + request: { + body: { + content: { + 'application/x-www-form-urlencoded': { + schema: RequestSchema, + }, + }, + required: true, + }, + }, + responses: { + 200: { + description: 'Post a post', + }, + }, + }) + const app = new OpenAPIHono() + app.openapi(route, (c) => { + return c.text('Post success') + }) + + it('Should return 400 response since body is required', async () => { + const res = await app.request('/posts', { + method: 'POST', + }) + expect(res.status).toBe(400) + }) + }) }) describe('JSON and Form', () => {