@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
|
@@ -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,6 +74,9 @@ 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
82
|
const getImportableVarMap = () => ({
|
|
@@ -101,15 +104,15 @@ const generateImports = (body, importStatements) => {
|
|
|
101
104
|
${importStatements.sort().join("\n")}`;
|
|
102
105
|
};
|
|
103
106
|
const templateClass = (className, body, importStatements) => `/**
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
* DON'T EDIT THIS FILE, it is AUTO GENERATED
|
|
108
|
+
*/
|
|
109
|
+
${generateImports(body, importStatements)}
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
export class ${className} {
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
constructor(private axiosInstance: AxiosInstance, private namespace: string, private cache = false) {}
|
|
111
114
|
${body}
|
|
112
|
-
|
|
115
|
+
}
|
|
113
116
|
`;
|
|
114
117
|
|
|
115
118
|
const templateJsdocFile = (apiName, body) => `
|
|
@@ -120,7 +123,24 @@ ${body}
|
|
|
120
123
|
\`\`\`
|
|
121
124
|
`.replace(/, \)/g, ")").trim();
|
|
122
125
|
|
|
126
|
+
const REMOVED_KEYWORDS = [
|
|
127
|
+
"/admin/",
|
|
128
|
+
"/public/",
|
|
129
|
+
"/v1/",
|
|
130
|
+
"/v2/",
|
|
131
|
+
"/v3/",
|
|
132
|
+
"/v4/",
|
|
133
|
+
"/v5/",
|
|
134
|
+
"/namespace/",
|
|
135
|
+
"/namespaces/",
|
|
136
|
+
"/{namespace}/"
|
|
137
|
+
];
|
|
123
138
|
class ParserUtils {
|
|
139
|
+
static generateClassName = (tag) => {
|
|
140
|
+
const className = _.upperFirst(_.camelCase(tag));
|
|
141
|
+
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
142
|
+
return { className, classGenName };
|
|
143
|
+
};
|
|
124
144
|
static parseQueryParamAttributeDefault = (definition) => {
|
|
125
145
|
const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
|
|
126
146
|
const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
|
|
@@ -261,34 +281,59 @@ class ParserUtils {
|
|
|
261
281
|
}
|
|
262
282
|
return parameters.filter((parameter) => parameter.in === "path");
|
|
263
283
|
}
|
|
264
|
-
static
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
284
|
+
static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
|
|
285
|
+
let path_ = path2;
|
|
286
|
+
path_ = path_.replace(`/${servicePrefix}/`, "/");
|
|
287
|
+
REMOVED_KEYWORDS.forEach((prefix) => {
|
|
288
|
+
path_ = path_.replace(prefix, "/");
|
|
289
|
+
});
|
|
290
|
+
path_ = path_.substring(1);
|
|
291
|
+
const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
|
|
292
|
+
if (!isPlural) {
|
|
293
|
+
path_ = replaceAll(path_, "ies/", "y/");
|
|
294
|
+
path_ = replaceAll(path_, "s/", "/");
|
|
295
|
+
if (path_.indexOf("status") < 0) {
|
|
296
|
+
path_ = path_.replace(/ies$/, "y");
|
|
297
|
+
path_ = path_.replace(/s$/, "");
|
|
278
298
|
}
|
|
279
299
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
300
|
+
const arrLastWords = path_.split("}/");
|
|
301
|
+
let lastWords = arrLastWords[arrLastWords.length - 1];
|
|
302
|
+
const extractLastWord = (lastWords_) => {
|
|
303
|
+
let res = lastWords_;
|
|
304
|
+
res = res.split("/{")[0];
|
|
305
|
+
return res;
|
|
306
|
+
};
|
|
307
|
+
lastWords = extractLastWord(lastWords);
|
|
308
|
+
if (lastWords.indexOf("{") >= 0 && arrLastWords.length > 1) {
|
|
309
|
+
lastWords = arrLastWords[arrLastWords.length - 2];
|
|
310
|
+
lastWords = extractLastWord(lastWords);
|
|
289
311
|
}
|
|
290
|
-
|
|
291
|
-
|
|
312
|
+
const listBeforeLastWords = [];
|
|
313
|
+
let foundParam = false;
|
|
314
|
+
const listByParams = [];
|
|
315
|
+
const pathElements = path_.split("/");
|
|
316
|
+
pathElements.slice().reverse().forEach((item) => {
|
|
317
|
+
if (item.indexOf("}") >= 0) {
|
|
318
|
+
foundParam = true;
|
|
319
|
+
let param = item.replace("{", "");
|
|
320
|
+
param = param.replace("}", "");
|
|
321
|
+
param = "Byword" + _.upperFirst(param);
|
|
322
|
+
listByParams.push(param);
|
|
323
|
+
} else if (!foundParam) {
|
|
324
|
+
if (lastWords.indexOf(item) === -1) {
|
|
325
|
+
listBeforeLastWords.push(item);
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
foundParam = false;
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
|
|
332
|
+
let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
|
|
333
|
+
generatedMethod = replaceAll(generatedMethod, "Byword", "_By");
|
|
334
|
+
generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
|
|
335
|
+
return generatedMethod;
|
|
336
|
+
};
|
|
292
337
|
static filterBodyParams(parameters) {
|
|
293
338
|
if (Array.isArray(parameters) && parameters.length > 0) {
|
|
294
339
|
return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
|
|
@@ -314,6 +359,9 @@ class ParserUtils {
|
|
|
314
359
|
const jsdocFile = templateJsdocFile(nameArray[0], apiBuffer);
|
|
315
360
|
fs.writeFileSync(`${distDir}/docs/${nameArray[0]}.md`, jsdocFile);
|
|
316
361
|
}
|
|
362
|
+
static writeSnippetFile(distDir, name, docBuffer) {
|
|
363
|
+
fs.writeFileSync(`${distDir}/${name}.json`, docBuffer);
|
|
364
|
+
}
|
|
317
365
|
static writeDefinitionFile(distDir, name, buffer) {
|
|
318
366
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
319
367
|
fs.writeFileSync(path.join(distDir, `${name}.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
@@ -322,6 +370,16 @@ class ParserUtils {
|
|
|
322
370
|
ParserUtils.mkdirIfNotExist(distDir);
|
|
323
371
|
fs.writeFileSync(path.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
|
|
324
372
|
}
|
|
373
|
+
static writeVersionFile(distDir, fileName, serviceName, apiInfo, buildDate) {
|
|
374
|
+
const ver = apiInfo.version ? `'${apiInfo.version}'` : void 0;
|
|
375
|
+
fs.writeFileSync(`${distDir}/${fileName}`, `export default {
|
|
376
|
+
title: '${serviceName}',
|
|
377
|
+
name: '${apiInfo.title}',
|
|
378
|
+
version: ${ver},
|
|
379
|
+
buildDate: '${buildDate}'
|
|
380
|
+
}
|
|
381
|
+
`);
|
|
382
|
+
}
|
|
325
383
|
static toCamelCase(str) {
|
|
326
384
|
return str.split("/").map(function(word, index) {
|
|
327
385
|
if (index === 0) {
|
|
@@ -355,6 +413,29 @@ class ParserUtils {
|
|
|
355
413
|
}
|
|
356
414
|
const swaggerContent = JSON.parse(fs.readFileSync(swaggerFilePath, "utf8"));
|
|
357
415
|
const swaggerPatchFileContent = JSON.parse(fs.readFileSync(possibleSwaggerPatchFilePath, "utf8"));
|
|
416
|
+
for (const patchEntry of swaggerPatchFileContent) {
|
|
417
|
+
const segments = patchEntry.path.split("/").filter(Boolean);
|
|
418
|
+
let currentNode = swaggerContent;
|
|
419
|
+
let aggregatedPath = "";
|
|
420
|
+
for (let i = 0; i < segments.length; i++) {
|
|
421
|
+
const segment = segments[i];
|
|
422
|
+
aggregatedPath += `/${segment}`;
|
|
423
|
+
const effectiveSegment = segment.replace(/(~1)/g, "/").replace(/(~0)/g, "~");
|
|
424
|
+
if (!currentNode[effectiveSegment]) {
|
|
425
|
+
if (i + 1 === segments.length && patchEntry.op === "add") ; else {
|
|
426
|
+
throw new Error([
|
|
427
|
+
`JSON patch error: operation "${patchEntry.op}" on path "${aggregatedPath}" fails because the path doesn't exist in ${swaggerFilePath}. This may be caused by:
|
|
428
|
+
`,
|
|
429
|
+
"1. The related service has patched the service, so patch is no longer needed.",
|
|
430
|
+
"2. There is a breaking change on the service that causes the path to change.\n",
|
|
431
|
+
`In any case, revisit this file: "${possibleSwaggerPatchFilePath}", then try again.
|
|
432
|
+
`
|
|
433
|
+
].join("\n"));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
currentNode = currentNode[effectiveSegment];
|
|
437
|
+
}
|
|
438
|
+
}
|
|
358
439
|
const { newDocument } = fastJsonPatch.applyPatch(swaggerContent, swaggerPatchFileContent);
|
|
359
440
|
fs.writeFileSync(swaggerPatchedFilePath, JSON.stringify(newDocument, null, 2));
|
|
360
441
|
}
|
|
@@ -372,6 +453,70 @@ class ParserUtils {
|
|
|
372
453
|
${content}`;
|
|
373
454
|
};
|
|
374
455
|
}
|
|
456
|
+
const replaceAll = (string, search, replace) => {
|
|
457
|
+
return string.split(search).join(replace);
|
|
458
|
+
};
|
|
459
|
+
const mappedMethod = (httpMethod, isForm) => {
|
|
460
|
+
if (httpMethod === "get") {
|
|
461
|
+
return "fetch";
|
|
462
|
+
} else if (httpMethod === "post" && isForm) {
|
|
463
|
+
return "post";
|
|
464
|
+
} else if (httpMethod === "post") {
|
|
465
|
+
return "create";
|
|
466
|
+
} else if (httpMethod === "put") {
|
|
467
|
+
return "update";
|
|
468
|
+
} else if (httpMethod === "patch") {
|
|
469
|
+
return "patch";
|
|
470
|
+
} else if (httpMethod === "delete") {
|
|
471
|
+
return "delete";
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
const resolveConflicts = (path2, generatedMethod, existingMethods) => {
|
|
475
|
+
try {
|
|
476
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
if (path2.indexOf("/namespaces/") >= 0) {
|
|
479
|
+
generatedMethod += "_ByNS";
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
484
|
+
} catch (e) {
|
|
485
|
+
if (path2.indexOf("/v4/") >= 0) {
|
|
486
|
+
generatedMethod += "_v4";
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
if (path2.indexOf("/v3/") >= 0) {
|
|
493
|
+
generatedMethod += "_v3";
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
498
|
+
} catch (e) {
|
|
499
|
+
if (path2.indexOf("/v2/") >= 0) {
|
|
500
|
+
generatedMethod += "_v2";
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
try {
|
|
504
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
505
|
+
} catch (e) {
|
|
506
|
+
if (path2.indexOf("/admin/") >= 0) {
|
|
507
|
+
generatedMethod += "_admin";
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
testConflict(path2, generatedMethod, existingMethods);
|
|
511
|
+
return generatedMethod;
|
|
512
|
+
};
|
|
513
|
+
const testConflict = (path2, generatedMethod, existingMethods) => {
|
|
514
|
+
if (existingMethods[generatedMethod]) {
|
|
515
|
+
const conflictingMethod = { path: path2, generatedMethod };
|
|
516
|
+
throw Error(`Duplicate method conflict in ${JSON.stringify(conflictingMethod)},
|
|
517
|
+
existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
375
520
|
|
|
376
521
|
const Schema = zod.z.object({
|
|
377
522
|
$ref: zod.z.string().nullish(),
|
|
@@ -463,50 +608,6 @@ zod.z.object({
|
|
|
463
608
|
}).nullish()
|
|
464
609
|
});
|
|
465
610
|
|
|
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
611
|
const templateMethod = ({
|
|
511
612
|
classMethod,
|
|
512
613
|
description,
|
|
@@ -521,6 +622,9 @@ const templateMethod = ({
|
|
|
521
622
|
let methodSignature = "";
|
|
522
623
|
let newPath = `'${path}'`;
|
|
523
624
|
let dependencies = [];
|
|
625
|
+
let snippetString = "";
|
|
626
|
+
let snippetMethodSignature = "";
|
|
627
|
+
let snippetCurl = "";
|
|
524
628
|
for (const pathParam of pathParams) {
|
|
525
629
|
const type = ParserUtils.parseType(pathParam);
|
|
526
630
|
if (pathParam.name !== "namespace") {
|
|
@@ -535,11 +639,20 @@ const templateMethod = ({
|
|
|
535
639
|
}
|
|
536
640
|
}
|
|
537
641
|
}
|
|
642
|
+
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'`;
|
|
538
643
|
let dataType = null;
|
|
539
644
|
if (httpMethod !== "get") {
|
|
540
645
|
dataType = ParserUtils.parseBodyParamsType(bodyParams);
|
|
541
646
|
dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
|
|
542
647
|
methodSignature += dataType ? `data: ${dataType},` : "";
|
|
648
|
+
const snippetParams = bodyParams?.map((ob) => {
|
|
649
|
+
return ` <span class='sn-purple'>${ob.name}</span>`;
|
|
650
|
+
});
|
|
651
|
+
snippetMethodSignature += snippetParams ? `data: { ${snippetParams} }` : "";
|
|
652
|
+
const curlParams = bodyParams?.map((ob) => {
|
|
653
|
+
return ` <span class='sn-purple'>"${ob.name}": ""</span>`;
|
|
654
|
+
});
|
|
655
|
+
snippetCurl += ` --data-raw { ${curlParams}}`;
|
|
543
656
|
}
|
|
544
657
|
const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
|
|
545
658
|
const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
|
|
@@ -575,20 +688,23 @@ const templateMethod = ({
|
|
|
575
688
|
const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
|
|
576
689
|
const methodGenerics = resolvedResponseClass !== "unknown" ? `<T = ${resolvedResponseClass}>` : "";
|
|
577
690
|
const responseType = resolvedResponseClass !== "unknown" ? `T` : "unknown";
|
|
691
|
+
const generateMethodName = () => `${methodName}${methodGenerics}(${parameters}): Promise<${responseSyncType}<${responseType}>>`;
|
|
692
|
+
const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
|
|
693
|
+
snippetString += `${generateSnippetMethodName()}`;
|
|
578
694
|
const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
|
|
579
695
|
methodImpl = `${descriptionText}
|
|
580
|
-
${
|
|
696
|
+
${generateMethodName()} {
|
|
581
697
|
${queryParamsDefault}
|
|
582
698
|
const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
|
|
583
699
|
const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
|
|
584
700
|
|
|
585
|
-
${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
701
|
+
${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
|
|
586
702
|
|
|
587
703
|
if (!this.cache) {
|
|
588
704
|
return SdkCache.withoutCache(res)
|
|
589
705
|
}
|
|
590
706
|
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})` : ""}
|
|
707
|
+
return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
|
|
592
708
|
}
|
|
593
709
|
`;
|
|
594
710
|
if (!isGuardInvoked) {
|
|
@@ -601,7 +717,7 @@ ${httpMethod === "get" ? `const res = () => Validate.responseType(() => resultPr
|
|
|
601
717
|
}
|
|
602
718
|
`;
|
|
603
719
|
}
|
|
604
|
-
return [methodImpl, dependencies];
|
|
720
|
+
return [methodImpl, dependencies, snippetString, snippetCurl];
|
|
605
721
|
};
|
|
606
722
|
|
|
607
723
|
class TemplateZod {
|
|
@@ -617,32 +733,32 @@ class TemplateZod {
|
|
|
617
733
|
}
|
|
618
734
|
let imports = "";
|
|
619
735
|
for (const cl of Array.from(this.importClasses).sort()) {
|
|
620
|
-
imports += `
|
|
736
|
+
imports += `import { ${cl} } from './${cl}'
|
|
621
737
|
`;
|
|
622
738
|
}
|
|
623
739
|
let exportedVariableString;
|
|
624
740
|
let exportedTypeString;
|
|
625
741
|
if (containsRecursiveType) {
|
|
626
742
|
exportedVariableString = `
|
|
627
|
-
|
|
743
|
+
export const ${fileName}: z.ZodType<${fileName}> = z.lazy(() =>
|
|
628
744
|
${content.schemaString}
|
|
629
745
|
)
|
|
630
746
|
`;
|
|
631
747
|
exportedTypeString = `
|
|
632
|
-
|
|
748
|
+
export interface ${fileName} {
|
|
633
749
|
${content.typeString}
|
|
634
|
-
|
|
635
|
-
|
|
750
|
+
}
|
|
751
|
+
`;
|
|
636
752
|
} else {
|
|
637
753
|
exportedVariableString = `export const ${fileName} = ${content.schemaString}`;
|
|
638
|
-
exportedTypeString = `export
|
|
754
|
+
exportedTypeString = `export interface ${fileName} extends z.TypeOf<typeof ${fileName}> {}`;
|
|
639
755
|
}
|
|
640
756
|
const template = `import { z } from 'zod'
|
|
641
|
-
|
|
757
|
+
${imports}
|
|
642
758
|
|
|
643
|
-
|
|
759
|
+
${exportedVariableString}
|
|
644
760
|
|
|
645
|
-
|
|
761
|
+
${exportedTypeString}
|
|
646
762
|
`;
|
|
647
763
|
return { buffer: template, duplicateFound: this.duplicateFound };
|
|
648
764
|
};
|
|
@@ -799,11 +915,11 @@ class TemplateZodArray {
|
|
|
799
915
|
render = (name) => {
|
|
800
916
|
const cls = name.replace("Array", "");
|
|
801
917
|
const template = `import { z } from 'zod'
|
|
802
|
-
|
|
918
|
+
import { ${cls} } from './${cls}'
|
|
803
919
|
|
|
804
|
-
|
|
920
|
+
export const ${name} = z.array(${cls})
|
|
805
921
|
|
|
806
|
-
|
|
922
|
+
export interface ${name} extends z.TypeOf<typeof ${name}> {}
|
|
807
923
|
`;
|
|
808
924
|
return template;
|
|
809
925
|
};
|
|
@@ -838,24 +954,37 @@ const extractEnumObject = (type, isRequired, enumArr) => {
|
|
|
838
954
|
};
|
|
839
955
|
};
|
|
840
956
|
|
|
957
|
+
const BUILD_DATE = new Date().toISOString();
|
|
841
958
|
class CodeGenerator {
|
|
842
959
|
static getPatchedDir = () => path.join(CliParser.getSwaggersOutputPath(), "patched");
|
|
843
960
|
static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
|
|
844
961
|
static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
|
|
962
|
+
static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
|
|
963
|
+
static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
|
|
845
964
|
static iterateApi = async (api) => {
|
|
846
965
|
const apiBufferByTag = {};
|
|
847
|
-
const jsDocApiBufferByTag = {};
|
|
848
966
|
const dependenciesByTag = {};
|
|
849
967
|
const classImports = {};
|
|
850
968
|
let arrayDefinitions = [];
|
|
851
|
-
|
|
969
|
+
const snippetMap = {};
|
|
970
|
+
const mapClassMethods = {};
|
|
971
|
+
const sortedPathsByLength = new Map(Object.entries(api.paths).sort((a, b) => {
|
|
972
|
+
if (a[0].length === b[0].length) {
|
|
973
|
+
return a[0].localeCompare(b[0]);
|
|
974
|
+
} else {
|
|
975
|
+
return a[0].length - b[0].length;
|
|
976
|
+
}
|
|
977
|
+
}));
|
|
978
|
+
const sortedKeys = Array.from(sortedPathsByLength.keys());
|
|
979
|
+
const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
|
|
980
|
+
console.log("ServicePrefix", servicePrefix, ", Paths:", sortedKeys);
|
|
981
|
+
for (const [path2, operation] of sortedPathsByLength) {
|
|
852
982
|
const isAdminEndpoint = path2.indexOf("/admin/") > -1;
|
|
853
983
|
if (CliParser.isAdmin() && !isAdminEndpoint) {
|
|
854
984
|
continue;
|
|
855
985
|
} else if (!CliParser.isAdmin() && isAdminEndpoint) {
|
|
856
986
|
continue;
|
|
857
987
|
}
|
|
858
|
-
const operation = api.paths[path2];
|
|
859
988
|
const httpMethods = Object.keys(operation);
|
|
860
989
|
for (const httpMethod of httpMethods) {
|
|
861
990
|
const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
|
|
@@ -865,59 +994,67 @@ class CodeGenerator {
|
|
|
865
994
|
if (!endpoint.tags) {
|
|
866
995
|
continue;
|
|
867
996
|
}
|
|
997
|
+
if (endpoint.deprecated) {
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
868
1000
|
const [tag] = endpoint.tags;
|
|
1001
|
+
mapClassMethods[tag] = mapClassMethods[tag] ? mapClassMethods[tag] : {};
|
|
1002
|
+
const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
|
|
1003
|
+
const classMethod = ParserUtils.generateNaturalLangMethod({
|
|
1004
|
+
servicePrefix,
|
|
1005
|
+
path: path2,
|
|
1006
|
+
httpMethod,
|
|
1007
|
+
isForm,
|
|
1008
|
+
existingMethods: mapClassMethods[tag]
|
|
1009
|
+
});
|
|
1010
|
+
mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
|
|
1011
|
+
snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
|
|
869
1012
|
const description = endpoint.description;
|
|
870
|
-
const isDeprecated = endpoint.deprecated;
|
|
871
1013
|
const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
|
|
872
|
-
const className =
|
|
1014
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
873
1015
|
classImports[className] = classImports[className] ? classImports[className] : {};
|
|
874
|
-
if (
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
arrayDefinitions.push(responseClass);
|
|
881
|
-
}
|
|
882
|
-
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
883
|
-
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
884
|
-
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
885
|
-
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
886
|
-
const classMethod = ParserUtils.generateClassMethod({
|
|
887
|
-
path: path2,
|
|
888
|
-
endpoint,
|
|
889
|
-
httpMethod,
|
|
890
|
-
className
|
|
891
|
-
});
|
|
892
|
-
if (endpoint.requestBody) {
|
|
893
|
-
bodyParams = [
|
|
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)];
|
|
1016
|
+
if (responseClass) {
|
|
1017
|
+
const importTypeClass = ParserUtils.parseRefType(responseClass);
|
|
1018
|
+
classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
|
|
1019
|
+
}
|
|
1020
|
+
if (responseClass && responseClass.endsWith("Array")) {
|
|
1021
|
+
arrayDefinitions.push(responseClass);
|
|
916
1022
|
}
|
|
1023
|
+
const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
|
|
1024
|
+
const pathWithBase = `${api.basePath ?? ""}${path2}`;
|
|
1025
|
+
const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
|
|
1026
|
+
const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
|
|
1027
|
+
let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
|
|
1028
|
+
if (endpoint.requestBody) {
|
|
1029
|
+
bodyParams = [
|
|
1030
|
+
{
|
|
1031
|
+
name: "body",
|
|
1032
|
+
in: "body",
|
|
1033
|
+
schema: endpoint.requestBody.content["application/json"].schema
|
|
1034
|
+
}
|
|
1035
|
+
];
|
|
1036
|
+
}
|
|
1037
|
+
const [generatedMethodString, importStatements, snippetString, snippetCurl] = templateMethod({
|
|
1038
|
+
classMethod,
|
|
1039
|
+
description,
|
|
1040
|
+
httpMethod,
|
|
1041
|
+
path: pathWithBase,
|
|
1042
|
+
pathParams,
|
|
1043
|
+
bodyParams,
|
|
1044
|
+
queryParams,
|
|
1045
|
+
isFormUrlEncoded,
|
|
1046
|
+
responseClass
|
|
1047
|
+
});
|
|
1048
|
+
apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
|
|
1049
|
+
dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
|
|
1050
|
+
snippetMap[path2][httpMethod] = {
|
|
1051
|
+
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>`,
|
|
1052
|
+
shell: snippetCurl
|
|
1053
|
+
};
|
|
917
1054
|
}
|
|
918
1055
|
}
|
|
919
1056
|
arrayDefinitions = [...new Set(arrayDefinitions)];
|
|
920
|
-
return { apiBufferByTag,
|
|
1057
|
+
return { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap };
|
|
921
1058
|
};
|
|
922
1059
|
static main = async (nameArray) => {
|
|
923
1060
|
const serviceName = nameArray[0];
|
|
@@ -925,7 +1062,6 @@ class CodeGenerator {
|
|
|
925
1062
|
const parser = new SwaggerParser();
|
|
926
1063
|
const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
|
|
927
1064
|
const DIST_DIR = `${generatedFolder}/${serviceName}`;
|
|
928
|
-
const DIST_DOCS_DIR = path.join(DIST_DIR, "docs");
|
|
929
1065
|
const DIST_DEFINITION_DIR = path.join(DIST_DIR, "definitions");
|
|
930
1066
|
const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
|
|
931
1067
|
const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
|
|
@@ -933,17 +1069,19 @@ class CodeGenerator {
|
|
|
933
1069
|
ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
|
|
934
1070
|
const api = await parser.parse(swaggerPatchedFilePath);
|
|
935
1071
|
const indexImportsSet = /* @__PURE__ */ new Set();
|
|
936
|
-
console.log("
|
|
1072
|
+
console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
|
|
937
1073
|
ParserUtils.mkdirIfNotExist(DIST_DIR);
|
|
938
|
-
ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
|
|
939
1074
|
ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
|
|
940
|
-
|
|
1075
|
+
ParserUtils.writeVersionFile(DIST_DIR, "Version.ts", serviceName, api.info, BUILD_DATE);
|
|
1076
|
+
const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api);
|
|
1077
|
+
if (CliParser.getSnippetOutputPath()) {
|
|
1078
|
+
ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
|
|
1079
|
+
}
|
|
941
1080
|
const targetSrcFolder = `${CliParser.getOutputPath()}/`;
|
|
942
1081
|
for (const tag in apiBufferByTag) {
|
|
943
|
-
const className =
|
|
1082
|
+
const { className, classGenName } = ParserUtils.generateClassName(tag);
|
|
944
1083
|
const apiBuffer = apiBufferByTag[tag];
|
|
945
1084
|
const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
|
|
946
|
-
const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
|
|
947
1085
|
ParserUtils.writeClassFile(DIST_DIR, classGenName, apiBuffer, imports);
|
|
948
1086
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DIR, `${classGenName}`), targetSrcFolder));
|
|
949
1087
|
}
|
|
@@ -975,7 +1113,7 @@ class CodeGenerator {
|
|
|
975
1113
|
ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
|
|
976
1114
|
indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
|
|
977
1115
|
}
|
|
978
|
-
console.log("\
|
|
1116
|
+
console.log("\nCOMPLETED\n----------\n\n");
|
|
979
1117
|
return indexImportsSet;
|
|
980
1118
|
};
|
|
981
1119
|
}
|
|
@@ -1002,7 +1140,7 @@ class SwaggerDownloader {
|
|
|
1002
1140
|
});
|
|
1003
1141
|
});
|
|
1004
1142
|
request.on("error", (err) => {
|
|
1005
|
-
console.log(`SwaggerDownloader
|
|
1143
|
+
console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
|
|
1006
1144
|
});
|
|
1007
1145
|
};
|
|
1008
1146
|
static main = () => {
|