Skip to content

feat: supports fragment in remote reference #87

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

Merged
merged 8 commits into from
Oct 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions scripts/testCodeGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand Down
8 changes: 7 additions & 1 deletion src/internal/FileSystem/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 17 additions & 10 deletions src/internal/OpenApiTools/components/Reference.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as path from "path";
import { posix as path } from "path";

import type { OpenApi } from "../../../types";
import { DevelopmentError, FeatureDevelopmentError, NotFoundFileError } from "../../Exception";
Expand Down Expand Up @@ -136,22 +136,29 @@ export const generate = <T>(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<T>(entryPoint, referencePoint, data);
}

if (!targetPath.startsWith("components")) {
throw new DevelopmentError(`targetPath is not start "components":\n${targetPath}`);
}

return {
type: "remote",
referencePoint,
Expand Down
145 changes: 145 additions & 0 deletions test/__tests__/__snapshots__/typedef-with-template-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1216,3 +1216,148 @@ export class Client<RequestOption> {
}
"
`;

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<RequestOption> {
request: <T = SuccessResponses>(httpMethod: HttpMethod, url: string, headers: ObjectLike | any, requestBody: ObjectLike | any, queryParameters: QueryParameters | undefined, options?: RequestOption) => Promise<T>;
}
export class Client<RequestOption> {
private baseUrl: string;
constructor(private apiClient: ApiClient<RequestOption>, baseUrl: string) { this.baseUrl = baseUrl.replace(/\\\\/$/, \\"\\"); }
public async getBook(params: Params$getBook, option?: RequestOption): Promise<Response$getBook$Status$200[\\"application/json\\"]> {
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<Response$getDescription$Status$200[\\"application/json\\"]> {
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<Response$getAuthor$Status$200[\\"application/json\\"]> {
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<Response$getPublisher$Status$200[\\"application/json\\"]> {
const url = this.baseUrl + \`/get/publisher/\${params.parameter.id}\`;
const headers = {
Accept: \\"application/json\\"
};
return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option);
}
}
"
`;
3 changes: 1 addition & 2 deletions test/__tests__/argo-rollout-test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as fs from "fs";
import * as path from "path";

import * as Utils from "../utils";

describe("Argo Rollout", () => {
test("client.ts", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/argo-rollout/client.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/argo-rollout/client.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
6 changes: 3 additions & 3 deletions test/__tests__/client-generate-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import type * as Types from "../../src/types";

describe("raw-json-generate", () => {
test("the raw json input result and the json file path result are same", () => {
const generateCode = JSON.parse(fs.readFileSync(path.join(__dirname, "../kubernetes/openapi-v1.18.5.json"), { encoding: "utf-8" }));
const generateCode = JSON.parse(fs.readFileSync("test/kubernetes/openapi-v1.18.5.json", { encoding: "utf-8" }));

const codeGenerator1 = new CodeGenerator(generateCode);
const codeGenerator2 = new CodeGenerator(path.join(__dirname, "../kubernetes/openapi-v1.18.5.json"));
const codeGenerator2 = new CodeGenerator("test/kubernetes/openapi-v1.18.5.json");

const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.ApiClient.Option> = {
generator: Templates.ApiClient.generator,
Expand All @@ -30,7 +30,7 @@ describe("raw-json-generate", () => {
expect(code1).toBe(code2);
});
test("yaml file path loadable", () => {
const codeGenerator = new CodeGenerator(path.join(__dirname, "../api.test.domain/index.yml"));
const codeGenerator = new CodeGenerator("test/api.test.domain/index.yml");

const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.ApiClient.Option> = {
generator: Templates.ApiClient.generator,
Expand Down
3 changes: 1 addition & 2 deletions test/__tests__/format.domain.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as fs from "fs";
import * as path from "path";

import * as Utils from "../utils";

describe("Format Types", () => {
test("format.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/format.domain/code.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/format.domain/code.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
3 changes: 1 addition & 2 deletions test/__tests__/kubernetes-test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as fs from "fs";
import * as path from "path";

import * as Utils from "../utils";

describe("Kubernetes", () => {
test("client-v1.18.5.ts", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/kubernetes/client-v1.18.5.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/kubernetes/client-v1.18.5.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions test/__tests__/multi-type.test.domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import * as Utils from "../utils";

describe("Multi Type", () => {
test("types", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/types.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/mulit-type-test.domain/types.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});

test("apiClient", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/mulit-type-test.domain/apiClient.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/mulit-type-test.domain/apiClient.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions test/__tests__/parameter-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import * as Utils from "../utils";

describe("Parameter", () => {
test("api.test.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/parameter/api.test.domain.json"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/parameter/api.test.domain.json", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
test("infer.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/parameter/infer.domain.json"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/parameter/infer.domain.json", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions test/__tests__/spit-code-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import * as Utils from "../utils";

describe("Split Code", () => {
test("types", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/split/types.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/split/types.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});

test("apiClient", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/split/apiClient.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/split/apiClient.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
6 changes: 3 additions & 3 deletions test/__tests__/template-only-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import * as Utils from "../utils";

describe("Template Only", () => {
test("api.test.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/api.test.domain.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/template-only/api.test.domain.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
test("async-api.test.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/sync-api.test.domain.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/template-only/sync-api.test.domain.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
test("infer.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/template-only/infer.domain.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/template-only/infer.domain.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions test/__tests__/typedef-only-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import * as Utils from "../utils";

describe("Typedef only", () => {
test("typedef-api.test.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-only/api.test.domain.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/typedef-only/api.test.domain.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
test("typedef-infer.domain", () => {
const generateCode = fs.readFileSync(path.join(__dirname, "../code/typedef-only/infer.domain.ts"), { encoding: "utf-8" });
const generateCode = fs.readFileSync("test/code/typedef-only/infer.domain.ts", { encoding: "utf-8" });
const text = Utils.replaceVersionInfo(generateCode);
expect(text).toMatchSnapshot();
});
Expand Down
Loading