Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ts from "typescript";
import type { TsGenerator } from "../../../../api";
import type { CodeGenerator } from "../../../../types";
import * as Utils from "../../utils";
import { escapeText2 as escapeText } from "../../../../utils";

export const isPathParameter = (params: any): params is CodeGenerator.PickedParameter => {
return params.in === "path";
Expand Down Expand Up @@ -45,11 +46,14 @@ export const generateUrlTemplateExpression = (
}, {});
const urlTemplate: Utils.Params$TemplateExpression = [];
let temporaryStringList: string[] = [];
// TODO generateVariableIdentifierに噛み合わ下げいいように変換する
const replaceText = (text: string): string | undefined => {
let replacedText = text;
Object.keys(patternMap).forEach(key => {
if (new RegExp(key).test(replacedText)) {
replacedText = replacedText.replace(new RegExp(key, "g"), `params.parameter.${patternMap[key]}`);
Object.keys(patternMap).forEach(pathParameterName => {
if (new RegExp(pathParameterName).test(replacedText)) {
const { text, escaped } = escapeText(patternMap[pathParameterName]);
const variableDeclaraText = escaped ? `params.parameter[${text}]` : `params.parameter.${text}`;
replacedText = replacedText.replace(new RegExp(pathParameterName, "g"), variableDeclaraText);
}
});
return replacedText === text ? undefined : replacedText;
Expand Down
86 changes: 86 additions & 0 deletions src/code-templates/api-client/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as Utils from "../utils";

type OK = Utils.VariableElement;

describe("Utils", () => {
test("splitVariableText", () => {
const splitVariableText = Utils.splitVariableText;
expect(splitVariableText("")).toStrictEqual<OK[]>([]);
expect(splitVariableText("a")).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
]);
expect(splitVariableText("a.b")).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
{
kind: "string",
value: "b",
},
]);
expect(splitVariableText("a.b.c")).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
{
kind: "string",
value: "b",
},

{
kind: "string",
value: "c",
},
]);
expect(splitVariableText('a.b["c"]')).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
{
kind: "string",
value: "b",
},

{
kind: "element-access",
value: "c",
},
]);
expect(splitVariableText('a.b["c.d"]')).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
{
kind: "string",
value: "b",
},

{
kind: "element-access",
value: "c.d",
},
]);
expect(splitVariableText('a.b["c.d.e"]')).toStrictEqual<OK[]>([
{
kind: "string",
value: "a",
},
{
kind: "string",
value: "b",
},

{
kind: "element-access",
value: "c.d.e",
},
]);
});
});
57 changes: 50 additions & 7 deletions src/code-templates/api-client/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,35 +140,78 @@ export const generateTemplateExpression = (factory: TsGenerator.Factory.Type, li
});
};

export interface VariableAccessIdentifer {
kind: "string";
value: string;
}

export interface ElementAccessExpression {
kind: "element-access";
value: string;
}

export type VariableElement = VariableAccessIdentifer | ElementAccessExpression;

export const splitVariableText = (text: string): VariableElement[] => {
// ["...."] にマッチする
const pattern = '["[a-zA-Z_0-9.]+"]';
// 'a.b.c["a"]["b"]'.split(/(\["[a-zA-Z_0-9\.]+"\])/g)
const splitTexts = text.split(/(\["[a-zA-Z_0-9\.]+"\])/g); // 区切り文字も含めて分割
return splitTexts.reduce<VariableElement[]>((splitList, value) => {
if (value === "") {
return splitList;
}
// ["book.name"] にマッチするか
if (new RegExp(pattern).test(value)) {
// ["book.name"] から book.name を抽出
const matchedValue = value.match(/[a-zA-Z_0-9\.]+/);
if (matchedValue) {
splitList.push({
kind: "element-access",
value: matchedValue[0],
});
}
return splitList;
} else {
const dotSplited = value.split(".");
const items = dotSplited.map<VariableAccessIdentifer>(childValue => ({ kind: "string", value: childValue }));
return splitList.concat(items);
}
}, []);
};

