Skip to content

feat: Support for schema containing format #63

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 5 commits into from
Nov 9, 2021
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
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,72 @@ const customGenerator: Types.CodeGenerator.CustomGenerator<Option> = {
}
```

### Define any Data Types Format

Convert a Data Type with the following `format` to any type definition.

```yaml
components:
schemas:
Binary:
type: string
format: binary
IntOrString:
type: string
format: int-or-string
AandB:
type: string
format: A-and-B
```

The option to convert the Data Type Format to an arbitrary type definition is defined as follows.

```ts
import { CodeGenerator, Option } from "@himenon/openapi-typescript-code-generator";
const option: Option = {
convertOption: {
formatConversions: [
{
selector: {
format: "binary",
},
output: {
type: ["Blob"],
},
},
{
selector: {
format: "int-or-string",
},
output: {
type: ["number", "string"],
},
},
{
selector: {
format: "A-and-B",
},
output: {
type: ["A", "B"],
multiType: "allOf",
},
},
],
},
};
const codeGenerator = new CodeGenerator(inputFilename, option);
```

The typedef generated by this will look like this

```ts
export namespace Schemas {
export type Binary = Blob;
export type IntOrString = number | string;
export type AandB = A & B;
}
```

### Define a code template with TypeScript AST

You can extend your code using the API of TypeScript AST.
Expand Down
66 changes: 66 additions & 0 deletions docs/ja/README-ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,72 @@ const customGenerator: Types.CodeGenerator.CustomGenerator<Option> = {
}
```

### 任意の Data Types Format を定義する

以下のような`format`が指定された Data Type を任意の型定義に変換します。

```yaml
components:
schemas:
Binary:
type: string
format: binary
IntOrString:
type: string
format: int-or-string
AandB:
type: string
format: A-and-B
```

Data Type Formatを任意の型定義に変換するオプションは次のように定義します。

```ts
import { CodeGenerator, Option } from "@himenon/openapi-typescript-code-generator";
const option: Option = {
convertOption: {
formatConversions: [
{
selector: {
format: "binary",
},
output: {
type: ["Blob"],
},
},
{
selector: {
format: "int-or-string",
},
output: {
type: ["number", "string"],
},
},
{
selector: {
format: "A-and-B",
},
output: {
type: ["A", "B"],
multiType: "allOf",
},
},
],
},
};
const codeGenerator = new CodeGenerator(inputFilename, option);
```

これで生成される型定義は次のようになります。

```ts
export namespace Schemas {
export type Binary = Blob;
export type IntOrString = number | string;
export type AandB = A & B;
}
```

### TypeScript AST によるコードテンプレートを定義する

TypeScript AST の API を利用したコードの拡張が可能です。
Expand Down
137 changes: 24 additions & 113 deletions scripts/testCodeGen.ts
Original file line number Diff line number Diff line change
@@ -1,131 +1,42 @@
import * as fs from "fs";
import { posix as path } from "path";

import { CodeGenerator } from "../lib";
import * as Templates from "../lib/templates";
import type * as Types from "../lib/types";

const writeText = (filename: string, text: string): void => {
fs.mkdirSync(path.dirname(filename), { recursive: true });
fs.writeFileSync(filename, text, { encoding: "utf-8" });
console.log(`Generate Code : ${filename}`);
};

const generateTypedefCodeOnly = (inputFilename: string, outputFilename: string, isValidate: boolean) => {
const codeGenerator = new CodeGenerator(inputFilename);
if (isValidate) {
codeGenerator.validateOpenApiSchema({
logger: { displayLogLines: 1 },
});
}
const code = codeGenerator.generateTypeDefinition();
writeText(outputFilename, code);
};

const generateTemplateCodeOnly = (
inputFilename: string,
outputFilename: string,
isValidate: boolean,
option: Templates.ApiClient.Option,
): void => {
const codeGenerator = new CodeGenerator(inputFilename);
if (isValidate) {
codeGenerator.validateOpenApiSchema({
logger: { displayLogLines: 1 },
});
}

const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.ApiClient.Option> = {
generator: Templates.ApiClient.generator,
option: option,
};

const code = codeGenerator.generateCode([apiClientGeneratorTemplate]);

writeText(outputFilename, code);
};

