Skip to content

Commit 3e5f4ef

Browse files
authored
feat: Support Kubernetes OpenAPI Schema (#62)
1 parent 47a2377 commit 3e5f4ef

File tree

26 files changed

+175959
-2128
lines changed

26 files changed

+175959
-2128
lines changed

.eslintingore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
lib
22
debug
3+
test/code/
4+
lib/
5+
test/kubernetes
6+
test/argo-rollout
7+
CHANGELOG.md

.prettierignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
package.json
22
test/code/
33
lib/
4+
test/code/
5+
test/kubernetes
6+
test/argo-rollout
7+
CHANGELOG.md

scripts/testCodeGen.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,18 @@ const main = () => {
114114
});
115115
generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false });
116116

117-
generateTypedefWithTemplateCode("test/argo-rollout/index.json", "test/code/typedef-with-template/argo-rollout.ts", false, {
117+
generateTypedefWithTemplateCode("test/ref.access/index.yml", "test/code/typedef-with-template/ref-access.ts", false, {
118118
sync: false,
119119
});
120-
121-
generateTypedefWithTemplateCode("test/ref.access/index.yml", "test/code/typedef-with-template/ref-access.ts", false, {
120+
generateTypedefWithTemplateCode("test/kubernetes/openapi-v1.18.5.json", "test/code/kubernetes/client-v1.18.5.ts", false, { sync: false });
121+
generateTypedefWithTemplateCode("test/argo-rollout/index.json", "test/code/argo-rollout/client.ts", false, {
122122
sync: false,
123123
});
124124

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

127127
generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json");
128128
generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json");
129-
130-
131129
};
132130

133131
main();

src/code-templates/api-client/ApiClientClass/Class.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ export const create = (factory: TsGenerator.Factory.Type, members: ts.ClassEleme
1616
modifiers: [ts.factory.createModifier(ts.SyntaxKind.PrivateKeyword)],
1717
name: "baseUrl",
1818
type: ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
19-
})
20-
,...members],
19+
}),
20+
...members,
21+
],
2122
typeParameterDeclaration: [
2223
factory.TypeParameterDeclaration.create({
2324
name: "RequestOption",

src/internal/Logger/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ export const showFilePosition = (entryPoint: string, currentPoint: string, refer
1414
export const error = (message: string): void => {
1515
console.error(message);
1616
};
17+
18+
export const warn = (message: string): void => {
19+
console.log(message);
20+
};

src/internal/OpenApiTools/TypeNodeContext.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ const generatePath = (entryPoint: string, currentPoint: string, referencePath: s
2626
};
2727
};
2828

29-
const calculateReferencePath = (store: Walker.Store, base: string, pathArray: string[], converterContext: ConverterContext.Types,): ToTypeNode.ResolveReferencePath => {
29+
const calculateReferencePath = (
30+
store: Walker.Store,
31+
base: string,
32+
pathArray: string[],
33+
converterContext: ConverterContext.Types,
34+
): ToTypeNode.ResolveReferencePath => {
3035
let names: string[] = [];
3136
let unresolvedPaths: string[] = [];
3237
pathArray.reduce((previous, lastPath, index) => {

src/internal/OpenApiTools/Walker/Store.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import * as Operation from "./Operation";
1212
import * as State from "./State";
1313
import * as Structure from "./structure";
1414

15+
export interface AddStatementOption {
16+
/**
17+
* pathに対して強制的にSchemaを上書きするフラグ
18+
* TypeAliasが先に登録され、Primitiveな型定義が登録されない問題を解決する
19+
*/
20+
override?: boolean;
21+
}
22+
1523
class Store {
1624
private state: State.Type;
1725
private operator: Structure.OperatorType;
@@ -49,12 +57,16 @@ class Store {
4957
statements,
5058
});
5159
}
60+
private capitalizeFirstLetter(text: string): string {
61+
return text.charAt(0).toUpperCase() + text.slice(1);
62+
}
5263
public getRootStatements(): ts.Statement[] {
5364
// Debug Point: 抽象的なデータ構造全体を把握するために出力すると良い
5465
// fs.writeFileSync("debug/tree.json", JSON.stringify(this.operator.getHierarchy(), null, 2), { encoding: "utf-8" });
5566
const statements = Def.componentNames.reduce<ts.Statement[]>((statements, componentName) => {
5667
const treeOfNamespace = this.getChildByPaths(componentName, "namespace");
5768
if (treeOfNamespace) {
69+
treeOfNamespace.name = this.capitalizeFirstLetter(treeOfNamespace.name);
5870
return statements.concat(this.convertNamespace(treeOfNamespace));
5971
}
6072
return statements;
@@ -74,23 +86,32 @@ class Store {
7486
/**
7587
* @params path: "components/headers/hoge"
7688
*/
77-
public addStatement(path: string, statement: Structure.ComponentParams): void {
89+
public addStatement(path: string, statement: Structure.ComponentParams, options?: AddStatementOption): void {
7890
if (!path.startsWith("components")) {
7991
throw new UnSupportError(`componentsから始まっていません。path=${path}`);
8092
}
8193
const targetPath = Path.posix.relative("components", path);
82-
// すでにinterfaceとして登録がある場合はスキップ
83-
if (this.hasStatement(targetPath, ["interface"])) {
94+
// すでにinterfaceまたはNAMESPACEとして登録がある場合はスキップ
95+
if (this.hasStatement(targetPath, ["interface", "namespace"])) {
8496
return;
8597
}
8698
// もしTypeAliasが同じスコープに登録されているかつ、interfaceが新しく追加しようとしている場合、既存のstatementを削除する
87-
if (this.hasStatement(targetPath, ["typeAlias"]) && statement.kind === "interface") {
99+
if (!!options?.override || (this.hasStatement(targetPath, ["typeAlias"]) && statement.kind === "interface")) {
88100
this.operator.remove(targetPath, "typeAlias");
89101
}
90102
this.operator.set(targetPath, Structure.createInstance(statement));
91103
}
92104
public getStatement<T extends Structure.DataStructure.Kind>(path: string, kind: T): Structure.DataStructure.GetChild<T> | undefined {
93105
const targetPath = Path.posix.relative("components", path);
106+
// components/schemasの場合
107+
if (path.split("/").length === 2 && kind === "namespace") {
108+
const child = this.getChildByPaths(targetPath, kind);
109+
if (child) {
110+
// FIXME Side Effect
111+
child.name = this.capitalizeFirstLetter(child.name);
112+
}
113+
return child;
114+
}
94115
return this.getChildByPaths(targetPath, kind);
95116
}
96117
public addComponent(componentName: Def.ComponentName, statement: Structure.ComponentParams): void {

src/internal/OpenApiTools/components/Operation.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,14 @@ export const generateNamespace = (
5555
deprecated: operation.deprecated,
5656
});
5757

58-
const parameters = [...pathItemParameters || [], ...operation.parameters || []];
58+
const parameters = [...(pathItemParameters || []), ...(operation.parameters || [])];
5959

6060
if (parameters.length > 0) {
6161
const parameterName = "Parameter";
6262
store.addStatement(`${basePath}/Parameter`, {
6363
kind: "interface",
6464
name: parameterName,
65-
value: Parameter.generateInterface(
66-
entryPoint,
67-
currentPoint,
68-
store,
69-
factory,
70-
parameterName,
71-
parameters,
72-
context,
73-
converterContext,
74-
),
65+
value: Parameter.generateInterface(entryPoint, currentPoint, store, factory, parameterName, parameters, context, converterContext),
7566
});
7667
}
7768

@@ -148,20 +139,11 @@ export const generateStatements = (
148139
throw new Error("not setting operationId\n" + JSON.stringify(operation));
149140
}
150141
store.updateOperationState(httpMethod, requestUri, operationId, {});
151-
const parameters = [...pathItemParameters || [], ...operation.parameters || []];
142+
const parameters = [...(pathItemParameters || []), ...(operation.parameters || [])];
152143
if (parameters.length > 0) {
153144
const parameterName = converterContext.generateParameterName(operationId);
154145
statements.push(
155-
Parameter.generateAliasInterface(
156-
entryPoint,
157-
currentPoint,
158-
store,
159-
factory,
160-
parameterName,
161-
parameters,
162-
context,
163-
converterContext,
164-
),
146+
Parameter.generateAliasInterface(entryPoint, currentPoint, store, factory, parameterName, parameters, context, converterContext),
165147
);
166148
}
167149
if (operation.requestBody) {
@@ -175,7 +157,7 @@ export const generateStatements = (
175157
export: true,
176158
name: converterContext.generateRequestBodyName(operationId),
177159
type: factory.TypeReferenceNode.create({
178-
name: context.resolveReferencePath(currentPoint, `${reference.path}`) + "." + Name.ComponentChild.Content, // TODO Contextから作成?
160+
name: context.resolveReferencePath(currentPoint, `${reference.path}`).name + "." + Name.ComponentChild.Content, // TODO Contextから作成?
179161
}),
180162
}),
181163
);

0 commit comments

Comments
 (0)