Skip to content

Commit

Permalink
feat: Add Code template feature (#30)
Browse files Browse the repository at this point in the history
* feat: changed the interface of the code generator
* fix: test types
* feat: add code template
  • Loading branch information
Himenon authored Apr 4, 2021
1 parent 5774177 commit 881625c
Show file tree
Hide file tree
Showing 17 changed files with 207 additions and 138 deletions.
48 changes: 33 additions & 15 deletions scripts/testCodeGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,57 @@ import * as fs from "fs";
import * as CodeGenerator from "../lib";

const gen = (name: string, enableValidate = true): void => {
const params: CodeGenerator.Params = {
const params: CodeGenerator.OpenApiTsCodeGen.Configuration = {
entryPoint: `test/${name}/index.yml`,
enableValidate,
log: {
validator: {
codeGenerator: {
templates: {
Default: CodeGenerator.DefaultCodeTemplate.makeApiClient,
},
},
typeDefinitionGenerator: {
additional: {
template: "Default",
},
},
validator: {
openapiSchema: enableValidate,
logger: {
displayLogLines: 1,
},
},
};
fs.mkdirSync("test/code", { recursive: true });
const code = CodeGenerator.generateTypeScriptCode(params);
fs.writeFileSync(`test/code/${name}.ts`, code, { encoding: "utf-8" });
const output = CodeGenerator.make(params);
fs.writeFileSync(`test/code/${name}.ts`, output.typeDefinition.value, { encoding: "utf-8" });
console.log(`Generate Code : test/code/${name}.ts`);
};

const genSyncMode = (name: string, enableValidate = true): void => {
const params: CodeGenerator.Params = {
const params: CodeGenerator.OpenApiTsCodeGen.Configuration = {
entryPoint: `test/${name}/index.yml`,
enableValidate,
option: {
codeGenerator: {
sync: true,
codeGenerator: {
templates: {
Default: CodeGenerator.DefaultCodeTemplate.makeApiClient,
},
},
typeDefinitionGenerator: {
additional: {
template: "Default",
option: {
sync: true,
},
},
},
log: {
validator: {
validator: {
openapiSchema: enableValidate,
logger: {
displayLogLines: 1,
},
},
};
fs.mkdirSync("test/code", { recursive: true });
const code = CodeGenerator.generateTypeScriptCode(params);
fs.writeFileSync(`test/code/sync-${name}.ts`, code, { encoding: "utf-8" });
const code = CodeGenerator.make(params);
fs.writeFileSync(`test/code/sync-${name}.ts`, code.typeDefinition.value, { encoding: "utf-8" });
console.log(`Generate Code : test/code/sync-${name}.ts`);
};

Expand Down
2 changes: 2 additions & 0 deletions src/CodeGenerator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { DevelopmentError } from "../Exception";
import * as Factory from "./factory";
import { CreateFunction, traverse } from "./traverse";

export * as Utils from "./utils";

export { CreateFunction, Factory };

export type TransformerFactory<T extends ts.Node> = ts.TransformerFactory<T>;
Expand Down
17 changes: 17 additions & 0 deletions src/CodeGenerator/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ts from "typescript";

import type { CodeGenerator } from "../types";

export const stringToStatements = (code: string): ts.Statement[] => {
const source = ts.createSourceFile("", code, ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS);
return Array.from(source.statements);
};

export const convertIntermediateCodes = (intermediateCodes: CodeGenerator.IntermediateCode[]): ts.Statement[] => {
return intermediateCodes.reduce<ts.Statement[]>((result, intermediateCode) => {
if (typeof intermediateCode === "string") {
return [...result, ...stringToStatements(intermediateCode)];
}
return result.concat(intermediateCode);
}, []);
};
27 changes: 3 additions & 24 deletions src/Converter/CodeGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import ts from "typescript";

import * as ConverterContext from "./ConverterContext";
import { Store } from "./store";
import { CodeGeneratorParams, OpenApi, PickedParameter } from "./types";
import type { CodeGeneratorParams, OpenApi, PickedParameter } from "./types";
import type { CodeGenerator } from "../types";

const extractPickedParameter = (parameter: OpenApi.Parameter): PickedParameter => {
return {
Expand Down Expand Up @@ -57,7 +58,7 @@ const hasQueryParameters = (parameters?: OpenApi.Parameter[]): boolean => {
return parameters.filter(parameter => parameter.in === "query").length > 0;
};

const generateCodeGeneratorParamsList = (
export const generateCodeGeneratorParamsList = (
store: Store.Type,
converterContext: ConverterContext.Types,
allowOperationIds: string[] | undefined,
Expand Down Expand Up @@ -120,25 +121,3 @@ const generateCodeGeneratorParamsList = (

return params;
};

export interface Option {
sync: boolean;
}

export type RewriteCodeAfterTypeDeclaration = (
context: ts.TransformationContext,
codeGeneratorParamsList: CodeGeneratorParams[],
codeGenerateOption: Option,
) => ts.Statement[];

export const generateApiClientCode = (
store: Store.Type,
context: ts.TransformationContext,
converterContext: ConverterContext.Types,
rewriteCodeAfterTypeDeclaration: RewriteCodeAfterTypeDeclaration,
allowOperationIds: string[] | undefined,
option: Option,
): void => {
const codeGeneratorParamsList = generateCodeGeneratorParamsList(store, converterContext, allowOperationIds);
store.addAdditionalStatement(rewriteCodeAfterTypeDeclaration(context, codeGeneratorParamsList, option));
};
4 changes: 2 additions & 2 deletions src/Converter/InferredType.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Types from "./types";
import type { OpenApi } from "./types";

export const getInferredType = (schema: Types.OpenApi.Schema): Types.OpenApi.Schema | undefined => {
export const getInferredType = (schema: OpenApi.Schema): OpenApi.Schema | undefined => {
if (schema.type || schema.oneOf || schema.allOf || schema.anyOf) {
return schema;
}
Expand Down
30 changes: 12 additions & 18 deletions src/Converter/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ts from "typescript";

import * as TypeScriptCodeGenerator from "../CodeGenerator";
import { CodeGenerator as CodeGenerator2 } from "../types";
import * as CodeGenerator from "./CodeGenerator";
import * as Comment from "./Comment";
import * as Headers from "./components/Headers";
Expand All @@ -21,22 +22,18 @@ export { OpenApi, CodeGenerator, CodeGeneratorParams, PickedParameter, Name };
export interface Type {
generateLeadingComment: () => string;
createFunction: TypeScriptCodeGenerator.CreateFunction;
codeGeneratorOption: CodeGenerator.Option;
codeGeneratorOption: CodeGenerator2.Option;
}

export interface Option {
/**
* It is possible to rewrite the implementation after the type declaration.
*/
rewriteCodeAfterTypeDeclaration: CodeGenerator.RewriteCodeAfterTypeDeclaration;
/**
*
*/
codeGeneratorOption: CodeGenerator.Option;
/**
* List of operationId to be used
*/
allowOperationIds?: string[];

generateCodeAfterGeneratedTypeDefinition?: CodeGenerator2.GenerateFunction;

codeGeneratorOption: CodeGenerator2.Option;
}

export const create = (entryPoint: string, rootSchema: OpenApi.Document, noReferenceOpenApiSchema: OpenApi.Document, option: Option): Type => {
Expand All @@ -47,6 +44,7 @@ export const create = (entryPoint: string, rootSchema: OpenApi.Document, noRefer
const factory = TypeScriptCodeGenerator.Factory.create(context);
const store = Store.create(factory, noReferenceOpenApiSchema);
const toTypeNodeContext = TypeNodeContext.create(entryPoint, store, factory, converterContext);
let extraStatements: ts.Statement[] = [];

if (rootSchema.components) {
if (rootSchema.components.schemas) {
Expand Down Expand Up @@ -106,16 +104,12 @@ export const create = (entryPoint: string, rootSchema: OpenApi.Document, noRefer
}
if (rootSchema.paths) {
Paths.generateStatements(entryPoint, currentPoint, store, factory, rootSchema.paths, toTypeNodeContext, converterContext);
CodeGenerator.generateApiClientCode(
store,
context,
converterContext,
option.rewriteCodeAfterTypeDeclaration,
option.allowOperationIds,
option.codeGeneratorOption,
);

const codeGeneratorParamsList = CodeGenerator.generateCodeGeneratorParamsList(store, converterContext, option.allowOperationIds);
const extraStatements2 = option.generateCodeAfterGeneratedTypeDefinition?.(context, codeGeneratorParamsList, option.codeGeneratorOption) || [];
extraStatements = extraStatements.concat(TypeScriptCodeGenerator.Utils.convertIntermediateCodes(extraStatements2));
}
return store.getRootStatements();
return store.getRootStatements().concat(extraStatements);
};

return {
Expand Down
4 changes: 2 additions & 2 deletions src/Converter/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CodeGeneratorParams, PickedParameter } from "./CodeGeneratorParams";
import * as OpenApi from "./OpenApiSchemaV3";
import { CodeGeneratorParams, PickedParameter } from "../../types/extractSchema";
import * as OpenApi from "../../types/OpenApi";

export { OpenApi, CodeGeneratorParams, PickedParameter };

Expand Down
3 changes: 2 additions & 1 deletion src/DefaultCodeTemplate/ApiClientClass/ApiClientInterface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ts from "typescript";

import { Factory } from "../../CodeGenerator";
import type { CodeGenerator, CodeGeneratorParams } from "../../Converter";
import type { CodeGenerator } from "../../types";
import type { CodeGeneratorParams } from "../../types/extractSchema";

const httpMethodList: string[] = ["GET", "PUT", "POST", "DELETE", "OPTIONS", "HEAD", "PATCH", "TRACE"];

Expand Down
3 changes: 2 additions & 1 deletion src/DefaultCodeTemplate/ApiClientClass/Method.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ts from "typescript";

import { Factory } from "../../CodeGenerator";
import type { CodeGenerator, CodeGeneratorParams } from "../../Converter";
import type { CodeGenerator } from "../../types";
import type { CodeGeneratorParams } from "../../types/extractSchema";
import * as MethodBody from "./MethodBody";

export { MethodBody };
Expand Down
3 changes: 2 additions & 1 deletion src/DefaultCodeTemplate/ApiClientClass/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import ts from "typescript";

import { Factory } from "../../CodeGenerator";
import type { CodeGenerator, CodeGeneratorParams } from "../../Converter";
import type { CodeGenerator } from "../../types";
import type { CodeGeneratorParams } from "../../types/extractSchema";
import * as ApiClientInterface from "./ApiClientInterface";
import * as Class from "./Class";
import * as Constructor from "./Constructor";
Expand Down
9 changes: 5 additions & 4 deletions src/DefaultCodeTemplate/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import ts from "typescript";

import * as TypeScriptCodeGenerator from "../CodeGenerator";
import type * as Converter from "../Converter";
import type { CodeGenerator } from "../types";
import type { CodeGeneratorParams } from "../types/extractSchema";
import * as ApiClientArgument from "./ApiClientArgument";
import * as ApiClientClass from "./ApiClientClass";

export const rewriteCodeAfterTypeDeclaration: Converter.CodeGenerator.RewriteCodeAfterTypeDeclaration = (
export const makeApiClient: CodeGenerator.GenerateFunction = (
context: ts.TransformationContext,
codeGeneratorParamsList: Converter.CodeGeneratorParams[],
option: Converter.CodeGenerator.Option,
codeGeneratorParamsList: CodeGeneratorParams[],
option: CodeGenerator.Option,
): ts.Statement[] => {
const statements: ts.Statement[] = [];
const factory = TypeScriptCodeGenerator.Factory.create(context);
Expand Down
19 changes: 6 additions & 13 deletions src/Validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,11 @@ import * as Ajv from "ajv";

import type { OpenApi } from "../Converter/types";
import openapiSchema from "./openapi.json";
import type { Validator } from "../types";

export interface LogOption {
/**
* default: undefined (all logs)
* Number of lines displayed in the latest log
*/
displayLogLines?: number;
}

const showLogs = (logs: any[], option?: LogOption) => {
if (option && option.displayLogLines && option.displayLogLines > 0) {
const latestLogs = logs.slice(0, option.displayLogLines);
const showLogs = (logs: any[], logger?: Validator.Logger) => {
if (logger && logger.displayLogLines && logger.displayLogLines > 0) {
const latestLogs = logs.slice(0, logger.displayLogLines);
const moreLogNum = logs.length - latestLogs.length;
console.error("Correct the validation error before generating the code.");
console.error(`There are a total of ${logs.length} errors below.`);
Expand All @@ -37,12 +30,12 @@ const showLogs = (logs: any[], option?: LogOption) => {
}
};

export const validate = (openapiDoc: OpenApi.Document, option?: LogOption): void => {
export const validate = (openapiDoc: OpenApi.Document, logger?: Validator.Logger): void => {
const ajv = new Ajv.default({ allErrors: true });
const validate = ajv.compile(openapiSchema);
validate(openapiDoc);
if (validate.errors) {
showLogs(validate.errors, option);
showLogs(validate.errors, logger);
throw new Error("Validation Error");
}
};
Loading

0 comments on commit 881625c

Please sign in to comment.