const generateTypedefWithTemplateCode = (
inputFilename: string,
outputFilename: string,
isValidate: boolean,
option: Templates.ApiClient.Option,
): void => {
const codeGenerator = new CodeGenerator(inputFilename);
if (isValidate) {
codeGenerator.validateOpenApiSchema({
logger: { displayLogLines: 1 },
});
}

const code = codeGenerator.generateTypeDefinition([
codeGenerator.getAdditionalTypeDefinitionCustomCodeGenerator(),
{
generator: Templates.ApiClient.generator,
option: option,
},
]);

writeText(outputFilename, code);
};

const generateSplitCode = (inputFilename: string, outputDir: string) => {
const codeGenerator = new CodeGenerator(inputFilename);

const apiClientGeneratorTemplate: Types.CodeGenerator.CustomGenerator<Templates.ApiClient.Option> = {
generator: Templates.ApiClient.generator,
option: { sync: false, additionalMethodComment: true },
};

const typeDefCode = codeGenerator.generateTypeDefinition();
const apiClientCode = codeGenerator.generateCode([
{
generator: () => {
return [`import { Schemas } from "./types";`];
},
},
codeGenerator.getAdditionalTypeDefinitionCustomCodeGenerator(),
apiClientGeneratorTemplate,
]);

writeText(path.join(outputDir, "types.ts"), typeDefCode);
writeText(path.join(outputDir, "apiClient.ts"), apiClientCode);
};

const generateParameter = (inputFilename: string, outputFilename: string) => {
const codeGenerator = new CodeGenerator(inputFilename);
writeText(outputFilename, JSON.stringify(codeGenerator.getCodeGeneratorParamsArray(), null, 2));
};
import * as Writer from "./writer";

const main = () => {
generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true);
generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false);
Writer.generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true);
Writer.generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false);

generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false });
generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true });
generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true });
Writer.generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false });
Writer.generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true });
Writer.generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true });

generateTypedefWithTemplateCode("test/api.v2.domain/index.yml", "test/code/typedef-with-template/api.v2.domain.ts", false, { sync: false });
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, {
Writer.generateTypedefWithTemplateCode("test/api.v2.domain/index.yml", "test/code/typedef-with-template/api.v2.domain.ts", false, {
sync: false,
});
generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, {
Writer.generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, {
sync: false,
});
Writer.generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, {
sync: true,
});
generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false });
Writer.generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, {
sync: false,
});

generateTypedefWithTemplateCode("test/ref.access/index.yml", "test/code/typedef-with-template/ref-access.ts", false, {
Writer.generateTypedefWithTemplateCode("test/ref.access/index.yml", "test/code/typedef-with-template/ref-access.ts", false, {
sync: false,
});
generateTypedefWithTemplateCode("test/kubernetes/openapi-v1.18.5.json", "test/code/kubernetes/client-v1.18.5.ts", false, { sync: false });
generateTypedefWithTemplateCode("test/argo-rollout/index.json", "test/code/argo-rollout/client.ts", false, {
Writer.generateTypedefWithTemplateCode("test/kubernetes/openapi-v1.18.5.json", "test/code/kubernetes/client-v1.18.5.ts", false, {
sync: false,
});
Writer.generateTypedefWithTemplateCode("test/argo-rollout/index.json", "test/code/argo-rollout/client.ts", false, {
sync: false,
});

Writer.generateSplitCode("test/api.test.domain/index.yml", "test/code/split");

generateSplitCode("test/api.test.domain/index.yml", "test/code/split");
Writer.generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json");
Writer.generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json");

generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json");
generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json");
Writer.generateFormatTypeCode("test/format.domain/index.yml", "test/code/format.domain/code.ts");
};

main();
Loading