@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
|
@@ -7,8 +7,8 @@ var zod = require('zod');
|
|
|
7
7
|
var fs = require('fs');
|
|
8
8
|
var path = require('path');
|
|
9
9
|
var SwaggerParser = require('@apidevtools/swagger-parser');
|
|
10
|
-
var _ = require('lodash');
|
|
11
10
|
var fastJsonPatch = require('fast-json-patch');
|
|
11
|
+
var _ = require('lodash');
|
|
12
12
|
var https = require('https');
|
|
13
13
|
|
|
14
14
|
function _interopNamespaceDefault(e) {
|
|
@@ -74,17 +74,55 @@ class CliParser {
|
|
|
74
74
|
static isAdmin = () => {
|
|
75
75
|
return CliParser.instance().argv.admin;
|
|
76
76
|
};
|
|
77
|
+
static getSnippetOutputPath = () => {
|
|
78
|
+
return CliParser.instance().argv.snippetOutput;
|
|
79
|
+
};
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
const getImportableVarMap = () => ({
|
|
82
|
+
const getImportableVarMap$1 = () => ({
|
|
80
83
|
"@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate"],
|
|
81
84
|
axios: ["AxiosRequestConfig", "AxiosResponse"],
|
|
82
85
|
zod: ["z"]
|
|
83
86
|
});
|
|
84
|
-
const makeNewImportVarMap = () => ({
|
|
87
|
+
const makeNewImportVarMap$1 = () => ({
|
|
85
88
|
axios: ["AxiosInstance"],
|
|
86
89
|
"@accelbyte/sdk": ["SDKRequestConfig"]
|
|
87
90
|
});
|
|
91
|
+
const generateImports$1 = (body, importStatements) => {
|
|
92
|
+
const usedImportVarMap = makeNewImportVarMap$1();
|
|
93
|
+
const importableVarMap = getImportableVarMap$1();
|
|
94
|
+
for (const [moduleSource, importableVars] of Object.entries(importableVarMap)) {
|
|
95
|
+
for (const importableVar of importableVars) {
|
|
96
|
+
const importVarRegex = new RegExp(`(?<![\\d\\w_])${importableVar}(?![\\d\\w_])`);
|
|
97
|
+
if (body.match(importVarRegex)) {
|
|
98
|
+
usedImportVarMap[moduleSource] = [...usedImportVarMap[moduleSource] || [], importableVar];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const generatedImports = Object.keys(usedImportVarMap).sort().map((moduleSource) => `import { ${usedImportVarMap[moduleSource].sort().join(", ")} } from '${moduleSource}'`).join("\n");
|
|
103
|
+
return `${generatedImports}
|
|
104
|
+
${importStatements.sort().join("\n")}`;
|
|
105
|
+
};
|
|
106
|
+
const templateClass = (className, body, importStatements) => {
|
|
107
|
+
return `/**
|
|
108
|
+
* AUTO GENERATED
|
|
109
|
+
*/
|
|
110
|
+
${generateImports$1(body, importStatements)}
|
|
111
|
+
|
|
112
|
+
export class ${className} {
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
|
|
115
|
+
${body}
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const getImportableVarMap = () => ({
|
|
121
|
+
"@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate", "ApiArgs", "Network", "AccelbyteSDK"]
|
|
122
|
+
});
|
|
123
|
+
const makeNewImportVarMap = () => ({
|
|
124
|
+
"@accelbyte/sdk": ["AccelbyteSDK", "ApiArgs", "ApiUtils"]
|
|
125
|
+
});
|
|
88
126
|
const generateImports = (body, importStatements) => {
|
|
89
127
|
const usedImportVarMap = makeNewImportVarMap();
|
|
90
128
|
const importableVarMap = getImportableVarMap();
|
|
@@ -100,27 +138,55 @@ const generateImports = (body, importStatements) => {
|
|
|
100
138
|
return `${generatedImports}
|
|
101
139
|
${importStatements.sort().join("\n")}`;
|
|
102
140
|
};
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
141
|
+
const templateApiClass = (className, body, importStatements, returnMethods) => {
|
|
142
|
+
return `/**
|
|
143
|
+
* AUTO GENERATED
|
|
144
|
+
*/
|
|
145
|
+
/* eslint-disable camelcase */
|
|
146
|
+
${generateImports(body, importStatements)}
|
|
107
147
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
148
|
+
export function ${className}(sdk: AccelbyteSDK, args?: ApiArgs) {
|
|
149
|
+
const sdkAssembly = sdk.assembly()
|
|
150
|
+
|
|
151
|
+
const namespace = args?.namespace ? args?.namespace : sdkAssembly.namespace
|
|
152
|
+
const cache = args?.cache ? args?.cache : sdkAssembly.cache
|
|
153
|
+
const requestConfig = ApiUtils.mergedConfigs(sdkAssembly.config, args)
|
|
111
154
|
${body}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
${returnMethods}
|
|
112
158
|
}
|
|
159
|
+
}
|
|
113
160
|
`;
|
|
161
|
+
};
|
|
114
162
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
163
|
+
const UNDEFINED_SWAGGER_SEMVER = "0.0.0";
|
|
164
|
+
const REMOVED_KEYWORDS = [
|
|
165
|
+
"/admin/",
|
|
166
|
+
"/public/",
|
|
167
|
+
"/v1/",
|
|
168
|
+
"/v2/",
|
|
169
|
+
"/v3/",
|
|
170
|
+
"/v4/",
|
|
171
|
+
"/v5/",
|
|
172
|
+
"/namespace/",
|
|
173
|
+
"/namespaces/",
|
|
174
|
+
"/{namespace}/"
|
|
175
|
+
];
|
|
123
176
|
class ParserUtils {
|
|
177
|
+
static replaceAll = (string, search, replace) => {
|
|
178
|
+
return string.split(search).join(replace);
|
|
179
|
+
};
|
|
180
|
+
static generateClassName = (tag) => {
|
|
181
|
+
const className = _.upperFirst(_.camelCase(tag));
|
|
182
|
+
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
183
|
+
return { className, classGenName };
|
|
184
|
+
};
|
|
185
|
+
static generateApiName = (tag) => {
|
|
186
|
+
const apiName = _.upperFirst(_.camelCase(tag));
|
|
187
|
+
const apiGenName = CliParser.isAdmin() ? apiName + "AdminApi" : apiName + "Api";
|
|
188
|
+
return { apiName, apiGenName };
|
|
189
|
+
};
|
|
124
190
|
static parseQueryParamAttributeDefault = (definition) => {
|
|
125
191
|
const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
|
|
126
192
|
const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
|
|
@@ -138,17 +204,26 @@ class ParserUtils {
|
|
|
138
204
|
return pathParam.type;
|
|
139
205
|
};
|
|
140
206
|
static parseQueryParamsType = (queryParams) => {
|
|
141
|
-
return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(",");
|
|
207
|
+
return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(", ");
|
|
142
208
|
};
|
|
143
209
|
static isAnyQueryParamRequired = (queryParams) => {
|
|
144
210
|
return queryParams.some((queryParam) => queryParam.required);
|
|
145
211
|
};
|
|
212
|
+
static convertDashesToTitleCase = (str) => {
|
|
213
|
+
const result = str.split("-").map((word, index) => {
|
|
214
|
+
if (index === 0) {
|
|
215
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
216
|
+
}
|
|
217
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
218
|
+
}).join("");
|
|
219
|
+
return result;
|
|
220
|
+
};
|
|
146
221
|
static parseQueryParamsDefault = (queryParams) => {
|
|
147
222
|
const result = queryParams.filter((queryParam) => !!queryParam.default && !queryParam.required).map(ParserUtils.parseQueryParamAttributeDefault).join(",");
|
|
148
223
|
return result ? `${result},` : "";
|
|
149
224
|
};
|
|
150
225
|
static parseBodyParamsImports = (bodyParams) => {
|
|
151
|
-
return bodyParams.map(ParserUtils.parseRefImport).filter(Boolean);
|
|
226
|
+
return bodyParams.map((bodyParams2) => ParserUtils.parseRefImport(bodyParams2)).filter(Boolean);
|
|
152
227
|
};
|
|
153
228
|
static parseImportDir = ($ref) => {
|
|
154
229
|
let ref = $ref.replace(".", "/");
|
|
@@ -167,7 +242,7 @@ class ParserUtils {
|
|
|
167
242
|
return null;
|
|
168
243
|
}
|
|
169
244
|
const type = ParserUtils.parseRefType($ref);
|
|
170
|
-
return `import { ${type} } from '
|
|
245
|
+
return `import { ${type} } from '../definitions/${type}'`;
|
|
171
246
|
};
|
|
172
247
|
static parseRefType = ($ref) => {
|
|
173
248
|
let ref = $ref.replace(".", "/");
|
|
@@ -261,34 +336,59 @@ class ParserUtils {
|
|
|
261
336
|
}
|
|
262
337
|
return parameters.filter((parameter) => parameter.in === "path");
|
|
263
338
|
}
|
|
264
|
-
static
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
339
|
+
static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
|
|
340
|
+
let path_ = path2;
|
|
341
|
+
path_ = path_.replace(`/${servicePrefix}/`, "/");
|
|
342
|
+
REMOVED_KEYWORDS.forEach((prefix) => {
|
|
343
|
+
path_ = path_.replace(prefix, "/");
|
|
344
|
+
});
|
|
345
|
+
path_ = path_.substring(1);
|
|
346
|
+
const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
|
|
347
|
+
if (!isPlural) {
|
|
348
|
+
path_ = ParserUtils.replaceAll(path_, "ies/", "y/");
|
|
349
|
+
path_ = ParserUtils.replaceAll(path_, "s/", "/");
|
|
350
|
+
if (path_.indexOf("status") < 0) {
|
|
351
|
+
path_ = path_.replace(/ies$/, "y");
|
|
352
|
+
path_ = path_.replace(/s$/, "");
|
|
278
353
|
}
|
|
279
354
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
355
|
+
const arrLastWords = path_.split("}/");
|
|
356
|
+
let lastWords = arrLastWords[arrLastWords.length - 1];
|
|
357
|
+
const extractLastWord = (lastWords_) => {
|
|
358
|
+
let res = lastWords_;
|
|
359
|
+
res = res.split("/{")[0];
|
|
360
|
+
return res;
|
|
361
|
+
};
|
|
362
|
+
lastWords = extractLastWord(lastWords);
|
|
363
|
+
if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
|
|
364
|
+
lastWords = arrLastWords[arrLastWords.length - 2];
|
|
365
|
+
lastWords = extractLastWord(lastWords);
|
|
366
|
+
}
|
|
367
|
+
const listBeforeLastWords = [];
|
|
368
|
+
let foundParam = false;
|
|
369
|
+
const listByParams = [];
|
|
370
|
+
const pathElements = path_.split("/");
|
|
371
|
+
pathElements.slice().reverse().forEach((item) => {
|
|
372
|
+
if (item.indexOf("}") >= 0) {
|
|
373
|
+
foundParam = true;
|
|
374
|
+
let param = item.replace("{", "");
|
|
375
|
+
param = param.replace("}", "");
|
|
376
|
+
param = "Byword" + _.upperFirst(param);
|
|
377
|
+
listByParams.push(param);
|
|
378
|
+
} else if (!foundParam) {
|
|
379
|
+
if (lastWords.indexOf(item) === -1) {
|
|
380
|
+
listBeforeLastWords.push(item);
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
foundParam = false;
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
|
|
387
|
+
let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
|
|
388
|
+
generatedMethod = ParserUtils.replaceAll(generatedMethod, "Byword", "_By");
|
|
389
|
+
generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
|
|
390
|
+
return generatedMethod;
|
|
391
|
+
};
|
|
292
392
|
static filterBodyParams(parameters) {
|
|
293
393
|
if (Array.isArray(parameters) && parameters.length > 0) {
|
|
294
394
|
return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
|
|
@@ -310,9 +410,22 @@ class ParserUtils {
|
|
|
310
410
|
const fileContent = templateClass(apiName, apiBuffer, imports);
|
|
311
411
|
fs.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
312
412
|
}
|
|
313
|
-
static
|
|
314
|
-
const
|
|
315
|
-
|
|
413
|
+
static writeApiFile(distDir, apiName, apiBuffer, imports, returnMethods) {
|
|
414
|
+
const newImports = [];
|
|
415
|
+
imports.forEach((el, index) => {
|
|
416
|
+
newImports.push(el.replace("../definitions", "./definitions"));
|
|
417
|
+
});
|
|
418
|
+
const fileContent = templateApiClass(apiName, apiBuffer, newImports, returnMethods);
|
|
419
|
+
fs.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
420
|
+
}
|
|
421
|
+
static writeApiMainFile(distDir, serviceName, fileContent) {
|
|
422
|
+
fs.writeFileSync(`${distDir}/${serviceName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
|
|
423
|
+
}
|
|
424
|
+
static writeSnippetFile(distDir, name, docBuffer) {
|
|
425
|
+
let snippetFileName = ParserUtils.replaceAll(name, " ", "-").toLowerCase();
|
|
426
|
+
snippetFileName = snippetFileName.replace("justice-", "");
|
|
427
|
+
snippetFileName = "snippet-" + snippetFileName + ".json";
|
|
428
|
+
fs.writeFileSync(`${distDir}/${snippetFileName}`, docBuffer);
|
|
316
429
|
}
|
|
317
430
|
static writeDefinitionFile(distDir, name, buffer) {
|
|
318
431
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
@@ -322,6 +435,37 @@ class ParserUtils {
|
|
|
322
435
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
323
436
|
fs.writeFileSync(path.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
324
437
|
}
|
|
438
|
+
static syncChangelog(packageVersion) {
|
|
439
|
+
const currDir = process.cwd();
|
|
440
|
+
const pathToChangelog = path.join(currDir, "./CHANGELOG.md");
|
|
441
|
+
let fileContent = fs.readFileSync(pathToChangelog, "utf-8");
|
|
442
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
443
|
+
fileContent = "### " + packageVersion + " - " + date + `
|
|
444
|
+
|
|
445
|
+
- code-generated update
|
|
446
|
+
|
|
447
|
+
` + fileContent.trim();
|
|
448
|
+
fs.writeFileSync(pathToChangelog, fileContent, "utf-8");
|
|
449
|
+
}
|
|
450
|
+
static syncPackageVersion(apiInfo, isAdminWebSdk) {
|
|
451
|
+
if (isAdminWebSdk) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const currDir = process.cwd();
|
|
455
|
+
const { packageJSON, pathToPackageJSON } = ParserUtils.getPackageJSONInfo(currDir);
|
|
456
|
+
let swaggerVersion = apiInfo.version ? apiInfo.version : UNDEFINED_SWAGGER_SEMVER;
|
|
457
|
+
swaggerVersion = Number(swaggerVersion.replace(".", "").replace(".", ""));
|
|
458
|
+
swaggerVersion = isNaN(swaggerVersion) ? 0 : swaggerVersion;
|
|
459
|
+
const semver = packageJSON.version.split(".").map((numStr) => Number(numStr));
|
|
460
|
+
const newSemver = [semver[0], swaggerVersion, ++semver[2]];
|
|
461
|
+
packageJSON.version = newSemver[0] + "." + newSemver[1] + "." + newSemver[2];
|
|
462
|
+
fs.writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
|
|
463
|
+
ParserUtils.syncChangelog(packageJSON.version);
|
|
464
|
+
}
|
|
465
|
+
static getPackageJSONInfo(dir) {
|
|
466
|
+
const pathToPackageJSON = path.join(dir, "./package.json");
|
|
467
|
+
return { packageJSON: JSON.parse(fs.readFileSync(pathToPackageJSON, "utf-8")), pathToPackageJSON };
|
|
468
|
+
}
|
|
325
469
|
static toCamelCase(str) {
|
|
326
470
|
return str.split("/").map(function(word, index) {
|
|
327
471
|
if (index === 0) {
|
|
@@ -355,6 +499,29 @@ class ParserUtils {
|
|
|
355
499
|
}
|
|
356
500
|
const swaggerContent = JSON.parse(fs.readFileSync(swaggerFilePath, "utf8"));
|
|
357
501
|
const swaggerPatchFileContent = JSON.parse(fs.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
|
|
502
|
+
for (const patchEntry of swaggerPatchFileContent) {
|
|
503
|
+
const segments = patchEntry.path.split("/").filter(Boolean);
|
|
504
|
+
let currentNode = swaggerContent;
|
|
505
|
+
let aggregatedPath = "";
|
|
506
|
+
for (let i = 0; i < segments.length; i++) {
|
|
507
|
+
const segment = segments[i];
|
|
508
|
+
aggregatedPath += `/${segment}`;
|
|
509
|
+
const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
|
|
510
|
+
if (!currentNode[effectiveSegment]) {
|
|
511
|
+
if (i + 1 === segments.length && patchEntry.op === "add") ; else {
|
|
512
|
+
throw new Error([
|
|
513
|
+
`JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
|
|
514
|
+
`,
|
|
515
|
+
"1. The related service has patched the service, so patch is no longer needed.",
|
|
516
|
+
"2. There is a breaking change on the service that causes the path to change.\n",
|
|
517
|
+
`In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
|
|
518
|
+
`
|
|
519
|
+
].join("\n"));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
currentNode = currentNode[effectiveSegment];
|
|
523
|
+
}
|
|
524
|
+
}
|
|
358
525
|
const { newDocument } = fastJsonPatch.applyPatch(swaggerContent, swaggerPatchFileContent);
|
|
359
526
|
fs.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
|
|
360
527
|
}
|
|
@@ -372,6 +539,67 @@ class ParserUtils {
|
|
|
372
539
|
${content}`;
|
|
373
540
|
};
|
|
374
541
|
}
|
|
542
|
+
const mappedMethod = (httpMethod, isForm) => {
|
|
543
|
+
if (httpMethod === "get") {
|
|
544
|
+
return "get";
|
|
545
|
+
} else if (httpMethod === "post" && isForm) {
|
|
546
|
+
return "post";
|
|
547
|
+
} else if (httpMethod === "post") {
|
|
548
|
+
return "create";
|
|
549
|
+
} else if (httpMethod === "put") {
|
|
550
|
+
return "update";
|
|
551
|
+
} else if (httpMethod === "patch") {
|
|
552
|
+
return "patch";
|
|
553
|
+
} else if (httpMethod === "delete") {
|
|
554
|
+
return "delete";
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const resolveConflicts = (path2, generatedMethod, existingMethods) => {
|
|
558
|
+
try {
|
|
559
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
if (path2.indexOf("/namespaces/") >= 0) {
|
|
562
|
+
generatedMethod += "_ByNS";
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
try {
|
|
566
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
567
|
+
} catch (e) {
|
|
568
|
+
if (path2.indexOf("/v4/") >= 0) {
|
|
569
|
+
generatedMethod += "_v4";
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
try {
|
|
573
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
574
|
+
} catch (e) {
|
|
575
|
+
if (path2.indexOf("/v3/") >= 0) {
|
|
576
|
+
generatedMethod += "_v3";
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
581
|
+
} catch (e) {
|
|
582
|
+
if (path2.indexOf("/v2/") >= 0) {
|
|
583
|
+
generatedMethod += "_v2";
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
588
|
+
} catch (e) {
|
|
589
|
+
if (path2.indexOf("/admin/") >= 0) {
|
|
590
|
+
generatedMethod += "_admin";
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
594
|
+
return generatedMethod;
|
|
595
|
+
};
|
|
596
|
+
const testConflict = (path2, generatedMethod, existingMethods) => {
|
|
597
|
+
if (existingMethods[generatedMethod]) {
|
|
598
|
+
const conflictingMethod = { path: path2, generatedMethod };
|
|
599
|
+
throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
|
|
600
|
+
existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
375
603
|
|
|
376
604
|
const Schema = zod.z.object({
|
|
377
605
|
$ref: zod.z.string().nullish(),
|
|
@@ -463,50 +691,6 @@ zod.z.object({
|
|
|
463
691
|
}).nullish()
|
|
464
692
|
});
|
|
465
693
|
|
|
466
|
-
const templateJsdocMethod = ({
|
|
467
|
-
classMethod,
|
|
468
|
-
httpMethod,
|
|
469
|
-
path,
|
|
470
|
-
pathParams,
|
|
471
|
-
bodyParams,
|
|
472
|
-
queryParams
|
|
473
|
-
}) => {
|
|
474
|
-
let jsdoc = "";
|
|
475
|
-
let methodSignature = "";
|
|
476
|
-
let newPath = path;
|
|
477
|
-
for (const p of pathParams) {
|
|
478
|
-
methodSignature += p.name + ", ";
|
|
479
|
-
newPath = newPath.replace("{" + p.name + "}", `' + ${p.name} + '`);
|
|
480
|
-
jsdoc += `
|
|
481
|
-
* @pathParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
|
|
482
|
-
}
|
|
483
|
-
let payloads = "";
|
|
484
|
-
for (const p of bodyParams) {
|
|
485
|
-
methodSignature += p.name + ", ";
|
|
486
|
-
payloads += p.name;
|
|
487
|
-
jsdoc += `
|
|
488
|
-
* @payload '${p.name}${p.required ? "" : "?"}' - ${p.description} ${p?.schema?.properties ? "=> " + JSON.stringify(p.schema.properties) : ""}`;
|
|
489
|
-
}
|
|
490
|
-
for (const p of queryParams) {
|
|
491
|
-
const optionalMark = p.required ? "" : "?";
|
|
492
|
-
methodSignature += p.name + optionalMark + ", ";
|
|
493
|
-
jsdoc += `
|
|
494
|
-
* @queryParam '${p.name}${p.required ? "" : "?"}' - ${p.description}`;
|
|
495
|
-
}
|
|
496
|
-
let queryString = "";
|
|
497
|
-
for (const p of queryParams) {
|
|
498
|
-
queryString += `${p.name}=__&`;
|
|
499
|
-
}
|
|
500
|
-
queryString = queryString.length > 0 ? "?" + queryString : "";
|
|
501
|
-
return `
|
|
502
|
-
/**
|
|
503
|
-
* ${httpMethod.toUpperCase()} '${newPath}${queryString}' ${payloads ? "payload: '" + payloads + "'" : ""}
|
|
504
|
-
* ${jsdoc}
|
|
505
|
-
*/
|
|
506
|
-
${classMethod}(${methodSignature}): Promise
|
|
507
|
-
`;
|
|
508
|
-
};
|
|
509
|
-
|
|
510
694
|
const templateMethod = ({
|
|
511
695
|
classMethod,
|
|
512
696
|
description,
|
|
@@ -518,13 +702,17 @@ const templateMethod = ({
|
|
|
518
702
|
isFormUrlEncoded,
|
|
519
703
|
responseClass
|
|
520
704
|
}) => {
|
|
521
|
-
let
|
|
705
|
+
let methodParams = "";
|
|
706
|
+
let methodParamsNoTypes = "";
|
|
522
707
|
let newPath = `'${path}'`;
|
|
523
|
-
let
|
|
708
|
+
let importStatements = [];
|
|
709
|
+
let snippetString = "";
|
|
710
|
+
let snippetMethodSignature = "";
|
|
524
711
|
for (const pathParam of pathParams) {
|
|
525
712
|
const type = ParserUtils.parseType(pathParam);
|
|
526
713
|
if (pathParam.name !== "namespace") {
|
|
527
|
-
|
|
714
|
+
methodParams += pathParam.name + `:${type}, `;
|
|
715
|
+
methodParamsNoTypes += pathParam.name + ", ";
|
|
528
716
|
}
|
|
529
717
|
const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
|
|
530
718
|
if (path.match(`{${pathParam.name}}`)) {
|
|
@@ -538,8 +726,16 @@ const templateMethod = ({
|
|
|
538
726
|
let dataType = null;
|
|
539
727
|
if (httpMethod !== "get") {
|
|
540
728
|
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
541
|
-
|
|
542
|
-
|
|
729
|
+
importStatements = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
730
|
+
methodParams += dataType ? `data: ${dataType},` : "";
|
|
731
|
+
methodParamsNoTypes += dataType ? `data,` : "";
|
|
732
|
+
const snippetParams = bodyParams?.map((ob) => {
|
|
733
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
734
|
+
});
|
|
735
|
+
snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
|
|
736
|
+
bodyParams?.map((ob) => {
|
|
737
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
738
|
+
});
|
|
543
739
|
}
|
|
544
740
|
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
545
741
|
const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
@@ -549,7 +745,7 @@ const templateMethod = ({
|
|
|
549
745
|
let dataPayload = "{params}";
|
|
550
746
|
const descriptionText = description ? `
|
|
551
747
|
/**
|
|
552
|
-
* ${
|
|
748
|
+
* ${description.replace(/\n/g, "\n * ")}
|
|
553
749
|
*/` : "";
|
|
554
750
|
let formPayloadString = "";
|
|
555
751
|
if (isFormUrlEncoded) {
|
|
@@ -561,47 +757,57 @@ const templateMethod = ({
|
|
|
561
757
|
} else if (isDelete) {
|
|
562
758
|
dataPayload = dataType ? `{data, params}` : "{params}";
|
|
563
759
|
}
|
|
564
|
-
const isFileUpload =
|
|
760
|
+
const isFileUpload = methodParams.indexOf("data: {file") > -1;
|
|
565
761
|
const resolvedResponseClass = responseClass || "unknown";
|
|
566
762
|
const resolvedResponseClassValidated = responseClass || "z.unknown()";
|
|
567
|
-
|
|
763
|
+
methodParams = (queryParamsType ? `${methodParams} ${queryParamsType}` : methodParams).replace(/,\s*$/, "");
|
|
764
|
+
methodParamsNoTypes = queryParamsType ? `${methodParamsNoTypes} queryParams` : methodParamsNoTypes;
|
|
568
765
|
let methodImpl = "";
|
|
569
766
|
const isCacheFetch = ["get"].includes(httpMethod) && resolvedResponseClass !== "unknown";
|
|
570
|
-
const cachedFetchMethod = classMethod
|
|
767
|
+
const cachedFetchMethod = classMethod;
|
|
571
768
|
const deprecateTag = isCacheFetch ? `/**
|
|
572
|
-
* @deprecated Use "${
|
|
769
|
+
* @deprecated Use "${classMethod}()" instead.
|
|
573
770
|
*/` : "";
|
|
574
771
|
const isGuardInvoked = ["get", "post", "put", "patch", "delete"].includes(httpMethod);
|
|
575
772
|
const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
|
|
576
|
-
const
|
|
577
|
-
const
|
|
773
|
+
const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
|
|
774
|
+
const generateMethodName = () => `${methodName}(${methodParams}): Promise<${responseSyncType}<${responseType}>>`;
|
|
775
|
+
const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
|
|
776
|
+
snippetString += `${generateSnippetMethodName()}`;
|
|
578
777
|
const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
|
|
579
778
|
methodImpl = `${descriptionText}
|
|
580
|
-
${
|
|
779
|
+
${generateMethodName()} {
|
|
581
780
|
${queryParamsDefault}
|
|
582
781
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
583
782
|
const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
584
783
|
|
|
585
|
-
${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
784
|
+
${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
586
785
|
|
|
587
786
|
if (!this.cache) {
|
|
588
787
|
return SdkCache.withoutCache(res)
|
|
589
788
|
}
|
|
590
789
|
const cacheKey = url + CodeGenUtil.hashCode(JSON.stringify({ params }))
|
|
591
|
-
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? `return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
790
|
+
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
592
791
|
}
|
|
593
792
|
`;
|
|
594
793
|
if (!isGuardInvoked) {
|
|
595
794
|
methodImpl = `${descriptionText}
|
|
596
795
|
${deprecateTag}
|
|
597
|
-
TODO_${classMethod}
|
|
796
|
+
TODO_${classMethod}(${methodParams}): Promise<AxiosResponse<${resolvedResponseClass}>> {
|
|
598
797
|
${queryParamsDefault}
|
|
599
798
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
600
799
|
return this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
601
800
|
}
|
|
602
801
|
`;
|
|
603
802
|
}
|
|
604
|
-
|
|
803
|
+
const res = {
|
|
804
|
+
methodImpl,
|
|
805
|
+
methodParams,
|
|
806
|
+
methodParamsNoTypes,
|
|
807
|
+
importStatements,
|
|
808
|
+
snippetString
|
|
809
|
+
};
|
|
810
|
+
return res;
|
|
605
811
|
};
|
|
606
812
|
|
|
607
813
|
class TemplateZod {
|
|
@@ -617,32 +823,32 @@ class TemplateZod {
|
|
|
617
823
|
}
|
|
618
824
|
let imports = "";
|
|
619
825
|
for (const cl of Array.from(this.importClasses).sort()) {
|
|
620
|
-
imports += `
|
|
826
|
+
imports += `import { ${cl} } from './${cl}'
|
|
621
827
|
`;
|
|
622
828
|
}
|
|
623
829
|
let exportedVariableString;
|
|
624
830
|
let exportedTypeString;
|
|
625
831
|
if (containsRecursiveType) {
|
|
626
832
|
exportedVariableString = `
|
|
627
|
-
|
|
833
|
+
export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
|
|
628
834
|
${content.schemaString}
|
|
629
835
|
)
|
|
630
836
|
`;
|
|
631
837
|
exportedTypeString = `
|
|
632
|
-
|
|
838
|
+
export interface ${fileName} {
|
|
633
839
|
${content.typeString}
|
|
634
|
-
|
|
635
|
-
|
|
840
|
+
}
|
|
841
|
+
`;
|
|
636
842
|
} else {
|
|
637
843
|
exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
|
|
638
|
-
exportedTypeString = `export
|
|
844
|
+
exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
|
|
639
845
|
}
|
|
640
846
|
const template = `import { z } from 'zod'
|
|
641
|
-
|
|
847
|
+
${imports}
|
|
642
848
|
|
|
643
|
-
|
|
849
|
+
${exportedVariableString}
|
|
644
850
|
|
|
645
|
-
|
|
851
|
+
${exportedTypeString}
|
|
646
852
|
`;
|
|
647
853
|
return { buffer: template, duplicateFound: this.duplicateFound };
|
|
648
854
|
};
|
|
@@ -727,7 +933,25 @@ class TemplateZod {
|
|
|
727
933
|
typeString: refType
|
|
728
934
|
};
|
|
729
935
|
} else if (items) {
|
|
730
|
-
|
|
936
|
+
if (items.type === "array") {
|
|
937
|
+
const ref3 = items.items?.$ref;
|
|
938
|
+
if (ref3) {
|
|
939
|
+
const refType = ParserUtils.parseRefType(ref3);
|
|
940
|
+
this.importClasses.add(refType);
|
|
941
|
+
model2 = {
|
|
942
|
+
schemaString: refType,
|
|
943
|
+
typeString: refType
|
|
944
|
+
};
|
|
945
|
+
} else if (items.items) {
|
|
946
|
+
model2 = this.parseEnumItems(items.items);
|
|
947
|
+
}
|
|
948
|
+
return {
|
|
949
|
+
schemaString: `${schemaAttribute} z.array(z.array(${model2.schemaString}))${schemaRequired}`,
|
|
950
|
+
typeString: `${typeAttribute} ${model2.typeString}[]${typeNullishability}`
|
|
951
|
+
};
|
|
952
|
+
} else {
|
|
953
|
+
model2 = this.parseEnumItems(items);
|
|
954
|
+
}
|
|
731
955
|
} else {
|
|
732
956
|
return {
|
|
733
957
|
schemaString: `${schemaAttribute} z.array(z.any())${schemaRequired}`,
|
|
@@ -799,11 +1023,11 @@ class TemplateZodArray {
|
|
|
799
1023
|
render = (name) => {
|
|
800
1024
|
const cls = name.replace("Array", "");
|
|
801
1025
|
const template = `import { z } from 'zod'
|
|
802
|
-
|
|
1026
|
+
import { ${cls} } from './${cls}'
|
|
803
1027
|
|
|
804
|
-
|
|
1028
|
+
export const ${name} = z.array(${cls})
|
|
805
1029
|
|
|
806
|
-
|
|
1030
|
+
export interface ${name} extends z.TypeOf<typeof ${name}> {}
|
|
807
1031
|
`;
|
|
808
1032
|
return template;
|
|
809
1033
|
};
|
|
@@ -838,24 +1062,147 @@ const extractEnumObject = (type, isRequired, enumArr) => {
|
|
|
838
1062
|
};
|
|
839
1063
|
};
|
|
840
1064
|
|
|
1065
|
+
const templateApiMethod = ({
|
|
1066
|
+
classMethod,
|
|
1067
|
+
description,
|
|
1068
|
+
httpMethod,
|
|
1069
|
+
path,
|
|
1070
|
+
pathParams,
|
|
1071
|
+
bodyParams,
|
|
1072
|
+
queryParams,
|
|
1073
|
+
responseClass,
|
|
1074
|
+
classGenName,
|
|
1075
|
+
methodParams,
|
|
1076
|
+
methodParamsNoTypes
|
|
1077
|
+
}) => {
|
|
1078
|
+
let methodSignature = "";
|
|
1079
|
+
let newPath = `'${path}'`;
|
|
1080
|
+
let dependencies = [];
|
|
1081
|
+
let snippetSdk = "";
|
|
1082
|
+
let snippetShell = "";
|
|
1083
|
+
for (const pathParam of pathParams) {
|
|
1084
|
+
const type = ParserUtils.parseType(pathParam);
|
|
1085
|
+
if (pathParam.name !== "namespace") {
|
|
1086
|
+
methodSignature += pathParam.name + `:${type}, `;
|
|
1087
|
+
}
|
|
1088
|
+
const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
|
|
1089
|
+
if (path.match(`{${pathParam.name}}`)) {
|
|
1090
|
+
if (type === "string") {
|
|
1091
|
+
newPath = `${newPath}.replace('{${pathParam.name}}', ${pName})`;
|
|
1092
|
+
} else {
|
|
1093
|
+
newPath = `${newPath}.replace('{${pathParam.name}}', String(${pName}))`;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
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'`;
|
|
1098
|
+
let dataType = null;
|
|
1099
|
+
if (httpMethod !== "get") {
|
|
1100
|
+
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
1101
|
+
dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
1102
|
+
methodSignature += dataType ? `data: ${dataType},` : "";
|
|
1103
|
+
bodyParams?.map((ob) => {
|
|
1104
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
1105
|
+
});
|
|
1106
|
+
const curlParams = bodyParams?.map((ob) => {
|
|
1107
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
1108
|
+
});
|
|
1109
|
+
snippetShell += ` --data-raw { ${curlParams}}`;
|
|
1110
|
+
}
|
|
1111
|
+
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
1112
|
+
queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
1113
|
+
queryParams.length ? `const params = {${ParserUtils.parseQueryParamsDefault(queryParams)} ...queryParams} as SDKRequestConfig` : "const params = {} as SDKRequestConfig";
|
|
1114
|
+
description ? `
|
|
1115
|
+
/**
|
|
1116
|
+
* ${description.replace(/\n/g, "\n * ")}
|
|
1117
|
+
*/` : "";
|
|
1118
|
+
const resolvedResponseClass = responseClass || "unknown";
|
|
1119
|
+
const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
|
|
1120
|
+
const methodImpl = `
|
|
1121
|
+
async function ${classMethod}(${methodParams}): Promise<${responseType}> {
|
|
1122
|
+
const $ = new ${classGenName}(Network.create(requestConfig), namespace, cache)
|
|
1123
|
+
const resp = await $.${classMethod}(${methodParamsNoTypes})
|
|
1124
|
+
if (resp.error) throw resp.error
|
|
1125
|
+
return resp.response.data
|
|
1126
|
+
}
|
|
1127
|
+
`;
|
|
1128
|
+
snippetSdk += `${classMethod}(${methodParams}) // return Promise<${responseType}>`;
|
|
1129
|
+
return [methodImpl, dependencies, snippetSdk, snippetShell];
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
const templateApiIndex = (serviceName, serviceNameTitle, apiList) => {
|
|
1133
|
+
let imports = "";
|
|
1134
|
+
let returnStatement = "";
|
|
1135
|
+
for (const cl of apiList) {
|
|
1136
|
+
imports += `
|
|
1137
|
+
import { ${cl} } from './${serviceName}/${cl}'`;
|
|
1138
|
+
returnStatement += `
|
|
1139
|
+
${cl}, `;
|
|
1140
|
+
}
|
|
1141
|
+
return `/**
|
|
1142
|
+
* AUTO GENERATED
|
|
1143
|
+
*/
|
|
1144
|
+
${imports}
|
|
1145
|
+
|
|
1146
|
+
const apis = {
|
|
1147
|
+
${returnStatement}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
export const ${ParserUtils.convertDashesToTitleCase(serviceNameTitle)} = apis
|
|
1151
|
+
`;
|
|
1152
|
+
};
|
|
1153
|
+
|
|
1154
|
+
const templateSdkSnippet = (serviceNameTitle, apiName, methodSnippet) => {
|
|
1155
|
+
const sdkSnippet = `
|
|
1156
|
+
import { Accelbyte } from '@accelbyte/sdk'
|
|
1157
|
+
import { ${serviceNameTitle} } from '@accelbyte/sdk-${serviceNameTitle.toLowerCase()}'
|
|
1158
|
+
|
|
1159
|
+
const SDK_CONFIG = {
|
|
1160
|
+
baseURL: 'https://demo.accelbyte.io',
|
|
1161
|
+
clientId: '77f88506b6174c3ea4d925f5b4096ce8',
|
|
1162
|
+
namespace: 'accelbyte',
|
|
1163
|
+
redirectURI: 'http://localhost:3030'
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const sdk = Accelbyte.SDK({
|
|
1167
|
+
options: SDK_CONFIG
|
|
1168
|
+
})
|
|
1169
|
+
|
|
1170
|
+
${serviceNameTitle}.${apiName}(sdk).${methodSnippet}
|
|
1171
|
+
`;
|
|
1172
|
+
return sdkSnippet;
|
|
1173
|
+
};
|
|
1174
|
+
|
|
841
1175
|
class CodeGenerator {
|
|
842
1176
|
static getPatchedDir = () => path.join(CliParser.getSwaggersOutputPath(), "patched");
|
|
843
1177
|
static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
|
|
844
1178
|
static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
|
|
845
|
-
static
|
|
1179
|
+
static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
|
|
1180
|
+
static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
|
|
1181
|
+
static iterateApi = async (api, serviceName) => {
|
|
846
1182
|
const apiBufferByTag = {};
|
|
847
|
-
const
|
|
1183
|
+
const apiArgumentsByTag = {};
|
|
1184
|
+
const classBufferByTag = {};
|
|
848
1185
|
const dependenciesByTag = {};
|
|
849
1186
|
const classImports = {};
|
|
850
1187
|
let arrayDefinitions = [];
|
|
851
|
-
|
|
1188
|
+
const snippetMap = {};
|
|
1189
|
+
const mapClassMethods = {};
|
|
1190
|
+
const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
|
|
1191
|
+
if (a[0].length === b[0].length) {
|
|
1192
|
+
return a[0].localeCompare(b[0]);
|
|
1193
|
+
} else {
|
|
1194
|
+
return a[0].length - b[0].length;
|
|
1195
|
+
}
|
|
1196
|
+
}));
|
|
1197
|
+
const sortedKeys = Array.from(sortedPathsByLength.keys());
|
|
1198
|
+
const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
|
|
1199
|
+
for (const [path2, operation] of sortedPathsByLength) {
|
|
852
1200
|
const isAdminEndpoint = path2.indexOf("/admin/") > -1;
|
|
853
1201
|
if (CliParser.isAdmin() && !isAdminEndpoint) {
|
|
854
1202
|
continue;
|
|
855
1203
|
} else if (!CliParser.isAdmin() && isAdminEndpoint) {
|
|
856
1204
|
continue;
|
|
857
1205
|
}
|
|
858
|
-
const operation = api.paths[path2];
|
|
859
1206
|
const httpMethods = Object.keys(operation);
|
|
860
1207
|
for (const httpMethod of httpMethods) {
|
|
861
1208
|
const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
|
|
@@ -865,59 +1212,98 @@ class CodeGenerator {
|
|
|
865
1212
|
if (!endpoint.tags) {
|
|
866
1213
|
continue;
|
|
867
1214
|
}
|
|
1215
|
+
if (endpoint.deprecated) {
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
868
1218
|
const [tag] = endpoint.tags;
|
|
869
|
-
|
|
870
|
-
const
|
|
1219
|
+
mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
|
|
1220
|
+
const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
|
|
1221
|
+
const classMethod = ParserUtils.generateNaturalLangMethod({
|
|
1222
|
+
servicePrefix,
|
|
1223
|
+
path: path2,
|
|
1224
|
+
httpMethod,
|
|
1225
|
+
isForm,
|
|
1226
|
+
existingMethods: mapClassMethods[tag]
|
|
1227
|
+
});
|
|
1228
|
+
mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
|
|
1229
|
+
snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
|
|
1230
|
+
let description = endpoint.description;
|
|
1231
|
+
description = description || "";
|
|
1232
|
+
description = description.replace(/\s+/g, " ");
|
|
871
1233
|
const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
|
|
872
|
-
const className =
|
|
1234
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
873
1235
|
classImports[className] = classImports[className] ? classImports[className] : {};
|
|
874
|
-
if (
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
{
|
|
895
|
-
name: "body",
|
|
896
|
-
in: "body",
|
|
897
|
-
schema: endpoint.requestBody.content["application/json"].schema
|
|
898
|
-
}
|
|
899
|
-
];
|
|
900
|
-
}
|
|
901
|
-
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
902
|
-
const [generatedMethodString, importStatements] = templateMethod({
|
|
903
|
-
classMethod,
|
|
904
|
-
description,
|
|
905
|
-
httpMethod,
|
|
906
|
-
path: pathWithBase,
|
|
907
|
-
pathParams,
|
|
908
|
-
bodyParams,
|
|
909
|
-
queryParams,
|
|
910
|
-
isFormUrlEncoded,
|
|
911
|
-
responseClass
|
|
912
|
-
});
|
|
913
|
-
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
|
|
914
|
-
jsDocApiBufferByTag[tag] = (jsDocApiBufferByTag[tag] || "") + templateJsdocMethod({ classMethod, httpMethod, path: path2, pathParams, bodyParams, queryParams });
|
|
915
|
-
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1236
|
+
if (responseClass) {
|
|
1237
|
+
const importTypeClass = ParserUtils.parseRefType(responseClass);
|
|
1238
|
+
classImports[className][importTypeClass] = `import { ${importTypeClass} } from '../definitions/${importTypeClass}'`;
|
|
1239
|
+
}
|
|
1240
|
+
if (responseClass && responseClass.endsWith("Array")) {
|
|
1241
|
+
arrayDefinitions.push(responseClass);
|
|
1242
|
+
}
|
|
1243
|
+
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
1244
|
+
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
1245
|
+
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
1246
|
+
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
1247
|
+
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
1248
|
+
if (endpoint.requestBody) {
|
|
1249
|
+
bodyParams = [
|
|
1250
|
+
{
|
|
1251
|
+
name: "body",
|
|
1252
|
+
in: "body",
|
|
1253
|
+
schema: endpoint.requestBody.content["application/json"].schema
|
|
1254
|
+
}
|
|
1255
|
+
];
|
|
916
1256
|
}
|
|
1257
|
+
const { methodImpl, methodParams, methodParamsNoTypes, importStatements, snippetString } = templateMethod({
|
|
1258
|
+
classMethod,
|
|
1259
|
+
description,
|
|
1260
|
+
httpMethod,
|
|
1261
|
+
path: pathWithBase,
|
|
1262
|
+
pathParams,
|
|
1263
|
+
bodyParams,
|
|
1264
|
+
queryParams,
|
|
1265
|
+
isFormUrlEncoded,
|
|
1266
|
+
responseClass
|
|
1267
|
+
});
|
|
1268
|
+
classBufferByTag[tag] = (classBufferByTag[tag] || "") + methodImpl;
|
|
1269
|
+
const [generatedMethodString1, importStatements1, snippetMethod, snippetShell] = templateApiMethod({
|
|
1270
|
+
classMethod,
|
|
1271
|
+
description,
|
|
1272
|
+
httpMethod,
|
|
1273
|
+
path: pathWithBase,
|
|
1274
|
+
pathParams,
|
|
1275
|
+
bodyParams,
|
|
1276
|
+
queryParams,
|
|
1277
|
+
responseClass,
|
|
1278
|
+
classGenName,
|
|
1279
|
+
methodParams,
|
|
1280
|
+
methodParamsNoTypes
|
|
1281
|
+
});
|
|
1282
|
+
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString1;
|
|
1283
|
+
apiArgumentsByTag[tag] = (apiArgumentsByTag[tag] || "") + classMethod + ",";
|
|
1284
|
+
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1285
|
+
const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
|
|
1286
|
+
const { apiGenName } = ParserUtils.generateApiName(tag);
|
|
1287
|
+
let resultSnippet = templateSdkSnippet(serviceNameTitle, apiGenName, snippetMethod);
|
|
1288
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, "<", "<");
|
|
1289
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, ">", ">");
|
|
1290
|
+
resultSnippet = ParserUtils.replaceAll(resultSnippet, "\n", "<br/>");
|
|
1291
|
+
snippetMap[path2][httpMethod] = {
|
|
1292
|
+
web: resultSnippet,
|
|
1293
|
+
shell: snippetShell
|
|
1294
|
+
};
|
|
917
1295
|
}
|
|
918
1296
|
}
|
|
919
1297
|
arrayDefinitions = [...new Set(arrayDefinitions)];
|
|
920
|
-
return {
|
|
1298
|
+
return {
|
|
1299
|
+
apiArgumentsByTag,
|
|
1300
|
+
apiBufferByTag,
|
|
1301
|
+
classBufferByTag,
|
|
1302
|
+
dependenciesByTag,
|
|
1303
|
+
classImports,
|
|
1304
|
+
arrayDefinitions,
|
|
1305
|
+
snippetMap
|
|
1306
|
+
};
|
|
921
1307
|
};
|
|
922
1308
|
static main = async (nameArray) => {
|
|
923
1309
|
const serviceName = nameArray[0];
|
|
@@ -925,7 +1311,7 @@ class CodeGenerator {
|
|
|
925
1311
|
const parser = new SwaggerParser();
|
|
926
1312
|
const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
|
|
927
1313
|
const DIST_DIR = `${generatedFolder}/${serviceName}`;
|
|
928
|
-
const
|
|
1314
|
+
const DIST_ENDPOINTS_DIR = path.join(DIST_DIR, "endpoints");
|
|
929
1315
|
const DIST_DEFINITION_DIR = path.join(DIST_DIR, "definitions");
|
|
930
1316
|
const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
|
|
931
1317
|
const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
|
|
@@ -933,20 +1319,35 @@ class CodeGenerator {
|
|
|
933
1319
|
ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
|
|
934
1320
|
const api = await parser.parse(swaggerPatchedFilePath);
|
|
935
1321
|
const indexImportsSet = /* @__PURE__ */ new Set();
|
|
936
|
-
console.log("
|
|
1322
|
+
console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
|
|
937
1323
|
ParserUtils.mkdirIfNotExist(DIST_DIR);
|
|
938
|
-
ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
|
|
939
1324
|
ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
|
|
940
|
-
|
|
1325
|
+
ParserUtils.mkdirIfNotExist(DIST_ENDPOINTS_DIR);
|
|
1326
|
+
ParserUtils.syncPackageVersion(api.info, CliParser.isAdmin());
|
|
1327
|
+
const { apiArgumentsByTag, apiBufferByTag, classBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api, serviceName);
|
|
1328
|
+
if (CliParser.getSnippetOutputPath()) {
|
|
1329
|
+
ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
|
|
1330
|
+
}
|
|
941
1331
|
const targetSrcFolder = `${CliParser.getOutputPath()}/`;
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
const
|
|
1332
|
+
const apiList = [];
|
|
1333
|
+
for (const tag in classBufferByTag) {
|
|
1334
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
1335
|
+
const classBuffer = classBufferByTag[tag];
|
|
945
1336
|
const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
1337
|
+
const apiImports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
1338
|
+
apiImports.push(`import { ${classGenName} } from './endpoints/${classGenName}'`);
|
|
1339
|
+
ParserUtils.writeClassFile(DIST_ENDPOINTS_DIR, classGenName, classBuffer, imports);
|
|
1340
|
+
const apiBuffer = apiBufferByTag[tag];
|
|
1341
|
+
const { apiGenName } = ParserUtils.generateApiName(tag);
|
|
1342
|
+
ParserUtils.writeApiFile(DIST_DIR, apiGenName, apiBuffer, apiImports, apiArgumentsByTag[tag]);
|
|
1343
|
+
apiList.push(apiGenName);
|
|
1344
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_ENDPOINTS_DIR, `${classGenName}`), targetSrcFolder));
|
|
1345
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DIR, `${apiGenName}`), targetSrcFolder));
|
|
949
1346
|
}
|
|
1347
|
+
const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
|
|
1348
|
+
const apiIndexBuff = templateApiIndex(serviceName, serviceNameTitle, apiList);
|
|
1349
|
+
ParserUtils.writeApiMainFile(generatedFolder, serviceNameTitle, apiIndexBuff);
|
|
1350
|
+
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(generatedFolder, serviceNameTitle), targetSrcFolder));
|
|
950
1351
|
const duplicates = /* @__PURE__ */ new Map();
|
|
951
1352
|
const definitions = api?.components?.schemas || api.definitions;
|
|
952
1353
|
for (const ref in definitions) {
|
|
@@ -975,7 +1376,7 @@ class CodeGenerator {
|
|
|
975
1376
|
ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
|
|
976
1377
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
|
|
977
1378
|
}
|
|
978
|
-
console.log("\
|
|
1379
|
+
console.log("\nCOMPLETED\n----------\n\n");
|
|
979
1380
|
return indexImportsSet;
|
|
980
1381
|
};
|
|
981
1382
|
}
|
|
@@ -1002,7 +1403,7 @@ class SwaggerDownloader {
|
|
|
1002
1403
|
});
|
|
1003
1404
|
});
|
|
1004
1405
|
request.on("error", (err) => {
|
|
1005
|
-
console.log(`SwaggerDownloader
|
|
1406
|
+
console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
|
|
1006
1407
|
});
|
|
1007
1408
|
};
|
|
1008
1409
|
static main = () => {
|