export const generateVariableIdentifier = (
factory: TsGenerator.Factory.Type,
name: string,
): ts.Identifier | ts.PropertyAccessExpression | ts.ElementAccessExpression => {
if (name.startsWith("/")) {
throw new Error("can't start '/'. name=" + name);
}
const list = name.split(".");
const list = splitVariableText(name);
// Object参照していない変数名の場合
if (list.length === 1) {
return factory.Identifier.create({
name: name,
});
}
const [n1, n2, ...rest] = list;
// a.b のような単純な変数名の場合
const first = factory.PropertyAccessExpression.create({
expression: n1,
name: n2,
expression: n1.value,
name: n2.value,
});

return rest.reduce<ts.PropertyAccessExpression | ts.ElementAccessExpression>((previous, current: string) => {
if (Utils.isAvailableVariableName(current)) {
return rest.reduce<ts.PropertyAccessExpression | ts.ElementAccessExpression>((previous, current) => {
if (current.kind === "string" && Utils.isAvailableVariableName(current.value)) {
return factory.PropertyAccessExpression.create({
expression: previous,
name: current,
name: current.value,
});
}
// 直接 .value でアクセスできない場合に ["value"] といった形で参照する
return factory.ElementAccessExpression.create({
expression: previous,
index: current,
index: current.value,
});
}, first);
};
Expand Down
14 changes: 14 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,17 @@ export const escapeText = (text: string): string => {
}
return `"${text}"`;
};

/** TODO escapeTextにマージする */
export const escapeText2 = (text: string): { escaped: boolean; text: string } => {
if (isAvailableVariableName(text)) {
return {
escaped: false,
text: text,
};
}
return {
escaped: true,
text: `"${text}"`,
};
};
72 changes: 72 additions & 0 deletions test/__tests__/__snapshots__/parameter-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,78 @@ exports[`Parameter api.test.domain 1`] = `
}
}
},
{
\\"operationId\\": \\"searchBook\\",
\\"convertedParams\\": {
\\"escapedOperationId\\": \\"searchBook\\",
\\"argumentParamsTypeDeclaration\\": \\"Params$searchBook\\",
\\"functionName\\": \\"searchBook\\",
\\"requestContentTypeName\\": \\"RequestContentType$searchBook\\",
\\"responseContentTypeName\\": \\"ResponseContentType$searchBook\\",
\\"parameterName\\": \\"Parameter$searchBook\\",
\\"requestBodyName\\": \\"RequestBody$searchBook\\",
\\"hasRequestBody\\": false,
\\"hasParameter\\": true,
\\"pickedParameters\\": [
{
\\"name\\": \\"book.name\\",
\\"in\\": \\"path\\",
\\"required\\": true
}
],
\\"requestContentTypes\\": [],
\\"responseSuccessNames\\": [
\\"Response$searchBook$Status$200\\"
],
\\"responseFirstSuccessName\\": \\"Response$searchBook$Status$200\\",
\\"has2OrMoreSuccessNames\\": false,
\\"responseErrorNames\\": [],
\\"has2OrMoreRequestContentTypes\\": false,
\\"successResponseContentTypes\\": [
\\"application/json\\"
],
\\"successResponseFirstContentType\\": \\"application/json\\",
\\"has2OrMoreSuccessResponseContentTypes\\": false,
\\"hasAdditionalHeaders\\": false,
\\"hasQueryParameters\\": false
},
\\"operationParams\\": {
\\"httpMethod\\": \\"get\\",
\\"requestUri\\": \\"/get/search/{book.name}\\",
\\"comment\\": \\"\\",
\\"deprecated\\": false,
\\"parameters\\": [
{
\\"in\\": \\"path\\",
\\"name\\": \\"book.name\\",
\\"required\\": true,
\\"schema\\": {
\\"type\\": \\"string\\"
}
}
],
\\"responses\\": {
\\"200\\": {
\\"description\\": \\"Search Book Result\\",
\\"content\\": {
\\"application/json\\": {
\\"schema\\": {
\\"type\\": \\"object\\",
\\"properties\\": {
\\"id\\": {
\\"type\\": \\"number\\"
},
\\"bookTitle\\": {
\\"type\\": \\"string\\"
}
}
}
}
}
}
}
}
},
{
\\"operationId\\": \\"getBookById\\",
\\"convertedParams\\": {
Expand Down
27 changes: 26 additions & 1 deletion test/__tests__/__snapshots__/spit-code-test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export interface Response$getReferenceItems$Status$200 {
books?: Schemas.Item[];
};
}
export interface Parameter$searchBook {
\\"book.name\\": string;
}
export interface Response$searchBook$Status$200 {
\\"application/json\\": {
id?: number;
bookTitle?: string;
};
}
export interface Parameter$getBookById {
/** Book ID */
id: string;
Expand Down Expand Up @@ -72,6 +81,10 @@ export interface Params$getFullRemoteReference {
parameter: Parameter$getFullRemoteReference;
}
export type ResponseContentType$getReferenceItems = keyof Response$getReferenceItems$Status$200;
export type ResponseContentType$searchBook = keyof Response$searchBook$Status$200;
export interface Params$searchBook {
parameter: Parameter$searchBook;
}
export type ResponseContentType$getBookById = keyof Response$getBookById$Status$200;
export interface Params$getBookById {
parameter: Parameter$getBookById;
Expand All @@ -92,12 +105,13 @@ export interface QueryParameter {
export interface QueryParameters {
[key: string]: QueryParameter;
}
export type SuccessResponses = Response$getIncludeLocalReference$Status$200 | Response$getFullRemoteReference$Status$200 | Response$getReferenceItems$Status$200 | Response$getBookById$Status$200 | Response$deleteBook$Status$200;
export type SuccessResponses = Response$getIncludeLocalReference$Status$200 | Response$getFullRemoteReference$Status$200 | Response$getReferenceItems$Status$200 | Response$searchBook$Status$200 | Response$getBookById$Status$200 | Response$deleteBook$Status$200;
export namespace ErrorResponse {
export type getIncludeLocalReference = void;
export type getIncludeRemoteReference = void;
export type getFullRemoteReference = void;
export type getReferenceItems = void;
export type searchBook = void;
export type getBookById = void;
export type deleteBook = void;
}
Expand Down Expand Up @@ -160,6 +174,17 @@ export class Client<RequestOption> {
};
return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option);
}
/**
* operationId: searchBook
* Request URI: /get/search/{book.name}
*/
public async searchBook(params: Params$searchBook, option?: RequestOption): Promise<Response$searchBook$Status$200[\\"application/json\\"]> {
const url = this.baseUrl + \`/get/search/\${params.parameter[\\"book.name\\"]}\`;
const headers = {
Accept: \\"application/json\\"
};
return this.apiClient.request(\\"GET\\", url, headers, undefined, undefined, option);
}
/**
* operationId: getBookById
* Request URI: /get/books/{id}
Expand Down
Loading