@accelbyte/codegen 1.0.0-beta.3 → 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.
@@ -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 = () => ({
@@ -97,7 +100,18 @@ ${body}
97
100
  \`\`\`
98
101
  `.replace(/, \)/g, ")").trim();
99
102
 
100
- const SERVICE_PREFIXES = ["/iam/", "/gdpr/", "/sessionbrowser/", "/event/", "/admin/", "/public/"];
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
+ ];
101
115
  class ParserUtils {
102
116
  static generateClassName = (tag) => {
103
117
  const className = _.upperFirst(_.camelCase(tag));
@@ -244,30 +258,34 @@ class ParserUtils {
244
258
  }
245
259
  return parameters.filter((parameter) => parameter.in === "path");
246
260
  }
247
- static mappedMethod = (httpMethod, isForm) => {
248
- if (httpMethod === "get") {
249
- return "fetch";
250
- } else if (httpMethod === "post" && isForm) {
251
- return "post";
252
- } else if (httpMethod === "post") {
253
- return "create";
254
- } else if (httpMethod === "put") {
255
- return "update";
256
- } else if (httpMethod === "patch") {
257
- return "patch";
258
- } else if (httpMethod === "delete") {
259
- return "delete";
260
- }
261
- };
262
- static generateHumanReadableMethod({ path: path2, httpMethod, isForm }) {
263
- let path_ = path2.replace();
264
- SERVICE_PREFIXES.forEach((prefix) => {
261
+ static generateNaturalLangMethod = ({ servicePrefix, path: path2, httpMethod, isForm, existingMethods }) => {
262
+ let path_ = path2;
263
+ path_ = path_.replace(`/${servicePrefix}/`, "/");
264
+ REMOVED_KEYWORDS.forEach((prefix) => {
265
265
  path_ = path_.replace(prefix, "/");
266
266
  });
267
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$/, "");
275
+ }
276
+ }
268
277
  const arrLastWords = path_.split("}/");
269
278
  let lastWords = arrLastWords[arrLastWords.length - 1];
270
- lastWords = lastWords.split("/{")[0];
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);
288
+ }
271
289
  const listBeforeLastWords = [];
272
290
  let foundParam = false;
273
291
  const listByParams = [];
@@ -277,7 +295,7 @@ class ParserUtils {
277
295
  foundParam = true;
278
296
  let param = item.replace("{", "");
279
297
  param = param.replace("}", "");
280
- param = "By" + _.upperFirst(param);
298
+ param = "Byword" + _.upperFirst(param);
281
299
  listByParams.push(param);
282
300
  } else if (!foundParam) {
283
301
  if (lastWords.indexOf(item) === -1) {
@@ -287,39 +305,12 @@ class ParserUtils {
287
305
  foundParam = false;
288
306
  }
289
307
  });
