From 368c3520fa8a15657e8e42313bbfde3d87b0183b Mon Sep 17 00:00:00 2001 From: Karibash <40270352+Karibash@users.noreply.github.com> Date: Thu, 16 Nov 2023 19:24:53 +0900 Subject: [PATCH] =?UTF-8?q?fix(zod-openapi):=20Fix=20a=20bug=20that=20slas?= =?UTF-8?q?hes=20were=20duplicated=20when=20mountin=E2=80=A6=20(#258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(zod-openapi): Fix a bug that slashes were duplicated when mounting a path using the route method * fix: Make the paths be merged using internally defined utility functions instead --- .changeset/four-rice-listen.md | 5 + packages/zod-openapi/src/index.ts | 5 +- packages/zod-openapi/test/index.test.ts | 137 ++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 .changeset/four-rice-listen.md diff --git a/.changeset/four-rice-listen.md b/.changeset/four-rice-listen.md new file mode 100644 index 00000000..7a92038d --- /dev/null +++ b/.changeset/four-rice-listen.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +Fix a bug that slashes were duplicated when mounting a path using the route method diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 7178dffa..08f51a59 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -27,6 +27,7 @@ import type { } from 'hono' import type { MergePath, MergeSchemaPath } from 'hono/types' import type { RemoveBlankRecord } from 'hono/utils/types' +import { mergePath } from 'hono/utils/url' import type { AnyZodObject, ZodSchema, ZodError } from 'zod' import { z, ZodType } from 'zod' @@ -323,13 +324,13 @@ export class OpenAPIHono< case 'route': return this.openAPIRegistry.registerPath({ ...def.route, - path: `${path}${def.route.path}`, + path: mergePath(path, def.route.path), }) case 'webhook': return this.openAPIRegistry.registerWebhook({ ...def.webhook, - path: `${path}${def.webhook.path}`, + path: mergePath(path, def.webhook.path), }) case 'schema': diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index 48de0cb5..de304784 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -977,3 +977,140 @@ describe('It allows the response type to be Response', () => { expect(res.body).toBe(null) }) }) + +describe('Path normalization', () => { + const createRootApp = () => { + const app = new OpenAPIHono() + app.doc('/doc', { + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'My API', + }, + }) + return app + } + + const generateRoute = (path: string) => { + return createRoute({ + path, + method: 'get', + responses: { + 204: { + description: 'No Content', + }, + }, + }) + } + + const handler = (c) => c.body(null, 204) + + describe('Duplicate slashes in the root path', () => { + const app = createRootApp() + const childApp = new OpenAPIHono() + + childApp.openapi(generateRoute('/child'), handler) + app.route('/', childApp) + + it('Should remove duplicate slashes', async () => { + const res = await app.request('/doc') + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'My API', + }, + components: { + schemas: {}, + parameters: {}, + }, + paths: { + '/child': { + get: { + responses: { + 204: { + description: 'No Content', + }, + }, + }, + }, + }, + }) + }) + }) + + describe('Duplicate slashes in the child path', () => { + const app = createRootApp() + const childApp = new OpenAPIHono() + const grandchildApp = new OpenAPIHono() + + grandchildApp.openapi(generateRoute('/granchild'), handler) + childApp.route('/', grandchildApp) + app.route('/api', childApp) + + it('Should remove duplicate slashes', async () => { + const res = await app.request('/doc') + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'My API', + }, + components: { + schemas: {}, + parameters: {}, + }, + paths: { + '/api/granchild': { + get: { + responses: { + 204: { + description: 'No Content', + }, + }, + }, + }, + }, + }) + }) + }) + + describe('Duplicate slashes in the trailing path', () => { + const app = createRootApp() + const childApp = new OpenAPIHono() + const grandchildApp = new OpenAPIHono() + + grandchildApp.openapi(generateRoute('/'), handler) + childApp.route('/', grandchildApp) + app.route('/api', childApp) + + it('Should remove duplicate slashes', async () => { + const res = await app.request('/doc') + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ + openapi: '3.0.0', + info: { + version: '1.0.0', + title: 'My API', + }, + components: { + schemas: {}, + parameters: {}, + }, + paths: { + '/api': { + get: { + responses: { + 204: { + description: 'No Content', + }, + }, + }, + }, + }, + }) + }) + }) +})