-
Notifications
You must be signed in to change notification settings - Fork 51
feat: transform path segements to PascalCase in generateOperationId #214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -176,4 +176,116 @@ describe("zod v3", () => { | |
|
|
||
| expect(specs).toMatchSnapshot(); | ||
| }); | ||
|
|
||
| it("operation id for path with dash", async () => { | ||
| const query = z.object({ | ||
| limit: z.coerce.number().default(10), | ||
| offset: z.coerce.number().default(0), | ||
| }); | ||
|
|
||
| const param = z | ||
| .object({ | ||
| id: z.coerce.number(), | ||
| }) | ||
| .openapi({ ref: "Param" }); | ||
|
|
||
| const app = new Hono().get( | ||
| "/some-path", | ||
| describeRoute({ | ||
| tags: ["test"], | ||
| summary: "Test route", | ||
| description: "This is a test route", | ||
| }), | ||
| validator("param", param), | ||
| validator("query", query), | ||
|
Comment on lines
+186
to
+200
|
||
| describeResponse( | ||
| async (c) => { | ||
| c.req.valid("query"); | ||
| c.req.valid("param"); | ||
|
|
||
| return c.json({ | ||
| result: [], | ||
| count: 0, | ||
| }); | ||
| }, | ||
| { | ||
| 200: { | ||
| description: "Success", | ||
| content: { | ||
| "application/json": { | ||
| vSchema: z.object({ | ||
| result: z.array(z.string()), | ||
| count: z.number(), | ||
| }), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| ), | ||
| ); | ||
|
|
||
| const specs = await generateSpecs(app); | ||
|
|
||
| expect(specs).toMatchSnapshot(); | ||
| }); | ||
|
Comment on lines
+180
to
+230
|
||
|
|
||
| it("operation id for path with underscore", async () => { | ||
| const app = new Hono().get( | ||
| "/api_v1/users", | ||
| describeRoute({ | ||
| tags: ["test"], | ||
| summary: "Test route", | ||
| description: "This is a test route", | ||
| }), | ||
| async (c) => { | ||
| return c.json({ message: "Hello" }); | ||
| }, | ||
| ); | ||
|
|
||
| const specs = await generateSpecs(app); | ||
|
|
||
| expect(specs.paths?.["/api_v1/users"]?.get?.operationId).toBe( | ||
| "getApiV1Users", | ||
| ); | ||
| }); | ||
|
|
||
| it("operation id for path param with dash", async () => { | ||
| const app = new Hono().get( | ||
| "/users/:user-id", | ||
| describeRoute({ | ||
| tags: ["test"], | ||
| summary: "Test route", | ||
| description: "This is a test route", | ||
| }), | ||
| async (c) => { | ||
| return c.json({ message: "Hello" }); | ||
| }, | ||
| ); | ||
|
|
||
| const specs = await generateSpecs(app); | ||
|
|
||
| expect(specs.paths?.["/users/{user-id}"]?.get?.operationId).toBe( | ||
| "getUsersByUserId", | ||
| ); | ||
| }); | ||
|
|
||
| it("operation id for multiple dashed segments", async () => { | ||
| const app = new Hono().get( | ||
| "/api-v1/user-profile", | ||
| describeRoute({ | ||
| tags: ["test"], | ||
| summary: "Test route", | ||
| description: "This is a test route", | ||
| }), | ||
| async (c) => { | ||
| return c.json({ message: "Hello" }); | ||
| }, | ||
| ); | ||
|
|
||
| const specs = await generateSpecs(app); | ||
|
|
||
| expect(specs.paths?.["/api-v1/user-profile"]?.get?.operationId).toBe( | ||
| "getApiV1UserProfile", | ||
| ); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,8 +47,12 @@ const toOpenAPIPathSegment = (segment: string) => { | |
| const toOpenAPIPath = (path: string) => | ||
| path.split("/").map(toOpenAPIPathSegment).join("/"); | ||
|
|
||
| const capitalize = (word: string) => | ||
| word.charAt(0).toUpperCase() + word.slice(1); | ||
| const toPascalCase = (text: string) => | ||
| text | ||
| .split(/[\W_]+/) | ||
| .filter(Boolean) | ||
| .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) | ||
| .join(""); | ||
|
|
||
| const generateOperationId = (route: RouterRoute) => { | ||
|
Comment on lines
+50
to
57
|
||
| let operationId = route.method.toLowerCase(); | ||
|
|
@@ -58,9 +62,9 @@ const generateOperationId = (route: RouterRoute) => { | |
| for (const segment of route.path.split("/")) { | ||
| const openApiPathSegment = toOpenAPIPathSegment(segment); | ||
| if (openApiPathSegment.charCodeAt(0) === 123) { | ||
| operationId += `By${capitalize(openApiPathSegment.slice(1, -1))}`; | ||
| operationId += `By${toPascalCase(openApiPathSegment.slice(1, -1))}`; | ||
| } else { | ||
| operationId += capitalize(openApiPathSegment); | ||
| operationId += toPascalCase(openApiPathSegment); | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test defines a path parameter validator but the route path '/some-path' doesn't contain any path parameters. The param validator at lines 186-190 expects an 'id' path parameter, but there's no ':id' in the route path. This appears to be copy-pasted from the 'describeResponse' test above. Either remove the param validator or change the path to include a path parameter like '/:id/some-path' to properly test the dash handling in both static and dynamic path segments.