diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a24cd05..c97e1011 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,6 +35,7 @@ jobs: - name: Test & Build run: | pnpm build + pnpm run test:code:gen pnpm test env: CI: true diff --git a/scripts/testCodeGen.ts b/scripts/testCodeGen.ts index 1e9492e0..7554c5e9 100644 --- a/scripts/testCodeGen.ts +++ b/scripts/testCodeGen.ts @@ -24,6 +24,9 @@ const main = () => { Writer.generateTypedefWithTemplateCode("test/ref.access/index.yml", "test/code/typedef-with-template/ref-access.ts", false, { sync: false, }); + Writer.generateTypedefWithTemplateCode("test/remote.ref.access/v1.yml", "test/code/typedef-with-template/remote-ref-access.ts", false, { + sync: false, + }); Writer.generateTypedefWithTemplateCode("test/kubernetes/openapi-v1.18.5.json", "test/code/kubernetes/client-v1.18.5.ts", false, { sync: false, }); diff --git a/src/internal/FileSystem/index.ts b/src/internal/FileSystem/index.ts index 2b4c0b6f..db570a70 100644 --- a/src/internal/FileSystem/index.ts +++ b/src/internal/FileSystem/index.ts @@ -28,11 +28,17 @@ export class FileSystem { } public static existSync(entryPoint: string): boolean { + const fragmentIndex = entryPoint.indexOf(this.FRAGMENT); + const hasFragment = fragmentIndex !== -1; + if (hasFragment) { + const filename = entryPoint.substring(0, fragmentIndex); + return !!(fs.existsSync(filename) && fs.statSync(filename).isFile()); + } return !!(fs.existsSync(entryPoint) && fs.statSync(entryPoint).isFile()); } public static loadJsonOrYaml(entryPoint: string): any { - const hasFragment: boolean = new RegExp(this.FRAGMENT).test(entryPoint); + const hasFragment = entryPoint.indexOf(this.FRAGMENT) !== -1; if (hasFragment) { const [filename, fragment] = entryPoint.split(this.FRAGMENT); const data = this.internalLoadJsonOrYaml(filename); diff --git a/src/internal/OpenApiTools/components/Reference.ts b/src/internal/OpenApiTools/components/Reference.ts index ff685064..ab07a558 100644 --- a/src/internal/OpenApiTools/components/Reference.ts +++ b/src/internal/OpenApiTools/components/Reference.ts @@ -136,22 +136,29 @@ export const generate = (entryPoint: string, currentPoint: string, reference: throw new NotFoundFileError(`Not found reference point from current point. \n Path: ${referencePoint}`); } - const relativePathFromEntryPoint = path.relative(path.dirname(entryPoint), referencePoint); // components/hoge/fuga.yml - const ext = path.extname(relativePathFromEntryPoint); // .yml - const pathArray: string[] = relativePathFromEntryPoint.replace(ext, "").split(path.sep); // ["components", "hoge", "fuga"] - const targetPath: string = pathArray.join("/"); // components/hoge/fuga + const fragmentIndex = referencePoint.indexOf("#/"); + let targetPath: string; + if (fragmentIndex !== -1) { + targetPath = referencePoint.substring(fragmentIndex + 2); + } else { + const relativePathFromEntryPoint = path.relative(path.dirname(entryPoint), referencePoint); // components/hoge/fuga.yml + const pathArray: string[] = relativePathFromEntryPoint.split(path.sep); // ["components", "hoge", "fuga"] + if (pathArray[0] != "components") { + throw new DevelopmentError(`targetPath is not start "components":\n${relativePathFromEntryPoint}`); + } + + const ext = path.extname(relativePathFromEntryPoint); // .yml + targetPath = pathArray.join("/").substring(0, relativePathFromEntryPoint.length - ext.length); // components/hoge/fuga + } + const pathArray: string[] = targetPath.split("/"); // ["components", "hoge", "fuga"] const schemaName = pathArray[pathArray.length - 1]; // fuga const componentName = pathArray[0] === "components" ? pathArray[1] : ""; - const data = FileSystem.loadJsonOrYaml(referencePoint); + const data = FileSystem.loadJsonOrYaml(referencePoint); if (Guard.isReference(data)) { return generate(entryPoint, referencePoint, data); } - if (!targetPath.startsWith("components")) { - throw new DevelopmentError(`targetPath is not start "components":\n${targetPath}`); - } - return { type: "remote", referencePoint, diff --git a/test/__tests__/__snapshots__/typedef-with-template-test.ts.snap b/test/__tests__/__snapshots__/typedef-with-template-test.ts.snap index 460828c9..69a9832f 100644 --- a/test/__tests__/__snapshots__/typedef-with-template-test.ts.snap +++ b/test/__tests__/__snapshots__/typedef-with-template-test.ts.snap @@ -1216,3 +1216,148 @@ export class Client { } " `; + +exports[`Typedef with template remote-ref-access 1`] = ` +"// +// Generated by @himenon/openapi-typescript-code-generator +// +// OpenApi : 3.1.0 +// +// License : MIT +// + + +export namespace Schemas { + export interface Book { + author?: { + name?: string; + age?: string; + }; + publisher?: { + name?: any; + address?: string; + }; + metadata: { + description: string; + }; + } + export namespace Book { + export namespace properties { + export interface author { + name?: string; + age?: string; + } + export interface publisher { + name?: any; + address?: string; + } + export namespace metadata { + export namespace properties { + export type description = string; + } + } + } + } + export type Author = Schemas.Book.properties.author; + export type Publisher = Schemas.Book.properties.publisher; +} +export interface Parameter$getBook { + /** Book ID */ + id: string; +} +export interface Response$getBook$Status$200 { + \\"application/json\\": Schemas.Book; +} +export interface Parameter$getDescription { + /** Book ID */ + id: string; +} +export interface Response$getDescription$Status$200 { + \\"application/json\\": Schemas.Book.properties.metadata.properties.description; +} +export interface Parameter$getAuthor { + /** Author Id */ + id: string; +} +export interface Response$getAuthor$Status$200 { + \\"application/json\\": Schemas.Author; +} +export interface Parameter$getPublisher { + /** Publisher ID */ + id: string; +} +export interface Response$getPublisher$Status$200 { + \\"application/json\\": Schemas.Publisher; +} +export type ResponseContentType$getBook = keyof Response$getBook$Status$200; +export interface Params$getBook { + parameter: Parameter$getBook; +} +export type ResponseContentType$getDescription = keyof Response$getDescription$Status$200; +export interface Params$getDescription { + parameter: Parameter$getDescription; +} +export type ResponseContentType$getAuthor = keyof Response$getAuthor$Status$200; +export interface Params$getAuthor { + parameter: Parameter$getAuthor; +} +export type ResponseContentType$getPublisher = keyof Response$getPublisher$Status$200; +export interface Params$getPublisher { + parameter: Parameter$getPublisher; +} +export type HttpMethod = \\"GET\\" | \\"PUT\\" | \\"POST\\" | \\"DELETE\\" | \\"OPTIONS\\" | \\"HEAD\\" | \\"PATCH\\" | \\"TRACE\\"; +export interface ObjectLike { + [key: string]: any; +} +export interface QueryParameter { + value: any; + style?: \\"form\\" | \\"spaceDelimited\\" | \\"pipeDelimited\\" | \\"deepObject\\"; + explode: boolean; +} +export interface QueryParameters { + [key: string]: QueryParameter; +} +export type SuccessResponses = Response$getBook$Status$200 | Response$getDescription$Status$200 | Response$getAuthor$Status$200 | Response$getPublisher$Status$200; +export namespace ErrorResponse { + export type getBook = void; + export type getDescription = void; + export type getAuthor = void; + export type getPublisher = void; +} +export interface ApiClient { + request: (httpMethod: HttpMethod, url: string, headers: ObjectLike | any, requestBody: ObjectLike | any, queryParameters: QueryParameters | undefined, options?: RequestOption) => Promise; +} +export class Client { + private baseUrl: string; + constructor(private apiClient: ApiClient, baseUrl: string) { this.baseUrl = baseUrl.replace(/\\\\/$/, \\"\\"); } + public async getBook(params: Params$getBook, option?: RequestOption): Promise { + const url = this.baseUrl + \`/get/book/\${params.parameter.id}\`; + const headers = { + Accept: \\"application/json\\" + }; + return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option); + } + public async getDescription(params: Params$getDescription, option?: RequestOption): Promise { + const url = this.baseUrl + \`/get/book/\${params.parameter.id}/description\`; + const headers = { + Accept: \\"application/json\\" + }; + return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option); + } + public async getAuthor(params: Params$getAuthor, option?: RequestOption): Promise { + const url = this.baseUrl + \`/get/author/\${params.parameter.id}\`; + const headers = { + Accept: \\"application/json\\" + }; + return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option); + } + public async getPublisher(params: Params$getPublisher, option?: RequestOption): Promise { + const url = this.baseUrl + \`/get/publisher/\${params.parameter.id}\`; + const headers = { + Accept: \\"application/json\\" + }; + return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option); + } +} +" +`; diff --git a/test/__tests__/typedef-with-template-test.ts b/test/__tests__/typedef-with-template-test.ts index 6d833fd7..abf4cccf 100644 --- a/test/__tests__/typedef-with-template-test.ts +++ b/test/__tests__/typedef-with-template-test.ts @@ -29,4 +29,9 @@ describe("Typedef with template", () => { const text = Utils.replaceVersionInfo(generateCode); expect(text).toMatchSnapshot(); }); + test("remote-ref-access", () => { + const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-with-template/remote-ref-access.ts"), { encoding: "utf-8" }); + const text = Utils.replaceVersionInfo(generateCode); + expect(text).toMatchSnapshot(); + }); }); diff --git a/test/remote.ref.access/v0.yml b/test/remote.ref.access/v0.yml new file mode 100644 index 00000000..9b01f562 --- /dev/null +++ b/test/remote.ref.access/v0.yml @@ -0,0 +1,124 @@ +openapi: 3.1.0 +info: + version: 1.0.0 + title: v0.remote.ref.access.test + description: Library test schema + license: + name: MIT + +servers: + - url: "http://dev.remote.ref.access.test/v0/" + description: Development Environment + - url: "https://ref,remote.access.test/v0/" + description: Production Environment + +tags: + - name: test + +components: + schemas: + Book: + type: object + required: + - metadata + properties: + author: + type: object + properties: + name: + type: string + age: + type: string + publisher: + type: object + properties: + name: + type: String + address: + type: string + metadata: + type: object + required: + - description + properties: + description: + type: string + Author: + $ref: "#/components/schemas/Book/properties/author" + Publisher: + $ref: "#/components/schemas/Book/properties/publisher" + +paths: + /get/book/{id}: + parameters: + - name: id + in: path + required: true + description: Book ID + schema: + type: string + format: uuid + get: + operationId: getBook + responses: + 200: + description: Get Books + content: + application/json: + schema: + $ref: "#/components/schemas/Book" + /get/book/{id}/description: + parameters: + - name: id + in: path + required: true + description: Book ID + schema: + type: string + format: uuid + get: + operationId: getDescription + responses: + 200: + description: Get Book Description + content: + application/json: + schema: + $ref: "#/components/schemas/Book/properties/metadata/properties/description" + + /get/author/{id}: + parameters: + - name: id + in: path + required: true + description: Author Id + schema: + type: string + format: uuid + get: + operationId: getAuthor + responses: + 200: + description: Get Author + content: + application/json: + schema: + $ref: "#/components/schemas/Book/properties/author" + /get/publisher/{id}: + parameters: + - name: id + in: path + required: true + description: Publisher ID + schema: + type: string + format: uuid + get: + operationId: getPublisher + responses: + 200: + description: Get Publisher + content: + application/json: + schema: + $ref: "#/components/schemas/Publisher" diff --git a/test/remote.ref.access/v1.yml b/test/remote.ref.access/v1.yml new file mode 100644 index 00000000..5c5f2ee5 --- /dev/null +++ b/test/remote.ref.access/v1.yml @@ -0,0 +1,100 @@ +openapi: 3.1.0 +info: + version: 1.0.0 + title: v1.remote.ref.access.test + description: Library test schema + license: + name: MIT + +servers: + - url: "http://dev.remote.ref.access.test/v1/" + description: Development Environment + - url: "https://ref,remote.access.test/v1/" + description: Production Environment + +tags: + - name: test + +components: + schemas: + Book: + $ref: "v0.yml#/components/schemas/Book" + Author: + $ref: "v0.yml#/components/schemas/Book/properties/author" + Publisher: + $ref: "v0.yml#/components/schemas/Book/properties/publisher" + +paths: + /get/book/{id}: + parameters: + - name: id + in: path + required: true + description: Book ID + schema: + type: string + format: uuid + get: + operationId: getBook + responses: + 200: + description: Get Books + content: + application/json: + schema: + $ref: "#/components/schemas/Book" + /get/book/{id}/description: + parameters: + - name: id + in: path + required: true + description: Book ID + schema: + type: string + format: uuid + get: + operationId: getDescription + responses: + 200: + description: Get Book Description + content: + application/json: + schema: + $ref: "v0.yml#/components/schemas/Book/properties/metadata/properties/description" + + /get/author/{id}: + parameters: + - name: id + in: path + required: true + description: Author Id + schema: + type: string + format: uuid + get: + operationId: getAuthor + responses: + 200: + description: Get Author + content: + application/json: + schema: + $ref: "#/components/schemas/Author" + /get/publisher/{id}: + parameters: + - name: id + in: path + required: true + description: Publisher ID + schema: + type: string + format: uuid + get: + operationId: getPublisher + responses: + 200: + description: Get Publisher + content: + application/json: + schema: + $ref: "#/components/schemas/Publisher"