@accelbyte/codegen 1.0.0-alpha.9 → 1.0.0-beta.6
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 +287 -149
- package/dist/accelbyte-codegen.js.map +1 -1
- package/dist/accelbyte-codegen.mjs +287 -149
- package/dist/accelbyte-codegen.mjs.map +1 -1
- package/package.json +5 -3
|
@@ -5,8 +5,8 @@ import fs__default 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,6 +51,9 @@ 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
59
|
const getImportableVarMap = () => ({
|
|
@@ -78,15 +81,15 @@ const generateImports = (body, importStatements) => {
|
|
|
78
81
|
${importStatements.sort().join("\n")}`;
|
|
79
82
|
};
|
|
80
83
|
const templateClass = (className, body, importStatements) => `/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
* DON'T EDIT THIS FILE, it is AUTO GENERATED
|
|
85
|
+
*/
|
|
86
|
+
${generateImports(body, importStatements)}
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
export class ${className} {
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
|
|
88
91
|
${body}
|
|
89
|
-
|
|
92
|
+
}
|
|
90
93
|
`;
|
|
91
94
|
|
|
92
95
|
const templateJsdocFile = (apiName, body) => `
|
|
@@ -97,7 +100,24 @@ ${body}
|
|
|
97
100
|
\`\`\`
|
|
98
101
|
`.replace(/, \)/g, ")").trim();
|
|
99
102
|
|
|
103
|
+
const REMOVED_KEYWORDS = [
|
|
104
|
+
"/admin/",
|
|
105
|
+
"/public/",
|
|
106
|
+
"/v1/",
|
|
107
|
+
"/v2/",
|
|
108
|
+
"/v3/",
|
|
109
|
+
"/v4/",
|
|
110
|
+
"/v5/",
|
|
111
|
+
"/namespace/",
|
|
112
|
+
"/namespaces/",
|
|
113
|
+
"/{namespace}/"
|
|
114
|
+
];
|
|
100
115
|
class ParserUtils {
|
|
116
|
+
static generateClassName = (tag) => {
|
|
117
|
+
const className = _.upperFirst(_.camelCase(tag));
|
|
118
|
+
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
119
|
+
return { className, classGenName };
|
|
120
|
+
};
|
|
101
121
|
static parseQueryParamAttributeDefault = (definition) => {
|
|
102
122
|
const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
|
|
103
123
|
const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
|
|
@@ -238,34 +258,59 @@ class ParserUtils {
|
|
|
238
258
|
}
|
|
239
259
|
return parameters.filter((parameter) => parameter.in === "path");
|
|
240
260
|
}
|
|
241
|
-
static
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
|
|
262
|
+
let path_ = path2;
|
|
263
|
+
path_ = path_.replace(`/${servicePrefix}/`, "/");
|
|
264
|
+
REMOVED_KEYWORDS.forEach((prefix) => {
|
|
265
|
+
path_ = path_.replace(prefix, "/");
|
|
266
|
+
});
|
|
267
|
+
path_ = path_.substring(1);
|
|
268
|
+
const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
|
|
269
|
+
if (!isPlural) {
|
|
270
|
+
path_ = replaceAll(path_, "ies/", "y/");
|
|
271
|
+
path_ = replaceAll(path_, "s/", "/");
|
|
272
|
+
if (path_.indexOf("status") < 0) {
|
|
273
|
+
path_ = path_.replace(/ies$/, "y");
|
|
274
|
+
path_ = path_.replace(/s$/, "");
|
|
255
275
|
}
|
|
256
276
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
277
|
+
const arrLastWords = path_.split("}/");
|
|
278
|
+
let lastWords = arrLastWords[arrLastWords.length - 1];
|
|
279
|
+
const extractLastWord = (lastWords_) => {
|
|
280
|
+
let res = lastWords_;
|
|
281
|
+
res = res.split("/{")[0];
|
|
282
|
+
return res;
|
|
283
|
+
};
|
|
284
|
+
lastWords = extractLastWord(lastWords);
|
|
285
|
+
if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
|
|
286
|
+
lastWords = arrLastWords[arrLastWords.length - 2];
|
|
287
|
+
lastWords = extractLastWord(lastWords);
|
|
266
288
|
}
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
const listBeforeLastWords = [];
|
|
290
|
+
let foundParam = false;
|
|
291
|
+
const listByParams = [];
|
|
292
|
+
const pathElements = path_.split("/");
|
|
293
|
+
pathElements.slice().reverse().forEach((item) => {
|
|
294
|
+
if (item.indexOf("}") >= 0) {
|
|
295
|
+
foundParam = true;
|
|
296
|
+
let param = item.replace("{", "");
|
|
297
|
+
param = param.replace("}", "");
|
|
298
|
+
param = "Byword" + _.upperFirst(param);
|
|
299
|
+
listByParams.push(param);
|
|
300
|
+
} else if (!foundParam) {
|
|
301
|
+
if (lastWords.indexOf(item) === -1) {
|
|
302
|
+
listBeforeLastWords.push(item);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
foundParam = false;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
|
|
309
|
+
let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
|
|
310
|
+
generatedMethod = replaceAll(generatedMethod, "Byword", "_By");
|
|
311
|
+
generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
|
|
312
|
+
return generatedMethod;
|
|
313
|
+
};
|
|
269
314
|
static filterBodyParams(parameters) {
|
|
270
315
|
if (Array.isArray(parameters) && parameters.length > 0) {
|
|
271
316
|
return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
|
|
@@ -291,6 +336,9 @@ class ParserUtils {
|
|
|
291
336
|
const jsdocFile = templateJsdocFile(nameArray[0], apiBuffer);
|
|
292
337
|
fs__default.writeFileSync(`${distDir}/docs/${nameArray[0]}.md`, jsdocFile);
|
|
293
338
|
}
|
|
339
|
+
static writeSnippetFile(distDir, name, docBuffer) {
|
|
340
|
+
fs__default.writeFileSync(`${distDir}/${name}.json`, docBuffer);
|
|
341
|
+
}
|
|
294
342
|
static writeDefinitionFile(distDir, name, buffer) {
|
|
295
343
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
296
344
|
fs__default.writeFileSync(path__default.join(distDir, `${name}.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
@@ -299,6 +347,16 @@ class ParserUtils {
|
|
|
299
347
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
300
348
|
fs__default.writeFileSync(path__default.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
301
349
|
}
|
|
350
|
+
static writeVersionFile(distDir, fileName, serviceName, apiInfo, buildDate) {
|
|
351
|
+
const ver = apiInfo.version ? `'${apiInfo.version}'` : void 0;
|
|
352
|
+
fs__default.writeFileSync(`${distDir}/${fileName}`, `export default {
|
|
353
|
+
title: '${serviceName}',
|
|
354
|
+
name: '${apiInfo.title}',
|
|
355
|
+
version: ${ver},
|
|
356
|
+
buildDate: '${buildDate}'
|
|
357
|
+
}
|
|
358
|
+
`);
|
|
359
|
+
}
|
|
302
360
|
static toCamelCase(str) {
|
|
303
361
|
return str.split("/").map(function(word, index) {
|
|
304
362
|
if (index === 0) {
|
|
@@ -332,6 +390,29 @@ class ParserUtils {
|
|
|
332
390
|
}
|
|
333
391
|
const swaggerContent = JSON.parse(fs__default.readFileSync(swaggerFilePath, "utf8"));
|
|
334
392
|
const swaggerPatchFileContent = JSON.parse(fs__default.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
|
|
393
|
+
for (const patchEntry of swaggerPatchFileContent) {
|
|
394
|
+
const segments = patchEntry.path.split("/").filter(Boolean);
|
|
395
|
+
let currentNode = swaggerContent;
|
|
396
|
+
let aggregatedPath = "";
|
|
397
|
+
for (let i = 0; i < segments.length; i++) {
|
|
398
|
+
const segment = segments[i];
|
|
399
|
+
aggregatedPath += `/${segment}`;
|
|
400
|
+
const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
|
|
401
|
+
if (!currentNode[effectiveSegment]) {
|
|
402
|
+
if (i + 1 === segments.length && patchEntry.op === "add") ; else {
|
|
403
|
+
throw new Error([
|
|
404
|
+
`JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
|
|
405
|
+
`,
|
|
406
|
+
"1. The related service has patched the service, so patch is no longer needed.",
|
|
407
|
+
"2. There is a breaking change on the service that causes the path to change.\n",
|
|
408
|
+
`In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
|
|
409
|
+
`
|
|
410
|
+
].join("\n"));
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
currentNode = currentNode[effectiveSegment];
|
|
414
|
+
}
|
|
415
|
+
}
|
|
335
416
|
const { newDocument } = applyPatch(swaggerContent, swaggerPatchFileContent);
|
|
336
417
|
fs__default.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
|
|
337
418
|
}
|
|
@@ -349,6 +430,70 @@ class ParserUtils {
|
|
|
349
430
|
${content}`;
|
|
350
431
|
};
|
|
351
432
|
}
|
|
433
|
+
const replaceAll = (string, search, replace) => {
|
|
434
|
+
return string.split(search).join(replace);
|
|
435
|
+
};
|
|
436
|
+
const mappedMethod = (httpMethod, isForm) => {
|
|
437
|
+
if (httpMethod === "get") {
|
|
438
|
+
return "fetch";
|
|
439
|
+
} else if (httpMethod === "post" && isForm) {
|
|
440
|
+
return "post";
|
|
441
|
+
} else if (httpMethod === "post") {
|
|
442
|
+
return "create";
|
|
443
|
+
} else if (httpMethod === "put") {
|
|
444
|
+
return "update";
|
|
445
|
+
} else if (httpMethod === "patch") {
|
|
446
|
+
return "patch";
|
|
447
|
+
} else if (httpMethod === "delete") {
|
|
448
|
+
return "delete";
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
const resolveConflicts = (path2, generatedMethod, existingMethods) => {
|
|
452
|
+
try {
|
|
453
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
454
|
+
} catch (e) {
|
|
455
|
+
if (path2.indexOf("/namespaces/") >= 0) {
|
|
456
|
+
generatedMethod += "_ByNS";
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
461
|
+
} catch (e) {
|
|
462
|
+
if (path2.indexOf("/v4/") >= 0) {
|
|
463
|
+
generatedMethod += "_v4";
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
try {
|
|
467
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
468
|
+
} catch (e) {
|
|
469
|
+
if (path2.indexOf("/v3/") >= 0) {
|
|
470
|
+
generatedMethod += "_v3";
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
475
|
+
} catch (e) {
|
|
476
|
+
if (path2.indexOf("/v2/") >= 0) {
|
|
477
|
+
generatedMethod += "_v2";
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
482
|
+
} catch (e) {
|
|
483
|
+
if (path2.indexOf("/admin/") >= 0) {
|
|
484
|
+
generatedMethod += "_admin";
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
488
|
+
return generatedMethod;
|
|
489
|
+
};
|
|
490
|
+
const testConflict = (path2, generatedMethod, existingMethods) => {
|
|
491
|
+
if (existingMethods[generatedMethod]) {
|
|
492
|
+
const conflictingMethod = { path: path2, generatedMethod };
|
|
493
|
+
throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
|
|
494
|
+
existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
352
497
|
|
|
353
498
|
const Schema = z.object({
|
|
354
499
|
$ref: z.string().nullish(),
|
|
@@ -440,50 +585,6 @@ z.object({
|
|
|
440
585
|
}).nullish()
|
|
441
586
|
});
|
|
442
587
|
|
|
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
588
|
const templateMethod = ({
|
|
488
589
|
classMethod,
|
|
489
590
|
description,
|
|
@@ -498,6 +599,9 @@ const templateMethod = ({
|
|
|
498
599
|
let methodSignature = "";
|
|
499
600
|
let newPath = `'${path}'`;
|
|
500
601
|
let dependencies = [];
|
|
602
|
+
let snippetString = "";
|
|
603
|
+
let snippetMethodSignature = "";
|
|
604
|
+
let snippetCurl = "";
|
|
501
605
|
for (const pathParam of pathParams) {
|
|
502
606
|
const type = ParserUtils.parseType(pathParam);
|
|
503
607
|
if (pathParam.name !== "namespace") {
|
|
@@ -512,11 +616,20 @@ const templateMethod = ({
|
|
|
512
616
|
}
|
|
513
617
|
}
|
|
514
618
|
}
|
|
619
|
+
snippetCurl = `<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'`;
|
|
515
620
|
let dataType = null;
|
|
516
621
|
if (httpMethod !== "get") {
|
|
517
622
|
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
518
623
|
dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
519
624
|
methodSignature += dataType ? `data: ${dataType},` : "";
|
|
625
|
+
const snippetParams = bodyParams?.map((ob) => {
|
|
626
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
627
|
+
});
|
|
628
|
+
snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
|
|
629
|
+
const curlParams = bodyParams?.map((ob) => {
|
|
630
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
631
|
+
});
|
|
632
|
+
snippetCurl += ` --data-raw { ${curlParams}}`;
|
|
520
633
|
}
|
|
521
634
|
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
522
635
|
const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
@@ -552,20 +665,23 @@ const templateMethod = ({
|
|
|
552
665
|
const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
|
|
553
666
|
const methodGenerics = resolvedResponseClass !== "unknown" ? `<T = ${resolvedResponseClass}>` : "";
|
|
554
667
|
const responseType = resolvedResponseClass !== "unknown" ? `T` : "unknown";
|
|
668
|
+
const generateMethodName = () => `${methodName}${methodGenerics}(${parameters}): Promise<${responseSyncType}<${responseType}>>`;
|
|
669
|
+
const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
|
|
670
|
+
snippetString += `${generateSnippetMethodName()}`;
|
|
555
671
|
const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
|
|
556
672
|
methodImpl = `${descriptionText}
|
|
557
|
-
${
|
|
673
|
+
${generateMethodName()} {
|
|
558
674
|
${queryParamsDefault}
|
|
559
675
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
560
676
|
const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
561
677
|
|
|
562
|
-
${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
678
|
+
${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
563
679
|
|
|
564
680
|
if (!this.cache) {
|
|
565
681
|
return SdkCache.withoutCache(res)
|
|
566
682
|
}
|
|
567
683
|
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})` : ""}
|
|
684
|
+
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
569
685
|
}
|
|
570
686
|
`;
|
|
571
687
|
if (!isGuardInvoked) {
|
|
@@ -578,7 +694,7 @@ ${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPr
|
|
|
578
694
|
}
|
|
579
695
|
`;
|
|
580
696
|
}
|
|
581
|
-
return [methodImpl, dependencies];
|
|
697
|
+
return [methodImpl, dependencies, snippetString, snippetCurl];
|
|
582
698
|
};
|
|
583
699
|
|
|
584
700
|
class TemplateZod {
|
|
@@ -594,32 +710,32 @@ class TemplateZod {
|
|
|
594
710
|
}
|
|
595
711
|
let imports = "";
|
|
596
712
|
for (const cl of Array.from(this.importClasses).sort()) {
|
|
597
|
-
imports += `
|
|
713
|
+
imports += `import { ${cl} } from './${cl}'
|
|
598
714
|
`;
|
|
599
715
|
}
|
|
600
716
|
let exportedVariableString;
|
|
601
717
|
let exportedTypeString;
|
|
602
718
|
if (containsRecursiveType) {
|
|
603
719
|
exportedVariableString = `
|
|
604
|
-
|
|
720
|
+
export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
|
|
605
721
|
${content.schemaString}
|
|
606
722
|
)
|
|
607
723
|
`;
|
|
608
724
|
exportedTypeString = `
|
|
609
|
-
|
|
725
|
+
export interface ${fileName} {
|
|
610
726
|
${content.typeString}
|
|
611
|
-
|
|
612
|
-
|
|
727
|
+
}
|
|
728
|
+
`;
|
|
613
729
|
} else {
|
|
614
730
|
exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
|
|
615
|
-
exportedTypeString = `export
|
|
731
|
+
exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
|
|
616
732
|
}
|
|
617
733
|
const template = `import { z } from 'zod'
|
|
618
|
-
|
|
734
|
+
${imports}
|
|
619
735
|
|
|
620
|
-
|
|
736
|
+
${exportedVariableString}
|
|
621
737
|
|
|
622
|
-
|
|
738
|
+
${exportedTypeString}
|
|
623
739
|
`;
|
|
624
740
|
return { buffer: template, duplicateFound: this.duplicateFound };
|
|
625
741
|
};
|
|
@@ -776,11 +892,11 @@ class TemplateZodArray {
|
|
|
776
892
|
render = (name) => {
|
|
777
893
|
const cls = name.replace("Array", "");
|
|
778
894
|
const template = `import { z } from 'zod'
|
|
779
|
-
|
|
895
|
+
import { ${cls} } from './${cls}'
|
|
780
896
|
|
|
781
|
-
|
|
897
|
+
export const ${name} = z.array(${cls})
|
|
782
898
|
|
|
783
|
-
|
|
899
|
+
export interface ${name} extends z.TypeOf<typeof ${name}> {}
|
|
784
900
|
`;
|
|
785
901
|
return template;
|
|
786
902
|
};
|
|
@@ -815,24 +931,37 @@ const extractEnumObject = (type, isRequired, enumArr) => {
|
|
|
815
931
|
};
|
|
816
932
|
};
|
|
817
933
|
|
|
934
|
+
const BUILD_DATE = new Date().toISOString();
|
|
818
935
|
class CodeGenerator {
|
|
819
936
|
static getPatchedDir = () => path__default.join(CliParser.getSwaggersOutputPath(), "patched");
|
|
820
937
|
static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
|
|
821
938
|
static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
|
|
939
|
+
static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
|
|
940
|
+
static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
|
|
822
941
|
static iterateApi = async (api) => {
|
|
823
942
|
const apiBufferByTag = {};
|
|
824
|
-
const jsDocApiBufferByTag = {};
|
|
825
943
|
const dependenciesByTag = {};
|
|
826
944
|
const classImports = {};
|
|
827
945
|
let arrayDefinitions = [];
|
|
828
|
-
|
|
946
|
+
const snippetMap = {};
|
|
947
|
+
const mapClassMethods = {};
|
|
948
|
+
const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
|
|
949
|
+
if (a[0].length === b[0].length) {
|
|
950
|
+
return a[0].localeCompare(b[0]);
|
|
951
|
+
} else {
|
|
952
|
+
return a[0].length - b[0].length;
|
|
953
|
+
}
|
|
954
|
+
}));
|
|
955
|
+
const sortedKeys = Array.from(sortedPathsByLength.keys());
|
|
956
|
+
const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
|
|
957
|
+
console.log("ServicePrefix", servicePrefix, ", Paths:", sortedKeys);
|
|
958
|
+
for (const [path2, operation] of sortedPathsByLength) {
|
|
829
959
|
const isAdminEndpoint = path2.indexOf("/admin/") > -1;
|
|
830
960
|
if (CliParser.isAdmin() && !isAdminEndpoint) {
|
|
831
961
|
continue;
|
|
832
962
|
} else if (!CliParser.isAdmin() && isAdminEndpoint) {
|
|
833
963
|
continue;
|
|
834
964
|
}
|
|
835
|
-
const operation = api.paths[path2];
|
|
836
965
|
const httpMethods = Object.keys(operation);
|
|
837
966
|
for (const httpMethod of httpMethods) {
|
|
838
967
|
const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
|
|
@@ -842,59 +971,67 @@ class CodeGenerator {
|
|
|
842
971
|
if (!endpoint.tags) {
|
|
843
972
|
continue;
|
|
844
973
|
}
|
|
974
|
+
if (endpoint.deprecated) {
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
845
977
|
const [tag] = endpoint.tags;
|
|
978
|
+
mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
|
|
979
|
+
const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
|
|
980
|
+
const classMethod = ParserUtils.generateNaturalLangMethod({
|
|
981
|
+
servicePrefix,
|
|
982
|
+
path: path2,
|
|
983
|
+
httpMethod,
|
|
984
|
+
isForm,
|
|
985
|
+
existingMethods: mapClassMethods[tag]
|
|
986
|
+
});
|
|
987
|
+
mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
|
|
988
|
+
snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
|
|
846
989
|
const description = endpoint.description;
|
|
847
|
-
const isDeprecated = endpoint.deprecated;
|
|
848
990
|
const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
|
|
849
|
-
const className =
|
|
991
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
850
992
|
classImports[className] = classImports[className] ? classImports[className] : {};
|
|
851
|
-
if (
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
arrayDefinitions.push(responseClass);
|
|
858
|
-
}
|
|
859
|
-
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
860
|
-
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
861
|
-
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
862
|
-
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
863
|
-
const classMethod = ParserUtils.generateClassMethod({
|
|
864
|
-
path: path2,
|
|
865
|
-
endpoint,
|
|
866
|
-
httpMethod,
|
|
867
|
-
className
|
|
868
|
-
});
|
|
869
|
-
if (endpoint.requestBody) {
|
|
870
|
-
bodyParams = [
|
|
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)];
|
|
993
|
+
if (responseClass) {
|
|
994
|
+
const importTypeClass = ParserUtils.parseRefType(responseClass);
|
|
995
|
+
classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
|
|
996
|
+
}
|
|
997
|
+
if (responseClass && responseClass.endsWith("Array")) {
|
|
998
|
+
arrayDefinitions.push(responseClass);
|
|
893
999
|
}
|
|
1000
|
+
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
1001
|
+
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
1002
|
+
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
1003
|
+
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
1004
|
+
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
1005
|
+
if (endpoint.requestBody) {
|
|
1006
|
+
bodyParams = [
|
|
1007
|
+
{
|
|
1008
|
+
name: "body",
|
|
1009
|
+
in: "body",
|
|
1010
|
+
schema: endpoint.requestBody.content["application/json"].schema
|
|
1011
|
+
}
|
|
1012
|
+
];
|
|
1013
|
+
}
|
|
1014
|
+
const [generatedMethodString, importStatements, snippetString, snippetCurl] = templateMethod({
|
|
1015
|
+
classMethod,
|
|
1016
|
+
description,
|
|
1017
|
+
httpMethod,
|
|
1018
|
+
path: pathWithBase,
|
|
1019
|
+
pathParams,
|
|
1020
|
+
bodyParams,
|
|
1021
|
+
queryParams,
|
|
1022
|
+
isFormUrlEncoded,
|
|
1023
|
+
responseClass
|
|
1024
|
+
});
|
|
1025
|
+
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
|
|
1026
|
+
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1027
|
+
snippetMap[path2][httpMethod] = {
|
|
1028
|
+
web: `<span class='sn-blue'>import</span> axios from 'axios'<br/><span class='sn-blue'>import</span> { <span class='sn-purple'>${classGenName}</span> } <span class='sn-blue'>from</span> <span class='sn-green'>'@accelbyte/sdk'</span><br/><br/><span class='sn-blue'>const</span> axios = <span class='sn-blue'>axios</span>.create({<span class='sn-purple'>clientId</span>, <span class='sn-purple'>redirectURI</span>, <span class='sn-purple'>baseURL</span>})<br/><br/><span class='sn-blue'>new</span> ${classGenName}(<span class='sn-purple'>axios</span>, <span class='sn-purple'>namespace</span>).<span class='method-name'>${snippetString}</span>`,
|
|
1029
|
+
shell: snippetCurl
|
|
1030
|
+
};
|
|
894
1031
|
}
|
|
895
1032
|
}
|
|
896
1033
|
arrayDefinitions = [...new Set(arrayDefinitions)];
|
|
897
|
-
return { apiBufferByTag,
|
|
1034
|
+
return { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap };
|
|
898
1035
|
};
|
|
899
1036
|
static main = async (nameArray) => {
|
|
900
1037
|
const serviceName = nameArray[0];
|
|
@@ -902,7 +1039,6 @@ class CodeGenerator {
|
|
|
902
1039
|
const parser = new SwaggerParser();
|
|
903
1040
|
const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
|
|
904
1041
|
const DIST_DIR = `${generatedFolder}/${serviceName}`;
|
|
905
|
-
const DIST_DOCS_DIR = path__default.join(DIST_DIR, "docs");
|
|
906
1042
|
const DIST_DEFINITION_DIR = path__default.join(DIST_DIR, "definitions");
|
|
907
1043
|
const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
|
|
908
1044
|
const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
|
|
@@ -910,17 +1046,19 @@ class CodeGenerator {
|
|
|
910
1046
|
ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
|
|
911
1047
|
const api = await parser.parse(swaggerPatchedFilePath);
|
|
912
1048
|
const indexImportsSet = /* @__PURE__ */ new Set();
|
|
913
|
-
console.log("
|
|
1049
|
+
console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
|
|
914
1050
|
ParserUtils.mkdirIfNotExist(DIST_DIR);
|
|
915
|
-
ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
|
|
916
1051
|
ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
|
|
917
|
-
|
|
1052
|
+
ParserUtils.writeVersionFile(DIST_DIR, "Version.ts", serviceName, api.info, BUILD_DATE);
|
|
1053
|
+
const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api);
|
|
1054
|
+
if (CliParser.getSnippetOutputPath()) {
|
|
1055
|
+
ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
|
|
1056
|
+
}
|
|
918
1057
|
const targetSrcFolder = `${CliParser.getOutputPath()}/`;
|
|
919
1058
|
for (const tag in apiBufferByTag) {
|
|
920
|
-
const className =
|
|
1059
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
921
1060
|
const apiBuffer = apiBufferByTag[tag];
|
|
922
1061
|
const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
923
|
-
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
924
1062
|
ParserUtils.writeClassFile(DIST_DIR, classGenName, apiBuffer, imports);
|
|
925
1063
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${classGenName}`), targetSrcFolder));
|
|
926
1064
|
}
|
|
@@ -952,7 +1090,7 @@ class CodeGenerator {
|
|
|
952
1090
|
ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
|
|
953
1091
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
|
|
954
1092
|
}
|
|
955
|
-
console.log("\
|
|
1093
|
+
console.log("\nCOMPLETED\n----------\n\n");
|
|
956
1094
|
return indexImportsSet;
|
|
957
1095
|
};
|
|
958
1096
|
}
|
|
@@ -979,7 +1117,7 @@ class SwaggerDownloader {
|
|
|
979
1117
|
});
|
|
980
1118
|
});
|
|
981
1119
|
request.on("error", (err) => {
|
|
982
|
-
console.log(`SwaggerDownloader
|
|
1120
|
+
console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
|
|
983
1121
|
});
|
|
984
1122
|
};
|
|
985
1123
|
static main = () => {
|