Skip to content

Commit

Permalink
feat: Support for schema containing format (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
Himenon committed Nov 9, 2021
1 parent bb80cbd commit 24c7bd2
Show file tree
Hide file tree
Showing 14 changed files with 543 additions and 125 deletions.
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

0 comments on commit 24c7bd2

Please sign in to comment.