290
- const genPath = _.upperFirst(lastWords) + "/" + listBeforeLastWords.join("/") + "/" + listByParams.join("/");
291
- let res = _.camelCase(ParserUtils.mappedMethod(httpMethod, isForm) + genPath);
292
- res = res.replace("ByNamespace", "");
293
- return res;
294
- }
295
- static generateClassMethod({
296
- path: path2,
297
- endpoint,
298
- httpMethod,
299
- className
300
- }) {
301
- let replacedIdsPath = path2;
302
- if (endpoint.parameters) {
303
- for (const parameter of endpoint.parameters) {
304
- if (parameter.in === "path") {
305
- replacedIdsPath = replacedIdsPath.replace("{" + parameter.name + "}", "By" + ParserUtils.toTitleCaseWord(parameter.name));
306
- replacedIdsPath = replacedIdsPath.replace("/iam", "");
307
- replacedIdsPath = replacedIdsPath.replace("/odin-config", "");
308
- }
309
- }
310
- }
311
- let classMethod = httpMethod + "/" + replacedIdsPath;
312
- classMethod = _.camelCase(classMethod);
313
- classMethod = classMethod.replace("PublicNamespacesByNamespace", "Ns");
314
- classMethod = classMethod.replace("AdminNamespacesByNamespace", "AdminNs");
315
- const searchWord = "NamespacesByNamespace";
316
- const nsExistInsideMethod = classMethod.indexOf(searchWord) > 0 && classMethod.indexOf(searchWord) + searchWord.length < classMethod.length;
317
- const excludedClasses = ["Policies"];
318
- if (nsExistInsideMethod && !excludedClasses.includes(className)) {
319
- classMethod = classMethod.replace(searchWord, "");
320
- }
321
- return classMethod;
322
- }
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
+ };
323
314
  static filterBodyParams(parameters) {
324
315
  if (Array.isArray(parameters) && parameters.length > 0) {
325
316
  return parameters.filter((parameter) => parameter.in === "body" || parameter.in === "formData");
@@ -439,6 +430,70 @@ class ParserUtils {
439
430
  ${content}`;
440
431
  };
441
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
+ };
442
497
 
443
498
  const Schema = z.object({
444
499
  $ref: z.string().nullish(),
@@ -881,21 +936,32 @@ class CodeGenerator {
881
936
  static getPatchedDir = () => path__default.join(CliParser.getSwaggersOutputPath(), "patched");
882
937
  static getGeneratedPublicFolder = () => `${CliParser.getOutputPath()}/generated-public`;
883
938
  static getGeneratedAdminFolder = () => `${CliParser.getOutputPath()}/generated-admin`;
884
- static getGeneratedSnippetsFolder = () => `${CliParser.getOutputPath()}/../../../../apps/api-explorer-app/src/generated-snippets`;
939
+ static getGeneratedSnippetsFolder = () => `${CliParser.getSnippetOutputPath()}/generated-snippets`;
940
+ static getServicePrefix = (servicePaths) => servicePaths[servicePaths.length - 1].split("/")[1];
885
941
  static iterateApi = async (api) => {
886
942
  const apiBufferByTag = {};
887
943
  const dependenciesByTag = {};
888
944
  const classImports = {};
889
945
  let arrayDefinitions = [];
890
946
  const snippetMap = {};
891
- for (const path2 in api.paths) {
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) {
892
959
  const isAdminEndpoint = path2.indexOf("/admin/") > -1;
893
960
  if (CliParser.isAdmin() && !isAdminEndpoint) {
894
961
  continue;
895
962
  } else if (!CliParser.isAdmin() && isAdminEndpoint) {
896
963
  continue;
897
964
  }
898
- const operation = api.paths[path2];
899
965
  const httpMethods = Object.keys(operation);
900
966
  for (const httpMethod of httpMethods) {
901
967
  const endpoint = await Endpoint.parseAsync(operation[httpMethod]).catch((error) => {
@@ -905,55 +971,63 @@ class CodeGenerator {
905
971
  if (!endpoint.tags) {
906
972
  continue;
907
973
  }
974
+ if (endpoint.deprecated) {
975
+ continue;
976
+ }
908
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] : {};
909
989
  const description = endpoint.description;
910
- const isDeprecated = endpoint.deprecated;
911
990
  const responseClass = ParserUtils.get2xxResponse(endpoint.responses);
912
991
  const { className, classGenName } = ParserUtils.generateClassName(tag);
913
992
  classImports[className] = classImports[className] ? classImports[className] : {};
914
- if (!isDeprecated) {
915
- snippetMap[path2] = snippetMap[path2] ? snippetMap[path2] : {};
916
- if (responseClass) {
917
- const importTypeClass = ParserUtils.parseRefType(responseClass);
918
- classImports[className][importTypeClass] = `import { ${importTypeClass} } from './definitions/${importTypeClass}'`;
919
- }
920
- if (responseClass && responseClass.endsWith("Array")) {
921
- arrayDefinitions.push(responseClass);
922
- }
923
- const isFormUrlEncoded = ParserUtils.isFormUrlEncoded(httpMethod, endpoint.consumes);
924
- const queryParams = ParserUtils.filterQueryParameters(endpoint.parameters);
925
- const pathParams = ParserUtils.filterPathParams(endpoint.parameters);
926
- let bodyParams = ParserUtils.filterBodyParams(endpoint.parameters);
927
- const isForm = endpoint.consumes && endpoint.consumes[0] === "application/x-www-form-urlencoded";
928
- const classMethod = ParserUtils.generateHumanReadableMethod({ path: path2, httpMethod, isForm });
929
- if (endpoint.requestBody) {
930
- bodyParams = [
931
- {
932
- name: "body",
933
- in: "body",
934
- schema: endpoint.requestBody.content["application/json"].schema
935
- }
936
- ];
937
- }
938
- const pathWithBase = `${api.basePath ?? ""}${path2}`;
939
- const [generatedMethodString, importStatements, snippetString, snippetCurl] = templateMethod({
940
- classMethod,
941
- description,
942
- httpMethod,
943
- path: pathWithBase,
944
- pathParams,
945
- bodyParams,
946
- queryParams,
947
- isFormUrlEncoded,
948
- responseClass
949
- });
950
- apiBufferByTag[tag] = (apiBufferByTag[tag] || "") + generatedMethodString;
951
- dependenciesByTag[tag] = dependenciesByTag[tag] ? [.../* @__PURE__ */ new Set([...importStatements, ...dependenciesByTag[tag]])] : [...new Set(importStatements)];
952
- snippetMap[path2][httpMethod] = {
953
- 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>`,
954
- shell: snippetCurl
955
- };
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);
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
+ ];
956
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
+ };
957
1031
  }
