@accelbyte/codegen 1.0.0-beta.7 → 1.0.1-experimental.1

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.
@@ -1,7 +1,7 @@
1
1
  import yargs from 'yargs';
2
2
  import { z } from 'zod';
3
3
  import * as fs from 'fs';
4
- import fs__default from 'fs';
4
+ import fs__default, { readFileSync, writeFileSync } from 'fs';
5
5
  import * as path from 'path';
6
6
  import path__default from 'path';
7
7
  import SwaggerParser from '@apidevtools/swagger-parser';
@@ -56,18 +56,18 @@ class CliParser {
56
56
  };
57
57
  }
58
58
 
59
- const getImportableVarMap = () => ({
59
+ const getImportableVarMap$1 = () => ({
60
60
  "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate"],
61
61
  axios: ["AxiosRequestConfig", "AxiosResponse"],
62
62
  zod: ["z"]
63
63
  });
64
- const makeNewImportVarMap = () => ({
64
+ const makeNewImportVarMap$1 = () => ({
65
65
  axios: ["AxiosInstance"],
66
66
  "@accelbyte/sdk": ["SDKRequestConfig"]
67
67
  });
68
- const generateImports = (body, importStatements) => {
69
- const usedImportVarMap = makeNewImportVarMap();
70
- const importableVarMap = getImportableVarMap();
68
+ const generateImports = (body, importStatements, makeNewImportVarMap2, getImportableVarMap2) => {
69
+ const usedImportVarMap = makeNewImportVarMap2;
70
+ const importableVarMap = getImportableVarMap2;
71
71
  for (const [moduleSource, importableVars] of Object.entries(importableVarMap)) {
72
72
  for (const importableVar of importableVars) {
73
73
  const importVarRegex = new RegExp(`(?<![\\d\\w_])${importableVar}(?![\\d\\w_])`);
@@ -80,10 +80,11 @@ const generateImports = (body, importStatements) => {
80
80
  return `${generatedImports}
81
81
  ${importStatements.sort().join("\n")}`;
82
82
  };
83
- const templateClass = (className, body, importStatements) => `/**
84
- * DON'T EDIT THIS FILE, it is AUTO GENERATED
83
+ const templateClass = (className, body, importStatements) => {
84
+ return `/**
85
+ * AUTO GENERATED
85
86
  */
86
- ${generateImports(body, importStatements)}
87
+ ${generateImports(body, importStatements, makeNewImportVarMap$1(), getImportableVarMap$1())}
87
88
 
88
89
  export class ${className} {
89
90
  // @ts-ignore
@@ -91,15 +92,37 @@ export class ${className} {
91
92
  ${body}
92
93
  }
93
94
  `;
95
+ };
94
96
 
95
- const templateJsdocFile = (apiName, body) => `
96
- \`\`\`
97
- SDK.getService('${apiName}', authHeader):
98
- ${body}
97
+ const getImportableVarMap = () => ({
98
+ "@accelbyte/sdk": ["CodeGenUtil", "SdkCache", "IResponse", "IResponseWithSync", "Validate", "ApiArgs", "Network", "AccelbyteSDK"]
99
+ });
100
+ const makeNewImportVarMap = () => ({
101
+ "@accelbyte/sdk": ["AccelbyteSDK", "ApiArgs", "ApiUtils"]
102
+ });
103
+ const templateApiClass = (className, body, importStatements, returnMethods) => {
104
+ return `/**
105
+ * AUTO GENERATED
106
+ */
107
+ /* eslint-disable camelcase */
108
+ ${generateImports(body, importStatements, makeNewImportVarMap(), getImportableVarMap())}
99
109
 
100
- \`\`\`
101
- `.replace(/, \)/g, ")").trim();
110
+ export function ${className}(sdk: AccelbyteSDK, args?: ApiArgs) {
111
+ const sdkAssembly = sdk.assembly()
112
+
113
+ const namespace = args?.namespace ? args?.namespace : sdkAssembly.namespace
114
+ const cache = args?.cache ? args?.cache : sdkAssembly.cache
115
+ const requestConfig = ApiUtils.mergedConfigs(sdkAssembly.config, args)
116
+ ${body}
117
+
118
+ return {
119
+ ${returnMethods}
120
+ }
121
+ }
122
+ `;
123
+ };
102
124
 
125
+ const UNDEFINED_SWAGGER_SEMVER = "0.0.0";
103
126
  const REMOVED_KEYWORDS = [
104
127
  "/admin/",
105
128
  "/public/",
@@ -113,39 +136,62 @@ const REMOVED_KEYWORDS = [
113
136
  "/{namespace}/"
114
137
  ];
115
138
  class ParserUtils {
139
+ static replaceAll = (string, search, replace) => {
140
+ return string.split(search).join(replace);
141
+ };
116
142
  static generateClassName = (tag) => {
117
143
  const className = _.upperFirst(_.camelCase(tag));
118
144
  const classGenName = CliParser.isAdmin() ? className + "Admin$" : className + "$";
119
145
  return { className, classGenName };
120
146
  };
147
+ static generateApiName = (tag) => {
148
+ const apiName = _.upperFirst(_.camelCase(tag));
149
+ const apiGenName = CliParser.isAdmin() ? apiName + "AdminApi" : apiName + "Api";
150
+ return { apiName, apiGenName };
151
+ };
121
152
  static parseQueryParamAttributeDefault = (definition) => {
122
153
  const attrName = definition.name.slice(definition.name.lastIndexOf(".") + 1);
123
154
  const defaultValue = definition.type === "string" ? `'${definition.default}'` : definition.default;
124
155
  return `${attrName}: ${defaultValue}`;
125
156
  };
126
157
  static parseType = (pathParam) => {
127
- if (pathParam.type === "int" || pathParam.type === "integer" || pathParam?.schema?.type === "integer")
158
+ if (isSwaggerIntegerType(pathParam.type || pathParam?.schema?.type))
128
159
  return "number";
129
- if (pathParam.type === "array")
160
+ if (pathParam.type === "array") {
161
+ if (isSwaggerIntegerType(pathParam.items.type))
162
+ return "number[]";
130
163
  return `${pathParam.items.type ?? "any"}[]`;
131
- if (pathParam?.schema?.type === "array")
164
+ }
165
+ if (pathParam?.schema?.type === "array") {
166
+ if (isSwaggerIntegerType(pathParam.schema.items.type))
167
+ return "number[]";
132
168
  return `${pathParam.schema.items.type ?? "any"}[]`;
169
+ }
133
170
  if (pathParam?.schema?.type)
134
171
  return pathParam.schema.type;
135
172
  return pathParam.type;
136
173
  };
137
174
  static parseQueryParamsType = (queryParams) => {
138
- return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(",");
175
+ return queryParams.map((queryParam) => ParserUtils.parseAttributeType(queryParam)).join(", ");
139
176
  };
140
177
  static isAnyQueryParamRequired = (queryParams) => {
141
178
  return queryParams.some((queryParam) => queryParam.required);
142
179
  };
180
+ static convertDashesToTitleCase = (str) => {
181
+ const result = str.split("-").map((word, index) => {
182
+ if (index === 0) {
183
+ return word.charAt(0).toUpperCase() + word.slice(1);
184
+ }
185
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
186
+ }).join("");
187
+ return result;
188
+ };
143
189
  static parseQueryParamsDefault = (queryParams) => {
144
190
  const result = queryParams.filter((queryParam) => !!queryParam.default && !queryParam.required).map(ParserUtils.parseQueryParamAttributeDefault).join(",");
145
191
  return result ? `${result},` : "";
146
192
  };
147
193
  static parseBodyParamsImports = (bodyParams) => {
148
- return bodyParams.map(ParserUtils.parseRefImport).filter(Boolean);
194
+ return bodyParams.map((bodyParams2) => ParserUtils.parseRefImport(bodyParams2)).filter(Boolean);
149
195
  };
150
196
  static parseImportDir = ($ref) => {
151
197
  let ref = $ref.replace(".", "/");
@@ -164,7 +210,7 @@ class ParserUtils {
164
210
  return null;
165
211
  }
166
212
  const type = ParserUtils.parseRefType($ref);
167
- return `import { ${type} } from './definitions/${type}'`;
213
+ return `import { ${type} } from '../definitions/${type}.js'`;
168
214
  };
169
215
  static parseRefType = ($ref) => {
170
216
  let ref = $ref.replace(".", "/");
@@ -211,6 +257,8 @@ class ParserUtils {
211
257
  return retBodyParams;
212
258
  }
213
259
  if (bodyParam?.schema?.type === "array" && !bodyParam?.schema?.items?.$ref) {
260
+ if (isSwaggerIntegerType(bodyParam.schema.items.type))
261
+ return "number[]";
214
262
  return `${bodyParam.schema.items.type ?? "any"}[]`;
215
263
  }
216
264
  if (bodyParam?.schema?.type === "array" && bodyParam?.schema?.items?.$ref) {
@@ -267,8 +315,8 @@ class ParserUtils {
267
315
  path_ = path_.substring(1);
268
316
  const isPlural = httpMethod === "get" && !(path2.slice(-1) === "}");
269
317
  if (!isPlural) {
270
- path_ = replaceAll(path_, "ies/", "y/");
271
- path_ = replaceAll(path_, "s/", "/");
318
+ path_ = ParserUtils.replaceAll(path_, "ies/", "y/");
319
+ path_ = ParserUtils.replaceAll(path_, "s/", "/");
272
320
  if (path_.indexOf("status") < 0) {
273
321
  path_ = path_.replace(/ies$/, "y");
274
322
  path_ = path_.replace(/s$/, "");
@@ -307,7 +355,7 @@ class ParserUtils {
307
355
  });
308
356
  const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + listByParams.reverse().join("/");
309
357
  let generatedMethod = _.camelCase(mappedMethod(httpMethod, isForm) + genPath);
310
- generatedMethod = replaceAll(generatedMethod, "Byword", "_By");
358
+ generatedMethod = ParserUtils.replaceAll(generatedMethod, "Byword", "_By");
311
359
  generatedMethod = resolveConflicts(path2, generatedMethod, existingMethods);
312
360
  return generatedMethod;
313
361
  };
@@ -332,12 +380,22 @@ class ParserUtils {
332
380
  const fileContent = templateClass(apiName, apiBuffer, imports);
333
381
  fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
334
382
  }
335
- static writeJsdocFile(distDir, nameArray, apiBuffer) {
336
- const jsdocFile = templateJsdocFile(nameArray[0], apiBuffer);
337
- fs__default.writeFileSync(`${distDir}/docs/${nameArray[0]}.md`, jsdocFile);
383
+ static writeApiFile(distDir, apiName, apiBuffer, imports, returnMethods) {
384
+ const newImports = [];
385
+ imports.forEach((el, index) => {
386
+ newImports.push(el.replace("../definitions", "./definitions"));
387
+ });
388
+ const fileContent = templateApiClass(apiName, apiBuffer, newImports, returnMethods);
389
+ fs__default.writeFileSync(`${distDir}/${apiName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
390
+ }
391
+ static writeApiMainFile(distDir, serviceName, fileContent) {
392
+ fs__default.writeFileSync(`${distDir}/${serviceName}.ts`, ParserUtils.prependCopyrightHeader(fileContent));
338
393
  }
339
394
  static writeSnippetFile(distDir, name, docBuffer) {
340
- fs__default.writeFileSync(`${distDir}/${name}.json`, docBuffer);
395
+ let snippetFileName = ParserUtils.replaceAll(name, " ", "-").toLowerCase();
396
+ snippetFileName = snippetFileName.replace("justice-", "");
397
+ snippetFileName = "snippet-" + snippetFileName + ".json";
398
+ fs__default.writeFileSync(`${distDir}/${snippetFileName}`, docBuffer);
341
399
  }
342
400
  static writeDefinitionFile(distDir, name, buffer) {
343
401
  ParserUtils.mkdirIfNotExist(distDir);
@@ -347,15 +405,36 @@ class ParserUtils {
347
405
  ParserUtils.mkdirIfNotExist(distDir);
348
406
  fs__default.writeFileSync(path__default.join(distDir, `all-${isAdminWebSdk ? "admin" : "public"}-imports.ts`), ParserUtils.prependCopyrightHeader(buffer));
349
407
  }
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
- `);
408
+ static syncChangelog(packageVersion) {
409
+ const currDir = process.cwd();
410
+ const pathToChangelog = path__default.join(currDir, "./CHANGELOG.md");
411
+ let fileContent = readFileSync(pathToChangelog, "utf-8");
412
+ const date = new Date().toISOString().slice(0, 10);
413
+ fileContent = "### " + packageVersion + " - " + date + `
414
+
415
+ - code-generated update
416
+
417
+ ` + fileContent.trim();
418
+ fs__default.writeFileSync(pathToChangelog, fileContent, "utf-8");
419
+ }
420
+ static syncPackageVersion(apiInfo, isAdminWebSdk) {
421
+ if (isAdminWebSdk) {
422
+ return;
423
+ }
424
+ const currDir = process.cwd();
425
+ const { packageJSON, pathToPackageJSON } = ParserUtils.getPackageJSONInfo(currDir);
426
+ let swaggerVersion = apiInfo.version ? apiInfo.version : UNDEFINED_SWAGGER_SEMVER;
427
+ swaggerVersion = Number(swaggerVersion.replace(".", "").replace(".", ""));
428
+ swaggerVersion = isNaN(swaggerVersion) ? 0 : swaggerVersion;
429
+ const semver = packageJSON.version.split(".").map((numStr) => Number(numStr));
430
+ const newSemver = [semver[0], swaggerVersion, ++semver[2]];
431
+ packageJSON.version = newSemver[0] + "." + newSemver[1] + "." + newSemver[2];
432
+ writeFileSync(pathToPackageJSON, JSON.stringify(packageJSON, null, 2));
433
+ ParserUtils.syncChangelog(packageJSON.version);
434
+ }
435
+ static getPackageJSONInfo(dir) {
436
+ const pathToPackageJSON = path__default.join(dir, "./package.json");
437
+ return { packageJSON: JSON.parse(readFileSync(pathToPackageJSON, "utf-8")), pathToPackageJSON };
359
438
  }
360
439
  static toCamelCase(str) {
361
440
  return str.split("/").map(function(word, index) {
@@ -430,12 +509,9 @@ class ParserUtils {
430
509
  ${content}`;
431
510
  };
432
511
  }
433
- const replaceAll = (string, search, replace) => {
434
- return string.split(search).join(replace);
435
- };
436
512
  const mappedMethod = (httpMethod, isForm) => {
437
513
  if (httpMethod === "get") {
438
- return "fetch";
514
+ return "get";
439
515
  } else if (httpMethod === "post" && isForm) {
440
516
  return "post";
441
517
  } else if (httpMethod === "post") {
@@ -494,6 +570,9 @@ const testConflict = (path2, generatedMethod, existingMethods) => {
494
570
  existingMethods: ${JSON.stringify(existingMethods, null, 2)}`);
495
571
  }
496
572
  };
573
+ const isSwaggerIntegerType = (type) => {
574
+ return type === "integer" || type === "int";
575
+ };
497
576
 
498
577
  const Schema = z.object({
499
578
  $ref: z.string().nullish(),
@@ -596,16 +675,15 @@ const templateMethod = ({
596
675
  isFormUrlEncoded,
597
676
  responseClass
598
677
  }) => {
599
- let methodSignature = "";
678
+ let methodParams = "";
679
+ let methodParamsNoTypes = "";
600
680
  let newPath = `'${path}'`;
601
- let dependencies = [];
602
- let snippetString = "";
603
- let snippetMethodSignature = "";
604
- let snippetCurl = "";
681
+ let importStatements = [];
605
682
  for (const pathParam of pathParams) {
606
683
  const type = ParserUtils.parseType(pathParam);
607
684
  if (pathParam.name !== "namespace") {
608
- methodSignature += pathParam.name + `:${type}, `;
685
+ methodParams += pathParam.name + `:${type}, `;
686
+ methodParamsNoTypes += pathParam.name + ", ";
609
687
  }
610
688
  const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
611
689
  if (path.match(`{${pathParam.name}}`)) {
@@ -616,20 +694,12 @@ const templateMethod = ({
616
694
  }
617
695
  }
618
696
  }
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'`;
620
697
  let dataType = null;
621
698
  if (httpMethod !== "get") {
622
699
  dataType = ParserUtils.parseBodyParamsType(bodyParams);
623
- dependencies = ParserUtils.parseBodyParamsImports(bodyParams);
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}}`;
700
+ importStatements = ParserUtils.parseBodyParamsImports(bodyParams);
701
+ methodParams += dataType ? `data: ${dataType},` : "";
702
+ methodParamsNoTypes += dataType ? `data,` : "";
633
703
  }
634
704
  const isAnyRequired = ParserUtils.isAnyQueryParamRequired(queryParams);
635
705
  const queryParamsType = queryParams.length ? `queryParams${isAnyRequired ? "" : "?"}: {${ParserUtils.parseQueryParamsType(queryParams)}}` : "";
@@ -639,7 +709,7 @@ const templateMethod = ({
639
709
  let dataPayload = "{params}";
640
710
  const descriptionText = description ? `
641
711
  /**
642
- * ${(description || "").replace(/\n/g, "\n * ")}
712
+ * ${description.replace(/\n/g, "\n * ")}
643
713
  */` : "";
644
714
  let formPayloadString = "";
645
715
  if (isFormUrlEncoded) {
@@ -651,23 +721,21 @@ const templateMethod = ({
651
721
  } else if (isDelete) {
652
722
  dataPayload = dataType ? `{data, params}` : "{params}";
653
723
  }
654
- const isFileUpload = methodSignature.indexOf("data: {file") > -1;
724
+ const isFileUpload = methodParams.indexOf("data: {file") > -1;
655
725
  const resolvedResponseClass = responseClass || "unknown";
656
726
  const resolvedResponseClassValidated = responseClass || "z.unknown()";
657
- const parameters = (queryParamsType ? `${methodSignature} ${queryParamsType}` : methodSignature).replace(/,\s*$/, "");
727
+ methodParams = (queryParamsType ? `${methodParams} ${queryParamsType}` : methodParams).replace(/,\s*$/, "");
728
+ methodParamsNoTypes = queryParamsType ? `${methodParamsNoTypes} queryParams` : methodParamsNoTypes;
658
729
  let methodImpl = "";
659
730
  const isCacheFetch = ["get"].includes(httpMethod) && resolvedResponseClass !== "unknown";
660
- const cachedFetchMethod = classMethod.replace("get", "fetch");
731
+ const cachedFetchMethod = classMethod;
661
732
  const deprecateTag = isCacheFetch ? `/**
662
- * @deprecated Use "${cachedFetchMethod}()" instead.
733
+ * @deprecated Use "${classMethod}()" instead.
663
734
  */` : "";
664
735
  const isGuardInvoked = ["get", "post", "put", "patch", "delete"].includes(httpMethod);
665
736
  const methodName = httpMethod === "get" ? cachedFetchMethod : ["post", "put", "patch", "delete"].includes(httpMethod) ? classMethod : "";
666
- const methodGenerics = resolvedResponseClass !== "unknown" ? `<T = ${resolvedResponseClass}>` : "";
667
- const responseType = resolvedResponseClass !== "unknown" ? `T` : "unknown";
668
- const generateMethodName = () => `${methodName}${methodGenerics}(${parameters}): Promise<${responseSyncType}<${responseType}>>`;
669
- const generateSnippetMethodName = () => `${methodName}(${snippetMethodSignature})`;
670
- snippetString += `${generateSnippetMethodName()}`;
737
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
738
+ const generateMethodName = () => `${methodName}(${methodParams}): Promise<${responseSyncType}<${responseType}>>`;
671
739
  const responseSyncType = httpMethod === "get" ? "IResponseWithSync" : ["post", "put", "patch", "delete"].includes(httpMethod) ? "IResponse" : "";
672
740
  methodImpl = `${descriptionText}
673
741
  ${generateMethodName()} {
@@ -675,26 +743,32 @@ const templateMethod = ({
675
743
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
676
744
  const resultPromise = this.axiosInstance.${httpMethod}(url, ${dataPayload})
677
745
 
678
- ${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})
746
+ ${httpMethod === "get" ? ` const res = () => Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated}, '${resolvedResponseClassValidated}')
679
747
 
680
748
  if (!this.cache) {
681
749
  return SdkCache.withoutCache(res)
682
750
  }
683
751
  const cacheKey = url + CodeGenUtil.hashCode(JSON.stringify({ params }))
684
- return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated})` : ""}
752
+ return SdkCache.withCache(cacheKey, res)` : ""}${["post", "put", "patch", "delete"].includes(httpMethod) ? ` return Validate.responseType(() => resultPromise, ${resolvedResponseClassValidated}, '${resolvedResponseClassValidated}')` : ""}
685
753
  }
686
754
  `;
687
755
  if (!isGuardInvoked) {
688
756
  methodImpl = `${descriptionText}
689
757
  ${deprecateTag}
690
- TODO_${classMethod}<T = ${resolvedResponseClass}>(${parameters}): Promise<AxiosResponse<T>> {
758
+ TODO_${classMethod}(${methodParams}): Promise<AxiosResponse<${resolvedResponseClass}>> {
691
759
  ${queryParamsDefault}
692
760
  const url = ${newPath} ${formPayloadString} ${isFileUpload ? "\n// TODO file upload not implemented" : ""}
693
761
  return this.axiosInstance.${httpMethod}(url, ${dataPayload})
694
762
  }
695
763
  `;
696
764
  }
697
- return [methodImpl, dependencies, snippetString, snippetCurl];
765
+ const res = {
766
+ methodImpl,
767
+ methodParams,
768
+ methodParamsNoTypes,
769
+ importStatements
770
+ };
771
+ return res;
698
772
  };
699
773
 
700
774
  class TemplateZod {
@@ -710,7 +784,7 @@ class TemplateZod {
710
784
  }
711
785
  let imports = "";
712
786
  for (const cl of Array.from(this.importClasses).sort()) {
713
- imports += `import { ${cl} } from './${cl}'
787
+ imports += `import { ${cl} } from './${cl}.js'
714
788
  `;
715
789
  }
716
790
  let exportedVariableString;
@@ -910,7 +984,7 @@ class TemplateZodArray {
910
984
  render = (name) => {
911
985
  const cls = name.replace("Array", "");
912
986
  const template = `import { z } from 'zod'
913
- import { ${cls} } from './${cls}'
987
+ import { ${cls} } from './${cls}.js'
914
988
 
915
989
  export const ${name} = z.array(${cls})
916
990
 
@@ -949,15 +1023,138 @@ const extractEnumObject = (type, isRequired, enumArr) => {
949
1023
  };
950
1024
  };
951
1025
 
952
- const BUILD_DATE = new Date().toISOString();
1026
+ const templateApiMethod = ({
1027
+ classMethod,
1028
+ description,
1029
+ httpMethod,
1030
+ path,
1031
+ pathParams,
1032
+ bodyParams,
1033
+ responseClass,
1034
+ classGenName,
1035
+ methodParams,
1036
+ methodParamsNoTypes
1037
+ }) => {
1038
+ let methodSignature = "";
1039
+ let newPath = `'${path}'`;
1040
+ let snippetSdk = "";
1041
+ let snippetShell = "";
1042
+ for (const pathParam of pathParams) {
1043
+ const type = ParserUtils.parseType(pathParam);
1044
+ if (pathParam.name !== "namespace") {
1045
+ methodSignature += pathParam.name + `:${type}, `;
1046
+ }
1047
+ const pName = pathParam.name === "namespace" ? "this.namespace" : pathParam.name;
1048
+ if (path.match(`{${pathParam.name}}`)) {
1049
+ if (type === "string") {
1050
+ newPath = `${newPath}.replace('{${pathParam.name}}', ${pName})`;
1051
+ } else {
1052
+ newPath = `${newPath}.replace('{${pathParam.name}}', String(${pName}))`;
1053
+ }
1054
+ }
1055
+ }
1056
+ snippetShell = `curl --location --request \\
1057
+ ${httpMethod} '__DOMAIN__${path}' \\
1058
+ --header 'accept: application/json'`;
1059
+ if (httpMethod !== "get") {
1060
+ const curlParams = bodyParams?.map((ob) => {
1061
+ return ` "${ob.name}": ""`;
1062
+ });
1063
+ snippetShell += ` \\
1064
+ --data-raw '{ ${curlParams}}'`;
1065
+ }
1066
+ const descriptionText = description ? `
1067
+ /**
1068
+ * ${description.replace(/\n/g, "\n * ")}
1069
+ */` : "";
1070
+ const resolvedResponseClass = responseClass || "unknown";
1071
+ const responseType = resolvedResponseClass !== "unknown" ? `${resolvedResponseClass}` : "unknown";
1072
+ const methodImpl = `
1073
+ ${descriptionText}
1074
+ async function ${classMethod}(${methodParams}): Promise<${responseType}> {
1075
+ const $ = new ${classGenName}(Network.create(requestConfig), namespace, cache)
1076
+ const resp = await $.${classMethod}(${methodParamsNoTypes})
1077
+ if (resp.error) throw resp.error
1078
+ return resp.response.data
1079
+ }
1080
+ `;
1081
+ const snippetPromiseString = responseType !== "unknown" ? `Promise<${responseType}>` : "Promise";
1082
+ snippetSdk += `${classMethod}(${methodParams})
1083
+ // return ${snippetPromiseString}`;
1084
+ return [methodImpl, snippetSdk, snippetShell];
1085
+ };
1086
+
1087
+ const templateApiIndex = (serviceName, serviceNameTitle, apiList) => {
1088
+ let imports = "";
1089
+ let returnStatement = "";
1090
+ for (const cl of apiList) {
1091
+ imports += `
1092
+ import { ${cl} } from './${serviceName}/${cl}.js'`;
1093
+ returnStatement += `
1094
+ ${cl}, `;
1095
+ }
1096
+ return `/**
1097
+ * AUTO GENERATED
1098
+ */
1099
+ ${imports}
1100
+
1101
+ const apis = {
1102
+ ${returnStatement}
1103
+ }
1104
+
1105
+ export const ${ParserUtils.convertDashesToTitleCase(serviceNameTitle)} = apis
1106
+ `;
1107
+ };
1108
+
1109
+ const templateSdkSnippet = (serviceNameTitle, apiName, methodSnippet) => {
1110
+ const methodArr = methodSnippet.split("//");
1111
+ let normMethod = normalizeMethodSnippet(methodArr[0].trim(), "data:");
1112
+ normMethod = normalizeMethodSnippet(normMethod, "queryParams:");
1113
+ normMethod = normalizeMethodSnippet(normMethod, "queryParams?:");
1114
+ normMethod += "\n\n//" + methodArr[1];
1115
+ const sdkSnippet = `import { Accelbyte } from '@accelbyte/sdk'
1116
+ import { ${serviceNameTitle} } from '@accelbyte/sdk-${serviceNameTitle.toLowerCase()}'
1117
+
1118
+ const sdk = Accelbyte.SDK({
1119
+ baseURL: 'https://demo.accelbyte.io',
1120
+ clientId: '77f88506b6174c3ea4d925f5b4096ce8',
1121
+ namespace: 'accelbyte',
1122
+ redirectURI: 'http://localhost:3030'
1123
+ })
1124
+
1125
+ ${serviceNameTitle}.${apiName}(sdk)
1126
+ .${normMethod}`;
1127
+ return sdkSnippet;
1128
+ };
1129
+ const normalizeMethodSnippet = (methodInput, splitWord) => {
1130
+ const split1 = methodInput.split(splitWord);
1131
+ if (!split1[1]) {
1132
+ return methodInput;
1133
+ }
1134
+ let split2 = split1[1].trim();
1135
+ split2 = ParserUtils.replaceAll(split2, "{", "");
1136
+ split2 = ParserUtils.replaceAll(split2, "})", "");
1137
+ split2 = split2.split(",");
1138
+ let params = "";
1139
+ split2.forEach((p) => {
1140
+ params += "\n " + ParserUtils.replaceAll(p.trim(), ")", "") + ",";
1141
+ });
1142
+ params = params.slice(0, -1);
1143
+ const result = split1[0] + splitWord + " {" + params + "\n })";
1144
+ return result;
1145
+ };
1146
+
1147
+ const GIT_URL = "https://github.com/AccelByte/accelbyte-web-sdk/blob/main/packages";
953
1148
  class CodeGenerator {
954
1149
  static getPatchedDir = () => path__default.join(CliParser.getSwaggersOutputPath(), "patched");
955
1150
  static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
956
1151
  static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
957
1152
  static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
958
1153
  static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
959
- static iterateApi = async (api) => {
1154
+ static iterateApi = async (api, serviceName) => {
960
1155
  const apiBufferByTag = {};
1156
+ const apiArgumentsByTag = {};
1157
+ const classBufferByTag = {};
961
1158
  const dependenciesByTag = {};
962
1159
  const classImports = {};
963
1160
  let arrayDefinitions = [];
@@ -972,7 +1169,6 @@ class CodeGenerator {
972
1169
  }));
973
1170
  const sortedKeys = Array.from(sortedPathsByLength.keys());
974
1171
  const servicePrefix = CodeGenerator.getServicePrefix(sortedKeys);
975
- console.log("ServicePrefix", servicePrefix, ", Paths:", sortedKeys);
976
1172
  for (const [path2, operation] of sortedPathsByLength) {
977
1173
  const isAdminEndpoint = path2.indexOf("/admin/") > -1;
978
1174
  if (CliParser.isAdmin() && !isAdminEndpoint) {
@@ -1004,13 +1200,15 @@ class CodeGenerator {
1004
1200
  });
1005
1201
  mapClassMethods[tag][classMethod] = `${path2} ${httpMethod}`;
1006
1202
  snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
1007
- const description = endpoint.description;
1203
+ let description = endpoint.description;
1204
+ description = description || "";
1205
+ description = description.replace(/\s+/g, " ");
1008
1206
  const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
1009
1207
  const { className, classGenName } = ParserUtils.generateClassName(tag);
1010
1208
  classImports[className] = classImports[className] ? classImports[className] : {};
1011
1209
  if (responseClass) {
1012
1210
  const importTypeClass = ParserUtils.parseRefType(responseClass);
1013
- classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
1211
+ classImports[className][importTypeClass] = `import { ${importTypeClass} } from '../definitions/${importTypeClass}.js'`;
1014
1212
  }
1015
1213
  if (responseClass && responseClass.endsWith("Array")) {
1016
1214
  arrayDefinitions.push(responseClass);
@@ -1029,7 +1227,7 @@ class CodeGenerator {
1029
1227
  }
1030
1228
  ];
1031
1229
  }
1032
- const [generatedMethodString, importStatements, snippetString, snippetCurl] = templateMethod({
1230
+ const { methodImpl, methodParams, methodParamsNoTypes, importStatements } = templateMethod({
1033
1231
  classMethod,
1034
1232
  description,
1035
1233
  httpMethod,
@@ -1040,16 +1238,42 @@ class CodeGenerator {
1040
1238
  isFormUrlEncoded,
1041
1239
  responseClass
1042
1240
  });
1043
- apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
1241
+ classBufferByTag[tag] = (classBufferByTag[tag] || "") + methodImpl;
1242
+ const [generatedMethodString1, snippetMethod, snippetShell] = templateApiMethod({
1243
+ classMethod,
1244
+ description,
1245
+ httpMethod,
1246
+ path: pathWithBase,
1247
+ pathParams,
1248
+ bodyParams,
1249
+ responseClass,
1250
+ classGenName,
1251
+ methodParams,
1252
+ methodParamsNoTypes
1253
+ });
1254
+ apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString1;
1255
+ apiArgumentsByTag[tag] = (apiArgumentsByTag[tag] || "") + classMethod + ",";
1044
1256
  dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
1257
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1258
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1259
+ const resultSnippet = templateSdkSnippet(serviceNameTitle, apiGenName, snippetMethod);
1045
1260
  snippetMap[path2][httpMethod] = {
1046
- 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>`,
1047
- shell: snippetCurl
1261
+ web: resultSnippet,
1262
+ webGit: GIT_URL + `/sdk-${serviceName}/src/generated-public/${serviceName}/${apiGenName}.ts`,
1263
+ shell: snippetShell
1048
1264
  };
1049
1265
  }
1050
1266
  }
1051
1267
  arrayDefinitions = [...new Set(arrayDefinitions)];
1052
- return { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap };
1268
+ return {
1269
+ apiArgumentsByTag,
1270
+ apiBufferByTag,
1271
+ classBufferByTag,
1272
+ dependenciesByTag,
1273
+ classImports,
1274
+ arrayDefinitions,
1275
+ snippetMap
1276
+ };
1053
1277
  };
1054
1278
  static main = async (nameArray) => {
1055
1279
  const serviceName = nameArray[0];
@@ -1057,6 +1281,7 @@ class CodeGenerator {
1057
1281
  const parser = new SwaggerParser();
1058
1282
  const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
1059
1283
  const DIST_DIR = `${generatedFolder}/${serviceName}`;
1284
+ const DIST_ENDPOINTS_DIR = path__default.join(DIST_DIR, "endpoints");
1060
1285
  const DIST_DEFINITION_DIR = path__default.join(DIST_DIR, "definitions");
1061
1286
  const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
1062
1287
  const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
@@ -1067,19 +1292,32 @@ class CodeGenerator {
1067
1292
  console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
1068
1293
  ParserUtils.mkdirIfNotExist(DIST_DIR);
1069
1294
  ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
1070
- ParserUtils.writeVersionFile(DIST_DIR, "Version.ts", serviceName, api.info, BUILD_DATE);
1071
- const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api);
1295
+ ParserUtils.mkdirIfNotExist(DIST_ENDPOINTS_DIR);
1296
+ ParserUtils.syncPackageVersion(api.info, CliParser.isAdmin());
1297
+ const { apiArgumentsByTag, apiBufferByTag, classBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api, serviceName);
1072
1298
  if (CliParser.getSnippetOutputPath()) {
1073
1299
  ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
1074
1300
  }
1075
1301
  const targetSrcFolder = `${CliParser.getOutputPath()}/`;
1076
- for (const tag in apiBufferByTag) {
1302
+ const apiList = [];
1303
+ for (const tag in classBufferByTag) {
1077
1304
  const { className, classGenName } = ParserUtils.generateClassName(tag);
1078
- const apiBuffer = apiBufferByTag[tag];
1305
+ const classBuffer = classBufferByTag[tag];
1079
1306
  const imports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
1080
- ParserUtils.writeClassFile(DIST_DIR, classGenName, apiBuffer, imports);
1081
- indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${classGenName}`), targetSrcFolder));
1307
+ const apiImports = [.../* @__PURE__ */ new Set([...dependenciesByTag[tag], ...Object.values(classImports[className])])];
1308
+ apiImports.push(`import { ${classGenName} } from './endpoints/${classGenName}.js'`);
1309
+ ParserUtils.writeClassFile(DIST_ENDPOINTS_DIR, classGenName, classBuffer, imports);
1310
+ const apiBuffer = apiBufferByTag[tag];
1311
+ const { apiGenName } = ParserUtils.generateApiName(tag);
1312
+ ParserUtils.writeApiFile(DIST_DIR, apiGenName, apiBuffer, apiImports, apiArgumentsByTag[tag]);
1313
+ apiList.push(apiGenName);
1314
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_ENDPOINTS_DIR, `${classGenName}`), targetSrcFolder));
1315
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DIR, `${apiGenName}`), targetSrcFolder));
1082
1316
  }
1317
+ const serviceNameTitle = ParserUtils.convertDashesToTitleCase(serviceName);
1318
+ const apiIndexBuff = templateApiIndex(serviceName, serviceNameTitle, apiList);
1319
+ ParserUtils.writeApiMainFile(generatedFolder, serviceNameTitle, apiIndexBuff);
1320
+ indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(generatedFolder, serviceNameTitle), targetSrcFolder));
1083
1321
  const duplicates = /* @__PURE__ */ new Map();
1084
1322
  const definitions = api?.components?.schemas || api.definitions;
1085
1323
  for (const ref in definitions) {
@@ -1124,6 +1362,22 @@ class SwaggerDownloader {
1124
1362
  fs.writeFileSync(destFile, "");
1125
1363
  return destFile;
1126
1364
  };
1365
+ static postSanitizeDownloadedFile = (filePath) => {
1366
+ const searchStr = ["%5B", "%5D", "%20", "%7B", "%7D"];
1367
+ fs.readFile(filePath, "utf8", (err, data) => {
1368
+ if (err)
1369
+ throw err;
1370
+ let result = data;
1371
+ searchStr.forEach((s) => {
1372
+ result = result.replace(new RegExp(s, "g"), " ");
1373
+ });
1374
+ fs.writeFile(filePath, result, "utf8", (err2) => {
1375
+ if (err2)
1376
+ throw err2;
1377
+ console.log("File updated successfully.");
1378
+ });
1379
+ });
1380
+ };
1127
1381
  static downloadFile = (targetFileName, url) => {
1128
1382
  const destFile = SwaggerDownloader.getDestFile(targetFileName);
1129
1383
  const file = fs.createWriteStream(destFile);
@@ -1131,6 +1385,7 @@ class SwaggerDownloader {
1131
1385
  response.pipe(file);
1132
1386
  file.on("finish", () => {
1133
1387
  file.close();
1388
+ SwaggerDownloader.postSanitizeDownloadedFile(destFile);
1134
1389
  console.log(`SwaggerDownload ${url} completed`);
1135
1390
  });
1136
1391
  });