@accelbyte/codegen 1.0.0-alpha.8 → 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PATCHING.md +137 -0
- package/README.md +7 -137
- package/dist/accelbyte-codegen.js +587 -186
- package/dist/accelbyte-codegen.js.map +1 -1
- package/dist/accelbyte-codegen.mjs +588 -187
- package/dist/accelbyte-codegen.mjs.map +1 -1
- package/package.json +9 -6
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import yargs from 'yargs';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
|
-
import fs__default from 'fs';
|
|
4
|
+
import fs__default, { readFileSync, writeFileSync } from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import path__default from 'path';
|
|
7
7
|
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
8
|
-
import _ from 'lodash';
|
|
9
8
|
import { applyPatch } from 'fast-json-patch';
|
|
9
|
+
import _ from 'lodash';
|
|
10
10
|
import * as https from 'https';
|
|
11
11
|
|
|
12
12
|
const SwaggersConfig = z.array(z.array(z.string()));
|
|
@@ -51,17 +51,55 @@ class CliParser {
|
|
|
51
51
|
static isAdmin = () => {
|
|
52
52
|
return CliParser.instance().argv.admin;
|
|
53
53
|
};
|
|
54
|
+
static getSnippetOutputPath = () => {
|
|
55
|
+
return CliParser.instance().argv.snippetOutput;
|
|
56
|
+
};
|
|
54
57
|
}
|
|
55
58
|
|
|
56
|
-
const getImportableVarMap = () => ({
|
|
59
|
+
const getImportableVarMap$1 = () => ({
|
|
57
60
|
"@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate"],
|
|
58
61
|
axios: ["AxiosRequestConfig", "AxiosResponse"],
|
|
59
62
|
zod: ["z"]
|
|
60
63
|
});
|
|
61
|
-
const makeNewImportVarMap = () => ({
|
|
64
|
+
const makeNewImportVarMap$1 = () => ({
|
|
62
65
|
axios: ["AxiosInstance"],
|
|
63
66
|
"@accelbyte/sdk": ["SDKRequestConfig"]
|
|
64
67
|
});
|
|
68
|
+
const generateImports$1 = (body, importStatements) => {
|
|
69
|
+
const usedImportVarMap = makeNewImportVarMap$1();
|
|
70
|
+
const importableVarMap = getImportableVarMap$1();
|
|
71
|
+
for (const [moduleSource, importableVars] of Object.entries(importableVarMap)) {
|
|
72
|
+
for (const importableVar of importableVars) {
|
|
73
|
+
const importVarRegex = new RegExp(`(?<![\\d\\w_])${importableVar}(?![\\d\\w_])`);
|
|
74
|
+
if (body.match(importVarRegex)) {
|
|
75
|
+
usedImportVarMap[moduleSource] = [...usedImportVarMap[moduleSource] || [], importableVar];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const generatedImports = Object.keys(usedImportVarMap).sort().map((moduleSource) => `import { ${usedImportVarMap[moduleSource].sort().join(", ")} } from '${moduleSource}'`).join("\n");
|
|
80
|
+
return `${generatedImports}
|
|
81
|
+
${importStatements.sort().join("\n")}`;
|
|
82
|
+
};
|
|
83
|
+
const templateClass = (className, body, importStatements) => {
|
|
84
|
+
return `/**
|
|
85
|
+
* AUTO GENERATED
|
|
86
|
+
*/
|
|
87
|
+
${generateImports$1(body, importStatements)}
|
|
88
|
+
|
|
89
|
+
export class ${className} {
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
|
|
92
|
+
${body}
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const getImportableVarMap = () => ({
|
|
98
|
+
"@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate", "ApiArgs", "Network", "AccelbyteSDK"]
|
|
99
|
+
});
|
|
100
|
+
const makeNewImportVarMap = () => ({
|
|
101
|
+
"@accelbyte/sdk": ["AccelbyteSDK", "ApiArgs", "ApiUtils"]
|
|
102
|
+
});
|
|
65
103
|
const generateImports = (body, importStatements) => {
|
|
66
104
|
const usedImportVarMap = makeNewImportVarMap();
|
|
67
105
|
const importableVarMap = getImportableVarMap();
|
|
@@ -77,27 +115,55 @@ const generateImports = (body, importStatements) => {
|
|
|
77
115
|
return `${generatedImports}
|
|
78
116
|
${importStatements.sort().join("\n")}`;
|
|
79
117
|
};
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
const templateApiClass = (className, body, importStatements, returnMethods) => {
|
|
119
|
+
return `/**
|
|
120
|
+
* AUTO GENERATED
|
|
121
|
+
*/
|
|
122
|
+
/* eslint-disable camelcase */
|
|
123
|
+
${generateImports(body, importStatements)}
|
|
84
124
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
125
|
+
export function ${className}(sdk: AccelbyteSDK, args?: ApiArgs) {
|
|
126
|
+
const sdkAssembly = sdk.assembly()
|
|
127
|
+
|
|
128
|
+
const namespace = args?.namespace ? args?.namespace : sdkAssembly.namespace
|
|
129
|
+
const cache = args?.cache ? args?.cache : sdkAssembly.cache
|
|
130
|
+
const requestConfig = ApiUtils.mergedConfigs(sdkAssembly.config, args)
|
|
88
131
|
${body}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
${returnMethods}
|
|
89
135
|
}
|
|
136
|
+
}
|
|
90
137
|
`;
|
|
138
|
+
};
|
|
91
139
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
140
|
+
const UNDEFINED_SWAGGER_SEMVER = "0.0.0";
|
|
141
|
+
const REMOVED_KEYWORDS = [
|
|
142
|
+
"/admin/",
|
|
143
|
+
"/public/",
|
|
144
|
+
"/v1/",
|
|
145
|
+
"/v2/",
|
|
146
|
+
"/v3/",
|
|
147
|
+
"/v4/",
|
|
148
|
+
"/v5/",
|
|
149
|
+
"/namespace/",
|
|
150
|
+
"/namespaces/",
|
|
151
|
+
"/{namespace}/"
|
|
152
|
+
];
|
|
100
153
|
class ParserUtils {
|
|
154
|
+
static replaceAll = (string, search, replace) => {
|
|
155
|
+
return string.split(search).join(replace);
|
|
156
|
+
};
|
|
157
|
+
static generateClassName = (tag) => {
|
|
158
|
+
const className = _.upperFirst(_.camelCase(tag));
|
|
159
|
+
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
160
|
+
return { className, classGenName };
|
|
161
|
+
};
|
|
162
|
+
static generateApiName = (tag) => {
|
|
163
|
+
const apiName = _.upperFirst(_.camelCase(tag));
|
|
164
|
+
const apiGenName = CliParser.isAdmin() ? apiName + "AdminApi" : apiName + "Api";
|
|
165
|
+
return { apiName, apiGenName };
|
|
166
|
+
};
|
|
101
167
|
static parseQueryParamAttributeDefault = (definition) => {
|
|
102
168
|
const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
|
|
103
169
|
const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
|
|
@@ -115,17 +181,26 @@ class ParserUtils {
|
|
|
115
181
|
return pathParam.type;
|
|
116
182
|
};
|
|
117
183
|
static parseQueryParamsType = (queryParams) => {
|
|
118
|
-
return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(",");
|
|
184
|
+
return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(", ");
|
|
119
185
|
};
|
|
120
186
|
static isAnyQueryParamRequired = (queryParams) => {
|
|
121
187
|
return queryParams.some((queryParam) => queryParam.required);
|
|
122
188
|
};
|
|
189
|
+
static convertDashesToTitleCase = (str) => {
|
|
190
|
+
const result = str.split("-").map((word, index) => {
|
|
191
|
+
if (index === 0) {
|
|
192
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
193
|
+
}
|
|
194
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
195
|
+
}).join("");
|
|
196
|
+
return result;
|
|
197
|
+
};
|
|
123
198
|
static parseQueryParamsDefault = (queryParams) => {
|
|
124
199
|
const result = queryParams.filter((queryParam) => !!queryParam.default && !queryParam.required).map(ParserUtils.parseQueryParamAttributeDefault).join(",");
|
|
125
200
|
return result ? `${result},` : "";
|
|
126
201
|
};
|
|
127
202
|
static parseBodyParamsImports = (bodyParams) => {
|
|
128
|
-
return bodyParams.map(ParserUtils.parseRefImport).filter(Boolean);
|
|
203
|
+
return bodyParams.map((bodyParams2) => ParserUtils.parseRefImport(bodyParams2)).filter(Boolean);
|
|
129
204
|
};
|
|
130
205
|
static parseImportDir = ($ref) => {
|
|
131
206
|
let ref = $ref.replace(".", "/");
|
|
@@ -144,7 +219,7 @@ class ParserUtils {
|
|
|
144
219
|
return null;
|
|
145
220
|
}
|
|
146
221
|
const type = ParserUtils.parseRefType($ref);
|
|
147
|
-
return `import { ${type} } from '
|
|
222
|
+
return `import { ${type} } from '../definitions/${type}'`;
|
|
148
223
|
};
|
|
149
224
|
static parseRefType = ($ref) => {
|
|
150
225
|
let ref = $ref.replace(".", "/");
|
|
@@ -238,34 +313,59 @@ class ParserUtils {
|
|
|
238
313
|
}
|
|
239
314
|
return parameters.filter((parameter) => parameter.in === "path");
|
|
240
315
|
}
|
|
241
|
-
static
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
316
|
+
static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
|
|
317
|
+
let path_ = path2;
|
|
318
|
+
path_ = path_.replace(`/${servicePrefix}/`, "/");
|
|
319
|
+
REMOVED_KEYWORDS.forEach((prefix) => {
|
|
320
|
+
path_ = path_.replace(prefix, "/");
|
|
321
|
+
});
|
|
322
|
+
path_ = path_.substring(1);
|
|
323
|
+
const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
|
|
324
|
+
if (!isPlural) {
|
|
325
|
+
path_ = ParserUtils.replaceAll(path_, "ies/", "y/");
|
|
326
|
+
path_ = ParserUtils.replaceAll(path_, "s/", "/");
|
|
327
|
+
if (path_.indexOf("status") < 0) {
|
|
328
|
+
path_ = path_.replace(/ies$/, "y");
|
|
329
|
+
path_ = path_.replace(/s$/, "");
|
|
255
330
|
}
|
|
256
331
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
332
|
+
const arrLastWords = path_.split("}/");
|
|
333
|
+
let lastWords = arrLastWords[arrLastWords.length - 1];
|
|
334
|
+
const extractLastWord = (lastWords_) => {
|
|
335
|
+
let res = lastWords_;
|
|
336
|
+
res = res.split("/{")[0];
|
|
337
|
+
return res;
|
|
338
|
+
};
|
|
339
|
+
lastWords = extractLastWord(lastWords);
|
|
340
|
+
if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
|
|
341
|
+
lastWords = arrLastWords[arrLastWords.length - 2];
|
|
342
|
+
lastWords = extractLastWord(lastWords);
|
|
343
|
+
}
|
|
344
|
+
const listBeforeLastWords = [];
|
|
345
|
+
let foundParam = false;
|
|
346
|
+
const listByParams = [];
|
|
347
|
+
const pathElements = path_.split("/");
|
|
348
|
+
pathElements.slice().reverse().forEach((item) => {
|
|
349
|
+
if (item.indexOf("}") >= 0) {
|
|
350
|
+
foundParam = true;
|
|
351
|
+
let param = item.replace("{", "");
|
|
352
|
+
param = param.replace("}", "");
|
|
353
|
+
param = "Byword" + _.upperFirst(param);
|
|
354
|
+
listByParams.push(param);
|
|
355
|
+
} else if (!foundParam) {
|
|
356
|
+
if (lastWords.indexOf(item) === -1) {
|
|
357
|
+
listBeforeLastWords.push(item);
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
foundParam = false;
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
|
|
364
|
+
let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
|
|
365
|
+
generatedMethod = ParserUtils.replaceAll(generatedMethod, "Byword", "_By");
|
|
366
|
+
generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
|
|
367
|
+
return generatedMethod;
|
|
368
|
+
};
|
|
269
369
|
static filterBodyParams(parameters) {
|
|
270
370
|
if (Array.isArray(parameters) && parameters.length > 0) {
|
|
271
371
|
return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
|
|
@@ -287,9 +387,22 @@ class ParserUtils {
|
|
|
287
387
|
const fileContent = templateClass(apiName, apiBuffer, imports);
|
|
288
388
|
fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
289
389
|
}
|
|
290
|
-
static
|
|
291
|
-
const
|
|
292
|
-
|
|
390
|
+
static writeApiFile(distDir, apiName, apiBuffer, imports, returnMethods) {
|
|
391
|
+
const newImports = [];
|
|
392
|
+
imports.forEach((el, index) => {
|
|
393
|
+
newImports.push(el.replace("../definitions", "./definitions"));
|
|
394
|
+
});
|
|
395
|
+
const fileContent = templateApiClass(apiName, apiBuffer, newImports, returnMethods);
|
|
396
|
+
fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
397
|
+
}
|
|
398
|
+
static writeApiMainFile(distDir, serviceName, fileContent) {
|
|
399
|
+
fs__default.writeFileSync(`${distDir}/${serviceName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
400
|
+
}
|
|
401
|
+
static writeSnippetFile(distDir, name, docBuffer) {
|
|
402
|
+
let snippetFileName = ParserUtils.replaceAll(name, " ", "-").toLowerCase();
|
|
403
|
+
snippetFileName = snippetFileName.replace("justice-", "");
|
|
404
|
+
snippetFileName = "snippet-" + snippetFileName + ".json";
|
|
405
|
+
fs__default.writeFileSync(`${distDir}/${snippetFileName}`, docBuffer);
|
|
293
406
|
}
|
|
294
407
|
static writeDefinitionFile(distDir, name, buffer) {
|
|
295
408
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
@@ -299,6 +412,37 @@ class ParserUtils {
|
|
|
299
412
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
300
413
|
fs__default.writeFileSync(path__default.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
301
414
|
}
|
|
415
|
+
static syncChangelog(packageVersion) {
|
|
416
|
+
const currDir = process.cwd();
|
|
417
|
+
const pathToChangelog = path__default.join(currDir, "./CHANGELOG.md");
|
|
418
|
+
let fileContent = readFileSync(pathToChangelog, "utf-8");
|
|
419
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
420
|
+
fileContent = "### " + packageVersion + " - " + date + `
|
|
421
|
+
|
|
422
|
+
- code-generated update
|
|
423
|
+
|
|
424
|
+
` + fileContent.trim();
|
|
425
|
+
fs__default.writeFileSync(pathToChangelog, fileContent, "utf-8");
|
|
426
|
+
}
|
|
427
|
+
static syncPackageVersion(apiInfo, isAdminWebSdk) {
|
|
428
|
+
if (isAdminWebSdk) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const currDir = process.cwd();
|
|
432
|
+
const { packageJSON, pathToPackageJSON } = ParserUtils.getPackageJSONInfo(currDir);
|
|
433
|
+
let swaggerVersion = apiInfo.version ? apiInfo.version : UNDEFINED_SWAGGER_SEMVER;
|
|
434
|
+
swaggerVersion = Number(swaggerVersion.replace(".", "").replace(".", ""));
|
|
435
|
+
swaggerVersion = isNaN(swaggerVersion) ? 0 : swaggerVersion;
|
|
436
|
+
const semver = packageJSON.version.split(".").map((numStr) => Number(numStr));
|
|
437
|
+
const newSemver = [semver[0], swaggerVersion, ++semver[2]];
|
|
438
|
+
packageJSON.version = newSemver[0] + "." + newSemver[1] + "." + newSemver[2];
|
|
439
|
+
writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
|
|
440
|
+
ParserUtils.syncChangelog(packageJSON.version);
|
|
441
|
+
}
|
|
442
|
+
static getPackageJSONInfo(dir) {
|
|
443
|
+
const pathToPackageJSON = path__default.join(dir, "./package.json");
|
|
444
|
+
return { packageJSON: JSON.parse(readFileSync(pathToPackageJSON, "utf-8")), pathToPackageJSON };
|
|
445
|
+
}
|
|
302
446
|
static toCamelCase(str) {
|
|
303
447
|
return str.split("/").map(function(word, index) {
|
|
304
448
|
if (index === 0) {
|
|
@@ -332,6 +476,29 @@ class ParserUtils {
|
|
|
332
476
|
}
|
|
333
477
|
const swaggerContent = JSON.parse(fs__default.readFileSync(swaggerFilePath, "utf8"));
|
|
334
478
|
const swaggerPatchFileContent = JSON.parse(fs__default.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
|
|
479
|
+
for (const patchEntry of swaggerPatchFileContent) {
|
|
480
|
+
const segments = patchEntry.path.split("/").filter(Boolean);
|
|
481
|
+
let currentNode = swaggerContent;
|
|
482
|
+
let aggregatedPath = "";
|
|
483
|
+
for (let i = 0; i < segments.length; i++) {
|
|
484
|
+
const segment = segments[i];
|
|
485
|
+
aggregatedPath += `/${segment}`;
|
|
486
|
+
const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
|
|
487
|
+
if (!currentNode[effectiveSegment]) {
|
|
488
|
+
if (i + 1 === segments.length && patchEntry.op === "add") ; else {
|
|
489
|
+
throw new Error([
|
|
490
|
+
`JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
|
|
491
|
+
`,
|
|
492
|
+
"1. The related service has patched the service, so patch is no longer needed.",
|
|
493
|
+
"2. There is a breaking change on the service that causes the path to change.\n",
|
|
494
|
+
`In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
|
|
495
|
+
`
|
|
496
|
+
].join("\n"));
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
currentNode = currentNode[effectiveSegment];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
335
502
|
const { newDocument } = applyPatch(swaggerContent, swaggerPatchFileContent);
|
|
336
503
|
fs__default.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
|
|
337
504
|
}
|
|
@@ -349,6 +516,67 @@ class ParserUtils {
|
|
|
349
516
|
${content}`;
|
|
350
517
|
};
|
|
351
518
|
}
|
|
519
|
+
const mappedMethod = (httpMethod, isForm) => {
|
|
520
|
+
if (httpMethod === "get") {
|
|
521
|
+
return "get";
|
|
522
|
+
} else if (httpMethod === "post" && isForm) {
|
|
523
|
+
return "post";
|
|
524
|
+
} else if (httpMethod === "post") {
|
|
525
|
+
return "create";
|
|
526
|
+
} else if (httpMethod === "put") {
|
|
527
|
+
return "update";
|
|
528
|
+
} else if (httpMethod === "patch") {
|
|
529
|
+
return "patch";
|
|
530
|
+
} else if (httpMethod === "delete") {
|
|
531
|
+
return "delete";
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
const resolveConflicts = (path2, generatedMethod, existingMethods) => {
|
|
535
|
+
try {
|
|
536
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
537
|
+
} catch (e) {
|
|
538
|
+
if (path2.indexOf("/namespaces/") >= 0) {
|
|
539
|
+
generatedMethod += "_ByNS";
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
544
|
+
} catch (e) {
|
|
545
|
+
if (path2.indexOf("/v4/") >= 0) {
|
|
546
|
+
generatedMethod += "_v4";
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
try {
|
|
550
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
551
|
+
} catch (e) {
|
|
552
|
+
if (path2.indexOf("/v3/") >= 0) {
|
|
553
|
+
generatedMethod += "_v3";
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
try {
|
|
557
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
558
|
+
} catch (e) {
|
|
559
|
+
if (path2.indexOf("/v2/") >= 0) {
|
|
560
|
+
generatedMethod += "_v2";
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
try {
|
|
564
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
565
|
+
} catch (e) {
|
|
566
|
+
if (path2.indexOf("/admin/") >= 0) {
|
|
567
|
+
generatedMethod += "_admin";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
571
|
+
return generatedMethod;
|
|
572
|
+
};
|
|
573
|
+
const testConflict = (path2, generatedMethod, existingMethods) => {
|
|
574
|
+
if (existingMethods[generatedMethod]) {
|
|
575
|
+
const conflictingMethod = { path: path2, generatedMethod };
|
|
576
|
+
throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
|
|
577
|
+
existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
|
|
578
|
+
}
|
|
579
|
+
};
|
|
352
580
|
|
|
353
581
|
const Schema = z.object({
|
|
354
582
|
$ref: z.string().nullish(),
|
|
@@ -440,50 +668,6 @@ z.object({
|
|
|
440
668
|
}).nullish()
|
|
441
669
|
});
|
|
442
670
|
|
|
443
|
-
const templateJsdocMethod = ({
|
|
444
|
-
classMethod,
|
|
445
|
-
httpMethod,
|
|
446
|
-
path,
|
|
447
|
-
pathParams,
|
|
448
|
-
bodyParams,
|
|
449
|
-
queryParams
|
|
450
|
-
}) => {
|
|
451
|
-
let jsdoc = "";
|
|
452
|
-
let methodSignature = "";
|
|
453
|
-
let newPath = path;
|
|
454
|
-
for (const p of pathParams) {
|
|
455
|
-
methodSignature += p.name + ", ";
|
|
456
|
-
newPath = newPath.replace("{" + p.name + "}", `' + ${p.name} + '`);
|
|
457
|
-
jsdoc += `
|
|
458
|
-
* @pathParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
|
|
459
|
-
}
|
|
460
|
-
let payloads = "";
|
|
461
|
-
for (const p of bodyParams) {
|
|
462
|
-
methodSignature += p.name + ", ";
|
|
463
|
-
payloads += p.name;
|
|
464
|
-
jsdoc += `
|
|
465
|
-
* @payload '${p.name}${p.required ? "" : "?"}' - ${p.description} ${p?.schema?.properties ? "=> " + JSON.stringify(p.schema.properties) : ""}`;
|
|
466
|
-
}
|
|
467
|
-
for (const p of queryParams) {
|
|
468
|
-
const optionalMark = p.required ? "" : "?";
|
|
469
|
-
methodSignature += p.name + optionalMark + ", ";
|
|
470
|
-
jsdoc += `
|
|
471
|
-
* @queryParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
|
|
472
|
-
}
|
|
473
|
-
let queryString = "";
|
|
474
|
-
for (const p of queryParams) {
|
|
475
|
-
queryString += `${p.name}=__&`;
|
|
476
|
-
}
|
|
477
|
-
queryString = queryString.length > 0 ? "?" + queryString : "";
|
|
478
|
-
return `
|
|
479
|
-
/**
|
|
480
|
-
* ${httpMethod.toUpperCase()} '${newPath}${queryString}' ${payloads ? "payload: '" + payloads + "'" : ""}
|
|
481
|
-
* ${jsdoc}
|
|
482
|
-
*/
|
|
483
|
-
${classMethod}(${methodSignature}): Promise
|
|
484
|
-
`;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
671
|
const templateMethod = ({
|
|
488
672
|
classMethod,
|
|
489
673
|
description,
|
|
@@ -495,13 +679,17 @@ const templateMethod = ({
|
|
|
495
679
|
isFormUrlEncoded,
|
|
496
680
|
responseClass
|
|
497
681
|
}) => {
|
|
498
|
-
let
|
|
682
|
+
let methodParams = "";
|
|
683
|
+
let methodParamsNoTypes = "";
|
|
499
684
|
let newPath = `'${path}'`;
|
|
500
|
-
let
|
|
685
|
+
let importStatements = [];
|
|
686
|
+
let snippetString = "";
|
|
687
|
+
let snippetMethodSignature = "";
|
|
501
688
|
for (const pathParam of pathParams) {
|
|
502
689
|
const type = ParserUtils.parseType(pathParam);
|
|
503
690
|
if (pathParam.name !== "namespace") {
|
|
504
|
-
|
|
691
|
+
methodParams += pathParam.name + `:${type}, `;
|
|
692
|
+
methodParamsNoTypes += pathParam.name + ", ";
|
|
505
693
|
}
|
|
506
694
|
const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
|
|
507
695
|
if (path.match(`{${pathParam.name}}`)) {
|
|
@@ -515,8 +703,16 @@ const templateMethod = ({
|
|
|
515
703
|
let dataType = null;
|
|
516
704
|
if (httpMethod !== "get") {
|
|
517
705
|
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
518
|
-
|
|
519
|
-
|
|
706
|
+
importStatements = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
707
|
+
methodParams += dataType ? `data: ${dataType},` : "";
|
|
708
|
+
methodParamsNoTypes += dataType ? `data,` : "";
|
|
709
|
+
const snippetParams = bodyParams?.map((ob) => {
|
|
710
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
711
|
+
});
|
|
712
|
+
snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
|
|
713
|
+
bodyParams?.map((ob) => {
|
|
714
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
715
|
+
});
|
|
520
716
|
}
|
|
521
717
|
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
522
718
|
const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
@@ -526,7 +722,7 @@ const templateMethod = ({
|
|
|
526
722
|
let dataPayload = "{params}";
|
|
527
723
|
const descriptionText = description ? `
|
|
528
724
|
/**
|
|
529
|
-
* ${
|
|
725
|
+
* ${description.replace(/\n/g, "\n * ")}
|
|
530
726
|
*/` : "";
|
|
531
727
|
let formPayloadString = "";
|
|
532
728
|
if (isFormUrlEncoded) {
|
|
@@ -538,47 +734,57 @@ const templateMethod = ({
|
|
|
538
734
|
} else if (isDelete) {
|
|
539
735
|
dataPayload = dataType ? `{data, params}` : "{params}";
|
|
540
736
|
}
|
|
541
|
-
const isFileUpload =
|
|
737
|
+
const isFileUpload = methodParams.indexOf("data: {file") > -1;
|
|
542
738
|
const resolvedResponseClass = responseClass || "unknown";
|
|
543
739
|
const resolvedResponseClassValidated = responseClass || "z.unknown()";
|
|
544
|
-
|
|
740
|
+
methodParams = (queryParamsType ? `${methodParams} ${queryParamsType}` : methodParams).replace(/,\s*$/, "");
|
|
741
|
+
methodParamsNoTypes = queryParamsType ? `${methodParamsNoTypes} queryParams` : methodParamsNoTypes;
|
|
545
742
|
let methodImpl = "";
|
|
546
743
|
const isCacheFetch = ["get"].includes(httpMethod) && resolvedResponseClass !== "unknown";
|
|
547
|
-
const cachedFetchMethod = classMethod
|
|
744
|
+
const cachedFetchMethod = classMethod;
|
|
548
745
|
const deprecateTag = isCacheFetch ? `/**
|
|
549
|
-
* @deprecated Use "${
|
|
746
|
+
* @deprecated Use "${classMethod}()" instead.
|
|
550
747
|
*/` : "";
|
|
551
748
|
const isGuardInvoked = ["get", "post", "put", "patch", "delete"].includes(httpMethod);
|
|
552
749
|
const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
|
|
553
|
-
const
|
|
554
|
-
const
|
|
750
|
+
const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
|
|
751
|
+
const generateMethodName = () => `${methodName}(${methodParams}): Promise<${responseSyncType}<${responseType}>>`;
|
|
752
|
+
const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
|
|
753
|
+
snippetString += `${generateSnippetMethodName()}`;
|
|
555
754
|
const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
|
|
556
755
|
methodImpl = `${descriptionText}
|
|
557
|
-
${
|
|
756
|
+
${generateMethodName()} {
|
|
558
757
|
${queryParamsDefault}
|
|
559
758
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
560
759
|
const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
561
760
|
|
|
562
|
-
${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
761
|
+
${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
563
762
|
|
|
564
763
|
if (!this.cache) {
|
|
565
764
|
return SdkCache.withoutCache(res)
|
|
566
765
|
}
|
|
567
766
|
const cacheKey = url + CodeGenUtil.hashCode(JSON.stringify({ params }))
|
|
568
|
-
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? `return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
767
|
+
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
569
768
|
}
|
|
570
769
|
`;
|
|
571
770
|
if (!isGuardInvoked) {
|
|
572
771
|
methodImpl = `${descriptionText}
|
|
573
772
|
${deprecateTag}
|
|
574
|
-
TODO_${classMethod}
|
|
773
|
+
TODO_${classMethod}(${methodParams}): Promise<AxiosResponse<${resolvedResponseClass}>> {
|
|
575
774
|
${queryParamsDefault}
|
|
576
775
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
577
776
|
return this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
578
777
|
}
|
|
579
778
|
`;
|
|
580
779
|
}
|
|
581
|
-
|
|
780
|
+
const res = {
|
|
781
|
+
methodImpl,
|
|
782
|
+
methodParams,
|
|
783
|
+
methodParamsNoTypes,
|
|
784
|
+
importStatements,
|
|
785
|
+
snippetString
|
|
786
|
+
};
|
|
787
|
+
return res;
|
|
582
788
|
};
|
|
583
789
|
|
|
584
790
|
class TemplateZod {
|
|
@@ -594,32 +800,32 @@ class TemplateZod {
|
|
|
594
800
|
}
|
|
595
801
|
let imports = "";
|
|
596
802
|
for (const cl of Array.from(this.importClasses).sort()) {
|
|
597
|
-
imports += `
|
|
803
|
+
imports += `import { ${cl} } from './${cl}'
|
|
598
804
|
`;
|
|
599
805
|
}
|
|
600
806
|
let exportedVariableString;
|
|
601
807
|
let exportedTypeString;
|
|
602
808
|
if (containsRecursiveType) {
|
|
603
809
|
exportedVariableString = `
|
|
604
|
-
|
|
810
|
+
export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
|
|
605
811
|
${content.schemaString}
|
|
606
812
|
)
|
|
607
813
|
`;
|
|
608
814
|
exportedTypeString = `
|
|
609
|
-
|
|
815
|
+
export interface ${fileName} {
|
|
610
816
|
${content.typeString}
|
|
611
|
-
|
|
612
|
-
|
|
817
|
+
}
|
|
818
|
+
`;
|
|
613
819
|
} else {
|
|
614
820
|
exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
|
|
615
|
-
exportedTypeString = `export
|
|
821
|
+
exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
|
|
616
822
|
}
|
|
617
823
|
const template = `import { z } from 'zod'
|
|
618
|
-
|
|
824
|
+
${imports}
|
|
619
825
|
|
|
620
|
-
|
|
826
|
+
${exportedVariableString}
|
|
621
827
|
|
|
622
|
-
|
|
828
|
+
${exportedTypeString}
|
|
623
829
|
`;
|
|
624
830
|
return { buffer: template, duplicateFound: this.duplicateFound };
|
|
625
831
|
};
|
|
@@ -704,7 +910,25 @@ class TemplateZod {
|
|
|
704
910
|
typeString: refType
|
|
705
911
|
};
|
|
706
912
|
} else if (items) {
|
|
707
|
-
|
|
913
|
+
if (items.type === "array") {
|
|
914
|
+
const ref3 = items.items?.$ref;
|
|
915
|
+
if (ref3) {
|
|
916
|
+
const refType = ParserUtils.parseRefType(ref3);
|
|
917
|
+
this.importClasses.add(refType);
|
|
918
|
+
model2 = {
|
|
919
|
+
schemaString: refType,
|
|
920
|
+
typeString: refType
|
|
921
|
+
};
|
|
922
|
+
} else if (items.items) {
|
|
923
|
+
model2 = this.parseEnumItems(items.items);
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
schemaString: `${schemaAttribute} z.array(z.array(${model2.schemaString}))${schemaRequired}`,
|
|
927
|
+
typeString: `${typeAttribute} ${model2.typeString}[]${typeNullishability}`
|
|
928
|
+
};
|
|
929
|
+
} else {
|
|
930
|
+
model2 = this.parseEnumItems(items);
|
|
931
|
+
}
|
|
708
932
|
} else {
|
|
709
933
|
return {
|
|
710
934
|
schemaString: `${schemaAttribute} z.array(z.any())${schemaRequired}`,
|
|
@@ -776,11 +1000,11 @@ class TemplateZodArray {
|
|
|
776
1000
|
render = (name) => {
|
|
777
1001
|
const cls = name.replace("Array", "");
|
|
778
1002
|
const template = `import { z } from 'zod'
|
|
779
|
-
|
|
1003
|
+
import { ${cls} } from './${cls}'
|
|
780
1004
|
|
|
781
|
-
|
|
1005
|
+
export const ${name} = z.array(${cls})
|
|
782
1006
|
|
|
783
|
-
|
|
1007
|
+
export interface ${name} extends z.TypeOf<typeof ${name}> {}
|
|
784
1008
|
`;
|
|
785
1009
|
return template;
|
|
786
1010
|
};
|
|
@@ -815,24 +1039,147 @@ const extractEnumObject = (type, isRequired, enumArr) => {
|
|
|
815
1039
|
};
|
|
816
1040
|
};
|
|
817
1041
|
|
|
1042
|
+
const templateApiMethod = ({
|
|
1043
|
+
classMethod,
|
|
1044
|
+
description,
|
|
1045
|
+
httpMethod,
|
|
1046
|
+
path,
|
|
1047
|
+
pathParams,
|
|
1048
|
+
bodyParams,
|
|
1049
|
+
queryParams,
|
|
1050
|
+
responseClass,
|
|
1051
|
+
classGenName,
|
|
1052
|
+
methodParams,
|
|
1053
|
+
methodParamsNoTypes
|
|
1054
|
+
}) => {
|
|
1055
|
+
let methodSignature = "";
|
|
1056
|
+
let newPath = `'${path}'`;
|
|
1057
|
+
let dependencies = [];
|
|
1058
|
+
let snippetSdk = "";
|
|
1059
|
+
let snippetShell = "";
|
|
1060
|
+
for (const pathParam of pathParams) {
|
|
1061
|
+
const type = ParserUtils.parseType(pathParam);
|
|
1062
|
+
if (pathParam.name !== "namespace") {
|
|
1063
|
+
methodSignature += pathParam.name + `:${type}, `;
|
|
1064
|
+
}
|
|
1065
|
+
const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
|
|
1066
|
+
if (path.match(`{${pathParam.name}}`)) {
|
|
1067
|
+
if (type === "string") {
|
|
1068
|
+
newPath = `${newPath}.replace('{${pathParam.name}}', ${pName})`;
|
|
1069
|
+
} else {
|
|
1070
|
+
newPath = `${newPath}.replace('{${pathParam.name}}', String(${pName}))`;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
snippetShell = `<span class='sn-blue'>curl</span> --location --request <span class='sn-blue'>${httpMethod}</span> '<span class='sn-green'>__DOMAIN__${path}</span>' --header 'accept: application/json'`;
|
|
1075
|
+
let dataType = null;
|
|
1076
|
+
if (httpMethod !== "get") {
|
|
1077
|
+
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
1078
|
+
dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
1079
|
+
methodSignature += dataType ? `data: ${dataType},` : "";
|
|
1080
|
+
bodyParams?.map((ob) => {
|
|
1081
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
1082
|
+
});
|
|
1083
|
+
const curlParams = bodyParams?.map((ob) => {
|
|
1084
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
1085
|
+
});
|
|
1086
|
+
snippetShell += ` --data-raw { ${curlParams}}`;
|
|
1087
|
+
}
|
|
1088
|
+
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
1089
|
+
queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
1090
|
+
queryParams.length ? `const params = {${ParserUtils.parseQueryParamsDefault(queryParams)} ...queryParams} as SDKRequestConfig` : "const params = {} as SDKRequestConfig";
|
|
1091
|
+
description ? `
|
|
1092
|
+
/**
|
|
1093
|
+
* ${description.replace(/\n/g, "\n * ")}
|
|
1094
|
+
*/` : "";
|
|
1095
|
+
const resolvedResponseClass = responseClass || "unknown";
|
|
1096
|
+
const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
|
|
1097
|
+
const methodImpl = `
|
|
1098
|
+
async function ${classMethod}(${methodParams}): Promise<${responseType}> {
|
|
1099
|
+
const $ = new ${classGenName}(Network.create(requestConfig), namespace, cache)
|
|
1100
|
+
const resp = await $.${classMethod}(${methodParamsNoTypes})
|
|
1101
|
+
if (resp.error) throw resp.error
|
|
1102
|
+
return resp.response.data
|
|
1103
|
+
}
|
|
1104
|
+
`;
|
|
1105
|
+
snippetSdk += `${classMethod}(${methodParams}) // return Promise<${responseType}>`;
|
|
1106
|
+
return [methodImpl, dependencies, snippetSdk, snippetShell];
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1109
|
+
const templateApiIndex = (serviceName, serviceNameTitle, apiList) => {
|
|
1110
|
+
let imports = "";
|
|
1111
|
+
let returnStatement = "";
|
|
1112
|
+
for (const cl of apiList) {
|
|
1113
|
+
imports += `
|
|
1114
|
+
import { ${cl} } from './${serviceName}/${cl}'`;
|
|
1115
|
+
returnStatement += `
|
|
1116
|
+
${cl}, `;
|
|
1117
|
+
}
|
|
1118
|
+
return `/**
|
|
1119
|
+
* AUTO GENERATED
|
|
1120
|
+
*/
|
|
1121
|
+
${imports}
|
|
1122
|
+
|
|
1123
|
+
const apis = {
|
|
1124
|
+
${returnStatement}
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
export const ${ParserUtils.convertDashesToTitleCase(serviceNameTitle)} = apis
|
|
1128
|
+
`;
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
const templateSdkSnippet = (serviceNameTitle, apiName, methodSnippet) => {
|
|
1132
|
+
const sdkSnippet = `
|
|
1133
|
+
import { Accelbyte } from '@accelbyte/sdk'
|
|
1134
|
+
import { ${serviceNameTitle} } from '@accelbyte/sdk-${serviceNameTitle.toLowerCase()}'
|
|
1135
|
+
|
|
1136
|
+
const SDK_CONFIG = {
|
|
1137
|
+
baseURL: 'https://demo.accelbyte.io',
|
|
1138
|
+
clientId: '77f88506b6174c3ea4d925f5b4096ce8',
|
|
1139
|
+
namespace: 'accelbyte',
|
|
1140
|
+
redirectURI: 'http://localhost:3030'
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const sdk = Accelbyte.SDK({
|
|
1144
|
+
options: SDK_CONFIG
|
|
1145
|
+
})
|
|
1146
|
+
|
|
1147
|
+
${serviceNameTitle}.${apiName}(sdk).${methodSnippet}
|
|
1148
|
+
`;
|
|
1149
|
+
return sdkSnippet;
|
|
1150
|
+
};
|
|
1151
|
+
|
|
818
1152
|
class CodeGenerator {
|
|
819
1153
|
static getPatchedDir = () => path__default.join(CliParser.getSwaggersOutputPath(), "patched");
|
|
820
1154
|
static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
|
|
821
1155
|
static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
|
|
822
|
-
static
|
|
1156
|
+
static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
|
|
1157
|
+
static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
|
|
1158
|
+
static iterateApi = async (api, serviceName) => {
|
|
823
1159
|
const apiBufferByTag = {};
|
|
824
|
-
const
|
|
1160
|
+
const apiArgumentsByTag = {};
|
|
1161
|
+
const classBufferByTag = {};
|
|
825
1162
|
const dependenciesByTag = {};
|
|
826
1163
|
const classImports = {};
|
|
827
1164
|
let arrayDefinitions = [];
|
|
828
|
-
|
|
1165
|
+
const snippetMap = {};
|
|
1166
|
+
const mapClassMethods = {};
|
|
1167
|
+
const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
|
|
1168
|
+
if (a[0].length === b[0].length) {
|
|
1169
|
+
return a[0].localeCompare(b[0]);
|
|
1170
|
+
} else {
|
|
1171
|
+
return a[0].length - b[0].length;
|
|
1172
|
+
}
|
|
1173
|
+
}));
|
|
1174
|
+
const sortedKeys = Array.from(sortedPathsByLength.keys());
|
|
1175
|
+
const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
|
|
1176
|
+
for (const [path2, operation] of sortedPathsByLength) {
|
|
829
1177
|
const isAdminEndpoint = path2.indexOf("/admin/") > -1;
|
|
830
1178
|
if (CliParser.isAdmin() && !isAdminEndpoint) {
|
|
831
1179
|
continue;
|
|
832
1180
|
} else if (!CliParser.isAdmin() && isAdminEndpoint) {
|
|
833
1181
|
continue;
|
|
834
1182
|
}
|
|
835
|
-
const operation = api.paths[path2];
|
|
836
1183
|
const httpMethods = Object.keys(operation);
|
|
837
1184
|
for (const httpMethod of httpMethods) {
|
|
838
1185
|
const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
|
|
@@ -842,59 +1189,98 @@ class CodeGenerator {
|
|
|
842
1189
|
if (!endpoint.tags) {
|
|
843
1190
|
continue;
|
|
844
1191
|
}
|
|
1192
|
+
if (endpoint.deprecated) {
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
845
1195
|
const [tag] = endpoint.tags;
|
|
846
|
-
|
|
847
|
-
const
|
|
1196
|
+
mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
|
|
1197
|
+
const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
|
|
1198
|
+
const classMethod = ParserUtils.generateNaturalLangMethod({
|
|
1199
|
+
servicePrefix,
|
|
1200
|
+
path: path2,
|
|
1201
|
+
httpMethod,
|
|
1202
|
+
isForm,
|
|
1203
|
+
existingMethods: mapClassMethods[tag]
|
|
1204
|
+
});
|
|
1205
|
+
mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
|
|
1206
|
+
snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
|
|
1207
|
+
let description = endpoint.description;
|
|
1208
|
+
description = description || "";
|
|
1209
|
+
description = description.replace(/\s+/g, " ");
|
|
848
1210
|
const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
|
|
849
|
-
const className =
|
|
1211
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
850
1212
|
classImports[className] = classImports[className] ? classImports[className] : {};
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
{
|
|
872
|
-
name: "body",
|
|
873
|
-
in: "body",
|
|
874
|
-
schema: endpoint.requestBody.content["application/json"].schema
|
|
875
|
-
}
|
|
876
|
-
];
|
|
877
|
-
}
|
|
878
|
-
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
879
|
-
const [generatedMethodString, importStatements] = templateMethod({
|
|
880
|
-
classMethod,
|
|
881
|
-
description,
|
|
882
|
-
httpMethod,
|
|
883
|
-
path: pathWithBase,
|
|
884
|
-
pathParams,
|
|
885
|
-
bodyParams,
|
|
886
|
-
queryParams,
|
|
887
|
-
isFormUrlEncoded,
|
|
888
|
-
responseClass
|
|
889
|
-
});
|
|
890
|
-
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
|
|
891
|
-
jsDocApiBufferByTag[tag] = (jsDocApiBufferByTag[tag] || "") + templateJsdocMethod({ classMethod, httpMethod, path: path2, pathParams, bodyParams, queryParams });
|
|
892
|
-
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1213
|
+
if (responseClass) {
|
|
1214
|
+
const importTypeClass = ParserUtils.parseRefType(responseClass);
|
|
1215
|
+
classImports[className][importTypeClass] = `import { ${importTypeClass} } from '../definitions/${importTypeClass}'`;
|
|
1216
|
+
}
|
|
1217
|
+
if (responseClass && responseClass.endsWith("Array")) {
|
|
1218
|
+
arrayDefinitions.push(responseClass);
|
|
1219
|
+
}
|
|
1220
|
+
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
1221
|
+
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
1222
|
+
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
1223
|
+
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
1224
|
+
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
1225
|
+
if (endpoint.requestBody) {
|
|
1226
|
+
bodyParams = [
|
|
1227
|
+
{
|
|
1228
|
+
name: "body",
|
|
1229
|
+
in: "body",
|
|
1230
|
+
schema: endpoint.requestBody.content["application/json"].schema
|
|
1231
|
+
}
|
|
1232
|
+
];
|
|
893
1233
|
}
|
|
1234
|
+
const { methodImpl, methodParams, methodParamsNoTypes, importStatements, snippetString } = templateMethod({
|
|
1235
|
+
classMethod,
|
|
1236
|
+
description,
|
|
1237
|
+
httpMethod,
|
|
1238
|
+
path: pathWithBase,
|
|
1239
|
+
pathParams,
|
|
1240
|
+
bodyParams,
|
|
1241
|
+
queryParams,
|
|
1242
|
+
isFormUrlEncoded,
|
|
1243
|
+
responseClass
|
|
1244
|
+
});
|
|
1245
|
+
classBufferByTag[tag] = (classBufferByTag[tag] || "") + methodImpl;
|
|
1246
|
+
const [generatedMethodString1, importStatements1, snippetMethod, snippetShell] = templateApiMethod({
|
|
1247
|
+
classMethod,
|
|
1248
|
+
description,
|
|
1249
|
+
httpMethod,
|
|
1250
|
+
path: pathWithBase,
|
|
1251
|
+
pathParams,
|
|
1252
|
+
bodyParams,
|
|
1253
|
+
queryParams,
|
|
1254
|
+
responseClass,
|
|
1255
|
+
classGenName,
|
|
1256
|
+
methodParams,
|
|
1257
|
+
methodParamsNoTypes
|
|
1258
|
+
});
|
|
1259
|
+
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString1;
|
|
1260
|
+
apiArgumentsByTag[tag] = (apiArgumentsByTag[tag] || "") + classMethod + ",";
|
|
1261
|
+
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1262
|
+
const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
|
|
1263
|
+
const { apiGenName } = ParserUtils.generateApiName(tag);
|
|
1264
|
+
let resultSnippet = templateSdkSnippet(serviceNameTitle, apiGenName, snippetMethod);
|
|
1265
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, "<", "<");
|
|
1266
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, ">", ">");
|
|
1267
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, "\n", "<br/>");
|
|
1268
|
+
snippetMap[path2][httpMethod] = {
|
|
1269
|
+
web: resultSnippet,
|
|
1270
|
+
shell: snippetShell
|
|
1271
|
+
};
|
|
894
1272
|
}
|
|
895
1273
|
}
|
|
896
1274
|
arrayDefinitions = [...new Set(arrayDefinitions)];
|
|
897
|
-
return {
|
|
1275
|
+
return {
|
|
1276
|
+
apiArgumentsByTag,
|
|
1277
|
+
apiBufferByTag,
|
|
1278
|
+
classBufferByTag,
|
|
1279
|
+
dependenciesByTag,
|
|
1280
|
+
classImports,
|
|
1281
|
+
arrayDefinitions,
|
|
1282
|
+
snippetMap
|
|
1283
|
+
};
|
|
898
1284
|
};
|
|
899
1285
|
static main = async (nameArray) => {
|
|
900
1286
|
const serviceName = nameArray[0];
|
|
@@ -902,7 +1288,7 @@ class CodeGenerator {
|
|
|
902
1288
|
const parser = new SwaggerParser();
|
|
903
1289
|
const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
|
|
904
1290
|
const DIST_DIR = `${generatedFolder}/${serviceName}`;
|
|
905
|
-
const
|
|
1291
|
+
const DIST_ENDPOINTS_DIR = path__default.join(DIST_DIR, "endpoints");
|
|
906
1292
|
const DIST_DEFINITION_DIR = path__default.join(DIST_DIR, "definitions");
|
|
907
1293
|
const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
|
|
908
1294
|
const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
|
|
@@ -910,20 +1296,35 @@ class CodeGenerator {
|
|
|
910
1296
|
ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
|
|
911
1297
|
const api = await parser.parse(swaggerPatchedFilePath);
|
|
912
1298
|
const indexImportsSet = /* @__PURE__ */ new Set();
|
|
913
|
-
console.log("
|
|
1299
|
+
console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
|
|
914
1300
|
ParserUtils.mkdirIfNotExist(DIST_DIR);
|
|
915
|
-
ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
|
|
916
1301
|
ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
|
|
917
|
-
|
|
1302
|
+
ParserUtils.mkdirIfNotExist(DIST_ENDPOINTS_DIR);
|
|
1303
|
+
ParserUtils.syncPackageVersion(api.info, CliParser.isAdmin());
|
|
1304
|
+
const { apiArgumentsByTag, apiBufferByTag, classBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api, serviceName);
|
|
1305
|
+
if (CliParser.getSnippetOutputPath()) {
|
|
1306
|
+
ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
|
|
1307
|
+
}
|
|
918
1308
|
const targetSrcFolder = `${CliParser.getOutputPath()}/`;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
const
|
|
1309
|
+
const apiList = [];
|
|
1310
|
+
for (const tag in classBufferByTag) {
|
|
1311
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
1312
|
+
const classBuffer = classBufferByTag[tag];
|
|
922
1313
|
const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
1314
|
+
const apiImports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
1315
|
+
apiImports.push(`import { ${classGenName} } from './endpoints/${classGenName}'`);
|
|
1316
|
+
ParserUtils.writeClassFile(DIST_ENDPOINTS_DIR, classGenName, classBuffer, imports);
|
|
1317
|
+
const apiBuffer = apiBufferByTag[tag];
|
|
1318
|
+
const { apiGenName } = ParserUtils.generateApiName(tag);
|
|
1319
|
+
ParserUtils.writeApiFile(DIST_DIR, apiGenName, apiBuffer, apiImports, apiArgumentsByTag[tag]);
|
|
1320
|
+
apiList.push(apiGenName);
|
|
1321
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_ENDPOINTS_DIR, `${classGenName}`), targetSrcFolder));
|
|
1322
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${apiGenName}`), targetSrcFolder));
|
|
926
1323
|
}
|
|
1324
|
+
const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
|
|
1325
|
+
const apiIndexBuff = templateApiIndex(serviceName, serviceNameTitle, apiList);
|
|
1326
|
+
ParserUtils.writeApiMainFile(generatedFolder, serviceNameTitle, apiIndexBuff);
|
|
1327
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(generatedFolder, serviceNameTitle), targetSrcFolder));
|
|
927
1328
|
const duplicates = /* @__PURE__ */ new Map();
|
|
928
1329
|
const definitions = api?.components?.schemas || api.definitions;
|
|
929
1330
|
for (const ref in definitions) {
|
|
@@ -952,7 +1353,7 @@ class CodeGenerator {
|
|
|
952
1353
|
ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
|
|
953
1354
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
|
|
954
1355
|
}
|
|
955
|
-
console.log("\
|
|
1356
|
+
console.log("\nCOMPLETED\n----------\n\n");
|
|
956
1357
|
return indexImportsSet;
|
|
957
1358
|
};
|
|
958
1359
|
}
|
|
@@ -979,7 +1380,7 @@ class SwaggerDownloader {
|
|
|
979
1380
|
});
|
|
980
1381
|
});
|
|
981
1382
|
request.on("error", (err) => {
|
|
982
|
-
console.log(`SwaggerDownloader
|
|
1383
|
+
console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
|
|
983
1384
|
});
|
|
984
1385
|
};
|
|
985
1386
|
static main = () => {
|