From 3e85496cec022c48d9e74d2023327f957b554276 Mon Sep 17 00:00:00 2001 From: "K.Himeno" <6715229+Himenon@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:01:33 +0900 Subject: [PATCH 1/3] feat: Fall back to an alternate string if operationId is undefined. --- src/generateValidRootSchema.ts | 16 ++++++++++++++++ src/index.ts | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/generateValidRootSchema.ts diff --git a/src/generateValidRootSchema.ts b/src/generateValidRootSchema.ts new file mode 100644 index 0000000..b8aa675 --- /dev/null +++ b/src/generateValidRootSchema.ts @@ -0,0 +1,16 @@ +import type * as Types from "./types"; + +export const generateValidRootSchema = (input: Types.OpenApi.Document): Types.OpenApi.Document => { + if (!input.paths) { + return input; + } + /** update undefined operation id */ + for (const [path, methods] of Object.entries(input.paths || {})) { + for (const [method, operation] of Object.entries(methods || {})) { + if (!operation.operationId) { + operation.operationId = `${method.toLowerCase()}${path.charAt(0).toUpperCase() + path.slice(1)}`; + } + } + } + return input; +}; diff --git a/src/index.ts b/src/index.ts index 26d83eb..fa6d0c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import { EOL } from "os"; import * as Api from "./api"; import type * as Types from "./types"; +import { generateValidRootSchema } from "./generateValidRootSchema"; export interface Option { allowOperationIds?: string[]; @@ -17,7 +18,7 @@ export class CodeGenerator { private option?: Option, ) { if (typeof entryPointOrDocument === "string") { - this.rootSchema = Api.FileSystem.loadJsonOrYaml(entryPointOrDocument); + this.rootSchema = generateValidRootSchema(Api.FileSystem.loadJsonOrYaml(entryPointOrDocument)); this.resolvedReferenceDocument = Api.ResolveReference.resolve( entryPointOrDocument, entryPointOrDocument, From 3f39df08f942b2927033165cec8b6366415be28a Mon Sep 17 00:00:00 2001 From: "K.Himeno" <6715229+Himenon@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:08:11 +0900 Subject: [PATCH 2/3] fix: ref --- src/generateValidRootSchema.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/generateValidRootSchema.ts b/src/generateValidRootSchema.ts index b8aa675..45b27bc 100644 --- a/src/generateValidRootSchema.ts +++ b/src/generateValidRootSchema.ts @@ -6,7 +6,24 @@ export const generateValidRootSchema = (input: Types.OpenApi.Document): Types.Op } /** update undefined operation id */ for (const [path, methods] of Object.entries(input.paths || {})) { - for (const [method, operation] of Object.entries(methods || {})) { + const targets = { + get: methods.get, + put: methods.put, + post: methods.post, + delete: methods.delete, + options: methods.options, + head: methods.head, + patch: methods.patch, + trace: methods.trace, + } satisfies Record; + for (const [method, operation] of Object.entries(targets)) { + if (!operation) { + continue; + } + // skip reference object + if ("$ref" in operation) { + continue; + } if (!operation.operationId) { operation.operationId = `${method.toLowerCase()}${path.charAt(0).toUpperCase() + path.slice(1)}`; } From b5947a742bca3be83b3793e94a732961e039fd22 Mon Sep 17 00:00:00 2001 From: "K.Himeno" <6715229+Himenon@users.noreply.github.com> Date: Mon, 30 Jun 2025 12:12:29 +0900 Subject: [PATCH 3/3] test: add undefined pattern --- .../typedef-with-template-test.ts.snap | 23 +++++++++++++++++-- .../typedef-with-template-test.ts.snap | 23 +++++++++++++++++-- test/api.v2.domain/index.yml | 13 +++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/test/__tests__/class/__snapshots__/typedef-with-template-test.ts.snap b/test/__tests__/class/__snapshots__/typedef-with-template-test.ts.snap index 62a9e4a..78ad58f 100644 --- a/test/__tests__/class/__snapshots__/typedef-with-template-test.ts.snap +++ b/test/__tests__/class/__snapshots__/typedef-with-template-test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Typedef with template api.test.domain 1`] = ` "// @@ -572,6 +572,12 @@ export interface Response$postHelloWorldReadonly$Status$200 { message?: Schemas.Message; }; } +export interface Response$get$undefined$operation$$id$Status$200 { + "application/json": { + /** Undefined operation response */ + message?: string; + }; +} export type ResponseContentType$getHelloWorld = keyof Response$getHelloWorld$Status$200; export interface Params$getHelloWorld { parameter: Parameter$getHelloWorld; @@ -581,6 +587,7 @@ export type ResponseContentType$postHelloWorldReadonly = keyof Response$postHell export interface Params$postHelloWorldReadonly { requestBody: RequestBody$postHelloWorldReadonly["application/json"]; } +export type ResponseContentType$get$undefined$operation$$id = keyof Response$get$undefined$operation$$id$Status$200; export type HttpMethod = "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE"; export interface ObjectLike { [key: string]: any; @@ -593,10 +600,11 @@ export interface QueryParameter { export interface QueryParameters { [key: string]: QueryParameter; } -export type SuccessResponses = Response$getHelloWorld$Status$200 | Response$postHelloWorldReadonly$Status$200; +export type SuccessResponses = Response$getHelloWorld$Status$200 | Response$postHelloWorldReadonly$Status$200 | Response$get$undefined$operation$$id$Status$200; export namespace ErrorResponse { export type getHelloWorld = void; export type postHelloWorldReadonly = void; + export type get$undefined$operation$$id = void; } export interface Encoding { readonly contentType?: string; @@ -647,6 +655,17 @@ export class Client { requestBody: params.requestBody }, option); } + public async get$undefined$operation$$id(option?: RequestOption): Promise { + const url = this.baseUrl + \`/undefined/operation/:id\`; + const headers = { + Accept: "application/json" + }; + return this.apiClient.request({ + httpMethod: "GET", + url, + headers + }, option); + } } " `; diff --git a/test/__tests__/functional/__snapshots__/typedef-with-template-test.ts.snap b/test/__tests__/functional/__snapshots__/typedef-with-template-test.ts.snap index a3b74f1..0e75991 100644 --- a/test/__tests__/functional/__snapshots__/typedef-with-template-test.ts.snap +++ b/test/__tests__/functional/__snapshots__/typedef-with-template-test.ts.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`Typedef with template api.test.domain 1`] = ` "// @@ -575,6 +575,12 @@ export interface Response$postHelloWorldReadonly$Status$200 { message?: Schemas.Message; }; } +export interface Response$get$undefined$operation$$id$Status$200 { + "application/json": { + /** Undefined operation response */ + message?: string; + }; +} export type ResponseContentType$getHelloWorld = keyof Response$getHelloWorld$Status$200; export interface Params$getHelloWorld { parameter: Parameter$getHelloWorld; @@ -584,6 +590,7 @@ export type ResponseContentType$postHelloWorldReadonly = keyof Response$postHell export interface Params$postHelloWorldReadonly { requestBody: RequestBody$postHelloWorldReadonly["application/json"]; } +export type ResponseContentType$get$undefined$operation$$id = keyof Response$get$undefined$operation$$id$Status$200; export type HttpMethod = "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE"; export interface ObjectLike { [key: string]: any; @@ -596,10 +603,11 @@ export interface QueryParameter { export interface QueryParameters { [key: string]: QueryParameter; } -export type SuccessResponses = Response$getHelloWorld$Status$200 | Response$postHelloWorldReadonly$Status$200; +export type SuccessResponses = Response$getHelloWorld$Status$200 | Response$postHelloWorldReadonly$Status$200 | Response$get$undefined$operation$$id$Status$200; export namespace ErrorResponse { export type getHelloWorld = void; export type postHelloWorldReadonly = void; + export type get$undefined$operation$$id = void; } export interface Encoding { readonly contentType?: string; @@ -649,6 +657,17 @@ export const createClient = (apiClient: ApiClient, headers, requestBody: params.requestBody }, option); + }, + get$undefined$operation$$id: (option?: RequestOption): Promise => { + const url = _baseUrl + \`/undefined/operation/:id\`; + const headers = { + Accept: "application/json" + }; + return apiClient.request({ + httpMethod: "GET", + url, + headers + }, option); } }; }; diff --git a/test/api.v2.domain/index.yml b/test/api.v2.domain/index.yml index 69a6021..32987c5 100644 --- a/test/api.v2.domain/index.yml +++ b/test/api.v2.domain/index.yml @@ -66,3 +66,16 @@ paths: properties: message: $ref: "#/components/schemas/Message" + /undefined/operation/:id: + get: + responses: + 200: + description: 成功 + content: + application/json: + schema: + type: object + properties: + message: + type: string + description: Undefined operation response