958
1032
  }
959
1033
  arrayDefinitions = [...new Set(arrayDefinitions)];
@@ -965,7 +1039,6 @@ class CodeGenerator {
965
1039
  const parser = new SwaggerParser();
966
1040
  const generatedFolder = CliParser.isAdmin() ? CodeGenerator.getGeneratedAdminFolder() : CodeGenerator.getGeneratedPublicFolder();
967
1041
  const DIST_DIR = `${generatedFolder}/${serviceName}`;
968
- const DIST_DOCS_DIR = path__default.join(DIST_DIR, "docs");
969
1042
  const DIST_DEFINITION_DIR = path__default.join(DIST_DIR, "definitions");
970
1043
  const swaggerFilePath = `${CliParser.getSwaggersOutputPath()}/${swaggerFile}`;
971
1044
  const swaggerPatchedFilePath = `${CodeGenerator.getPatchedDir()}/${swaggerFile}`;
@@ -973,13 +1046,12 @@ class CodeGenerator {
973
1046
  ParserUtils.applyPatchIfExists(swaggerFilePath, swaggerPatchFilePath, swaggerPatchedFilePath, CodeGenerator.getPatchedDir());
974
1047
  const api = await parser.parse(swaggerPatchedFilePath);
975
1048
  const indexImportsSet = /* @__PURE__ */ new Set();
976
- console.log("\n----------\n API name: %s, Version: %s schemes %s", api, api.info.title, api.info.version, api.schemes);
1049
+ console.log("----------\nGenerating API:", { title: api.info.title, version: api.info.version });
977
1050
  ParserUtils.mkdirIfNotExist(DIST_DIR);
978
- ParserUtils.mkdirIfNotExist(DIST_DOCS_DIR);
979
1051
  ParserUtils.mkdirIfNotExist(DIST_DEFINITION_DIR);
980
1052
  ParserUtils.writeVersionFile(DIST_DIR, "Version.ts", serviceName, api.info, BUILD_DATE);
981
1053
  const { apiBufferByTag, dependenciesByTag, classImports, arrayDefinitions, snippetMap } = await CodeGenerator.iterateApi(api);
982
- if (!CliParser.isAdmin()) {
1054
+ if (CliParser.getSnippetOutputPath()) {
983
1055
  ParserUtils.writeSnippetFile(CodeGenerator.getGeneratedSnippetsFolder(), api.info.title, JSON.stringify(snippetMap, null, 2));
984
1056
  }
985
1057
  const targetSrcFolder = `${CliParser.getOutputPath()}/`;
@@ -1018,7 +1090,7 @@ class CodeGenerator {
1018
1090
  ParserUtils.writeDefinitionFile(DIST_DEFINITION_DIR, arrayClass, buffer);
1019
1091
  indexImportsSet.add(ParserUtils.getRelativePathToWebSdkSrcFolder(path__default.join(DIST_DEFINITION_DIR, arrayClass), targetSrcFolder));
1020
1092
  }
1021
- console.log("\n----------\nCOMPLETED.\n----------\n\n");
1093
+ console.log("\nCOMPLETED\n----------\n\n");
1022
1094
  return indexImportsSet;
1023
1095
  };
1024
1096
  }
@@ -1045,7 +1117,7 @@ class SwaggerDownloader {
1045
1117
  });
1046
1118
  });
1047
1119
  request.on("error", (err) => {
1048
- console.log(`SwaggerDownloader dl failed for "${targetFileName}" and "${url}"`, err);
1120
+ console.log(`SwaggerDownloader failed for "${targetFileName}" and "${url}"`, err);
1049
1121
  });
1050
1122
  };
1051
1123
  static main = () => {