apn-app-manager 1.1.0 → 1.2.0-beta.2

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.
Files changed (61) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/actions/cloneAction.js +83 -47
  4. package/src/cons.js +3 -2
  5. package/src/handlers/objectExtractHandler.js +42 -10
  6. package/src/handlers/objectMapsUpdateHandler.js +85 -21
  7. package/src/handlers/objectModifyHandler.js +23 -9
  8. package/src/objectProps.js +8 -7
  9. package/src/util.js +12 -29
  10. package/APPS.properties +0 -45
  11. package/APPS.zip +0 -0
  12. package/AS GSS Full Application.zip +0 -0
  13. package/AS IO Full Application.zip +0 -0
  14. package/AS MSG Messaging.zip +0 -0
  15. package/CT APP Clone Tester.zip +0 -0
  16. package/NEW - CT APP Clone Tester.zip +0 -0
  17. package/lib/aim/import-manager_template.properties +0 -50
  18. package/lib/aim/lib/log4j.properties +0 -17
  19. package/lib/aim/metrics.properties +0 -10
  20. package/out/NEW Clone Tester.zip +0 -0
  21. package/out/namespaces.json +0 -8
  22. package/out/not-cloned-uuids.json +0 -6
  23. package/out/objects.json +0 -1090
  24. package/out/temp/META-INF/MANIFEST.MF +0 -4
  25. package/out/temp/META-INF/export.log +0 -925
  26. package/out/temp/META-INF/plugins.txt +0 -4
  27. package/out/temp/application/_a-0000e88d-6440-8000-9bf2-011c48011c48_855378-new.xml +0 -373
  28. package/out/temp/connectedSystem/_a-0000e88d-6440-8000-9bf2-011c48011c48_864509-new.xml +0 -63
  29. package/out/temp/content/1d6e665c-14b4-4bb9-88fb-ae64e1eddbf3-new.xml +0 -75
  30. package/out/temp/content/6b262b50-0d7a-4dc9-a31b-473fd11f347a-new.xml +0 -100
  31. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_855525-new.xml +0 -181
  32. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863563-new/file.png +0 -0
  33. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863563-new.xml +0 -51
  34. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863564-new.xml +0 -57
  35. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863573-new.xml +0 -50
  36. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863584-new.xml +0 -57
  37. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863603-new/file.properties +0 -1
  38. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863603-new.xml +0 -51
  39. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863609-new.xml +0 -57
  40. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_863616-new.xml +0 -55
  41. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_864578-new.xml +0 -162
  42. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_874621-new.xml +0 -55
  43. package/out/temp/content/_a-0000e88d-6440-8000-9bf2-011c48011c48_879069.xml +0 -51
  44. package/out/temp/content/b639666e-eb53-4a1a-b963-c2a21ca400f7-new.xml +0 -89
  45. package/out/temp/content/c1448a43-e704-42c3-8588-2180b74a138a-new.xml +0 -255
  46. package/out/temp/content/fcb9b72f-10dd-4744-8870-a44a7fcb4aeb-new.xml +0 -77
  47. package/out/temp/dataStore/_a-0000e88d-6440-8000-9bf2-011c48011c48_855385-new.xml +0 -57
  48. package/out/temp/datatype/%7Burn%3Acom%3Aappian%3Atypes%3ANEW%7DNEW_TEST_TABLE.xsd +0 -48
  49. package/out/temp/datatype/%7Burn%3Acom%3Aappian%3Atypes%3ANEW%7DNEW_TEST_TABLE_REFERENCE.xsd +0 -31
  50. package/out/temp/group/_e-0000e88d-6440-8000-9b3f-01075c01075c_805-new.xml +0 -29
  51. package/out/temp/group/_e-0000e88d-6440-8000-9b3f-01075c01075c_807-new.xml +0 -29
  52. package/out/temp/groupType/_f-0000e88d-6440-8000-9b3f-01075c01075c_1-new.xml +0 -13
  53. package/out/temp/processModel/0002e8ba-9f2b-8000-1196-7f0000014e7a-new.xml +0 -1964
  54. package/out/temp/processModelFolder/_g-0000e88d-6441-8000-10c5-7f0000014e7a_197-new.xml +0 -46
  55. package/out/temp/recordType/00262392-ae09-4941-8b80-d46df28c734f-new.xml +0 -325
  56. package/out/temp/recordType/0c9e7b5a-c28d-496b-a7c1-8ad7227e513a-new.xml +0 -155
  57. package/out/temp/recordType/cbbc5029-b0c1-43a2-9430-24fa704c512c-new.xml +0 -223
  58. package/out/temp/site/15c1e536-fd29-4412-879d-219b02bd1c59-new.xml +0 -47
  59. package/out/temp/tempoReport/eb755f20-5148-4f8f-92cc-fd69980d6082-new.xml +0 -27
  60. package/out/temp/webApi/3a013f66-bd81-414a-a72e-bbd03c8befe9-new.xml +0 -62
  61. package/resources/force_update_.properties +0 -1
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  `apn-app-manager` is a command line tool for managing Appian applications.
4
4
 
5
+ Test beta version
6
+
5
7
  ## Why?
6
8
 
7
9
  When app building you may want to do things in bulk that are easiest to do via an application's exported XML files and a re-import. This tool helps to automate some of those tasks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apn-app-manager",
3
- "version": "1.1.0",
3
+ "version": "1.2.0-beta.2",
4
4
  "description": "Appian App Manager",
5
5
  "license": "Apache-2.0",
6
6
  "preferGlobal": true,
@@ -2,6 +2,7 @@ module.exports.main = main;
2
2
 
3
3
  // Dependency imports
4
4
  const path = require("path");
5
+ const lodash = require("lodash");
5
6
 
6
7
  // Custom imports
7
8
  const util = require("../util");
@@ -14,62 +15,106 @@ const obMapsUpdates = require("../handlers/objectMapsUpdateHandler");
14
15
  const obModify = require("../handlers/objectModifyHandler");
15
16
 
16
17
  // Entry-point
17
- function main(curNamespace, newNamespace, callback) {
18
+ function main(callback) {
18
19
  util.delFile(cons.outDir);
19
20
  fileSelect.getAppPackage(function (appZipPath) {
20
21
  util.unzipDir(appZipPath, cons.tempDir);
21
22
  fileSelect.selectSingleFile("Select import customization properties file", ["properties"], true, function (icfPath) {
22
- promptNamespace("What is your current namespace?", function (curNamespace) {
23
- promptNamespace("What is your new namespace?", function (newNamespace) {
24
- executeClone(curNamespace, newNamespace, appZipPath, icfPath, callback);
25
- });
26
- });
23
+ executeClone(appZipPath, icfPath, callback);
27
24
  });
28
25
  });
29
26
  }
30
27
 
31
28
  // Executes all the steps of the clone
32
- function executeClone(curNamespace, newNamespace, appZipPath, icfPath, callback) {
33
- const appName = path.basename(appZipPath, ".zip");
34
- util.print(`\nCloning '${appName}' to namespace '${newNamespace}'`);
35
-
29
+ function executeClone(appZipPath, icfPath, callback) {
36
30
  // Initialize variables based on the new namespace
37
- const outZipPath = path.resolve(cons.outDir, `${newNamespace} - ${appName}.zip`);
31
+ const outZipPath = path.resolve(cons.outDir, path.basename(appZipPath));
38
32
  const outIcfPath = icfPath ? path.resolve(cons.outDir, path.basename(icfPath)) : null;
39
33
 
40
- // Extract the name, uuid, etc. from each object file
34
+ // Extract the metadata including the namespace from each object file
41
35
  const objectMaps = {};
42
36
  const uuidCollector = { uuids: [] };
43
- obExtract.buildObjectMaps(objectMaps, uuidCollector, cons.tempDir, obProps.usageTypes.CLONE);
44
-
45
- // Update the object maps we are cloning based on the application prefix
46
- obMapsUpdates.renameObjectMaps(objectMaps, uuidCollector, curNamespace, newNamespace, obProps.usageTypes.CLONE);
47
-
48
- // These output files are helpful for debugging
49
- util.writeJson(path.resolve(cons.outDir, `objects.json`), objectMaps);
50
- util.writeJson(path.resolve(cons.outDir, `not-cloned-uuids.json`), uuidCollector);
51
-
52
- // Modify the underlying object XML files
53
- obModify.modifyObjectFiles(cons.tempDir, objectMaps, obProps.usageTypes.CLONE, []);
54
-
55
- // Modify the ICF file
56
- obModify.modifySingleFile(icfPath, outIcfPath, objectMaps, obProps.usageTypes.CLONE, []);
37
+ const namespaceCollector = {};
38
+ util.print(`Collecting all metadata from the objects...`);
39
+ obExtract.buildObjectMaps(objectMaps, uuidCollector, namespaceCollector, cons.tempDir, obProps.usageTypes.CLONE);
57
40
 
58
- // Print warnings and output
59
- if (uuidCollector["uuids"].length > 0) {
41
+ // Have the user map each namespace to its proper behavior
42
+ mapNamespaces(namespaceCollector, function onComplete() {
60
43
  util.printGap();
61
- util.printWarning(
62
- `WARNING: ${uuidCollector["uuids"].length} uuids were found in your zip but not parsed via cloning (see output file 'not-cloned-uuids.json'). Check the count of missing precedents from your app package in your source environment. If it is ${uuidCollector["uuids"].length} objects, then those are likely just the missing precedents, otherwise please reach out to the authors of this tool, since the XML structure of Appian objects has probably changed and this tool needs to be updated.`
63
- );
64
- }
65
- util.print(`\nCloning of '${appName}' to namespace '${newNamespace}' complete`);
44
+ // Update the object maps we are cloning based on the application prefix
45
+ util.print(`Cloning the objects files...`);
46
+ obMapsUpdates.renameObjectMaps(objectMaps, uuidCollector, namespaceCollector, obProps.usageTypes.CLONE);
47
+
48
+ // These output files are helpful for debugging
49
+ util.writeJson(path.resolve(cons.outDir, `objects.json`), objectMaps);
50
+ util.writeJson(path.resolve(cons.outDir, `not-cloned-uuids.json`), uuidCollector);
51
+ util.writeJson(path.resolve(cons.outDir, `namespaces.json`), namespaceCollector);
52
+
53
+ // Modify the underlying object XML files & zip the package
54
+ obModify.modifyObjectFiles(cons.tempDir, objectMaps, obProps.usageTypes.CLONE, []);
55
+ util.zipDir(cons.tempDir, outZipPath, false);
56
+ obModify.renameSingleFile(outZipPath, objectMaps, obProps.usageTypes.CLONE, []);
57
+
58
+ // Modify the ICF file
59
+ obModify.modifySingleFile(icfPath, outIcfPath, objectMaps, obProps.usageTypes.CLONE, []);
60
+
61
+ // Print warnings and output
62
+ if (uuidCollector["uuids"].length > 0) {
63
+ util.printGap();
64
+ util.printWarning(
65
+ `WARNING: ${uuidCollector["uuids"].length} uuids were found in your zip but not parsed via cloning (see output file 'not-cloned-uuids.json'). Check the count of missing precedents from your app package in your source environment. If it is ${uuidCollector["uuids"].length} objects, then those are likely just the missing precedents, otherwise please reach out to the authors of this tool, since the XML structure of Appian objects has probably changed and this tool needs to be updated.`
66
+ );
67
+ }
68
+ util.printGap();
69
+ util.print(`Cloning complete. Output can be found in the '/out/' folder (${path.resolve(cons.outDir)}).`);
66
70
 
67
- // Zip the package and log the output path
68
- util.zipDir(cons.tempDir, outZipPath, false);
69
- util.print(`Output can be found in the '/out/' folder (${path.resolve(cons.outDir)})`);
71
+ // Execute callback if passed
72
+ util.execCallback(callback, outZipPath);
73
+ });
74
+ }
70
75
 
71
- // Execute callback if passed
72
- util.execCallback(callback, outZipPath);
76
+ // Prompts the user to select a namespace
77
+ function mapNamespaces(namespaceCollector, onComplete) {
78
+ let namespacesToChooseFrom = lodash.filter(obExtract.sortNamespaces(namespaceCollector), function (namespace) {
79
+ return namespaceCollector[namespace]["newNamespace"] == null;
80
+ });
81
+ let isFirstIteration = namespacesToChooseFrom.length == Object.keys(namespaceCollector).length;
82
+ if (namespacesToChooseFrom.length > 0) {
83
+ let message;
84
+ if (isFirstIteration) {
85
+ message = `After analyzing the objects, ${
86
+ Object.keys(namespaceCollector).length
87
+ } potential unique namespaces were found. For each namespace, select what behavior you would like (clone or keep as-is). There may be overlap with sub-prefixes, in which case selecting a shorter namespace that matches ones with more prefixes will result in that behavior applying to all longer matching namespaces. For objects without a namespace, a prefix will automatically be added if they will be cloned. All namespaces with more than 1 prefix are represented with '_' delimiters, although the proper delimiter will automatically be determined by the object type.`;
88
+ } else {
89
+ message = `Select another namespace`;
90
+ }
91
+ prompt.promptList(message, namespacesToChooseFrom, function (answer) {
92
+ let chosenNamespace = answer;
93
+ promptNamespace(
94
+ `Namespace: ${chosenNamespace}. Enter a new namespace to clone these objects. Enter nothing to keep them as-is.`,
95
+ function (answer) {
96
+ Object.keys(namespaceCollector).forEach(namespace => {
97
+ if (namespaceCollector[namespace]["newNamespace"] == null) {
98
+ if (namespace.startsWith(chosenNamespace)) {
99
+ if (namespace == chosenNamespace) {
100
+ namespaceCollector[namespace]["newNamespace"] = answer;
101
+ } else {
102
+ delete namespaceCollector[namespace];
103
+ }
104
+ }
105
+ }
106
+ });
107
+ mapNamespaces(namespaceCollector, onComplete);
108
+ }
109
+ );
110
+ });
111
+ } else {
112
+ util.print(`All namespaces accounted for. See details below.`);
113
+ obExtract.sortNamespaces(namespaceCollector).forEach(namespace => {
114
+ util.print(` ${namespace} -> ${util.replaceBlank(namespaceCollector[namespace]["newNamespace"], `Will not clone`)}`, true);
115
+ });
116
+ onComplete();
117
+ }
73
118
  }
74
119
 
75
120
  // Prompts the user for a namespace
@@ -79,15 +124,6 @@ function promptNamespace(message, onAnswer) {
79
124
  });
80
125
  }
81
126
 
82
- // Returns the module properties
83
- function getModuleProps() {
84
- if (util.fileExists(cons.modulePropsPath)) {
85
- return util.readJson(cons.modulePropsPath);
86
- } else {
87
- return null;
88
- }
89
- }
90
-
91
127
  // Utility function to differenciate between debug logging and real logging
92
128
  function debug(input) {
93
129
  console.log(input);
package/src/cons.js CHANGED
@@ -33,6 +33,9 @@ module.exports = {
33
33
  // -- rather than an UUID that is a reference, or connected system third-party id, or just referenced in a code block, or commented out
34
34
  uuidRegex: /(?<=>|")[A-Za-z0-9-_]{10,}[0-9][A-Za-z0-9-_]{1,}-[A-Za-z0-9-_]{10,}(?=<|")/g,
35
35
 
36
+ noNamespace: "Objects with no found namespace",
37
+ namespaceDelimiterRegex: "[:|_| |\\.|-]",
38
+
36
39
  xmlEscapedChars: [
37
40
  {
38
41
  from: `&`,
@@ -40,7 +43,5 @@ module.exports = {
40
43
  },
41
44
  ],
42
45
 
43
- namespaceDelimiterRegexString: "([:|_| |\\.|-]?)",
44
-
45
46
  appianUrlRegex: /^https:\/\/.*\/suite/g,
46
47
  };
@@ -6,20 +6,21 @@ const path = require("path");
6
6
  const util = require("../util");
7
7
  const cons = require("../cons");
8
8
  const obProps = require("../objectProps");
9
+ const { split } = require("lodash");
9
10
 
10
11
  // Exports
11
12
  module.exports = {
12
13
  // Builds the objectMaps object from the application
13
- buildObjectMaps: function (objectMaps, uuidCollector, appDir, usageType) {
14
+ buildObjectMaps: function (objectMaps, uuidCollector, namespaceCollector, appDir, usageType) {
14
15
  let fileCount = { count: 0 };
15
16
  initObjectMaps(objectMaps);
16
17
  // Extract the name, uuid, etc. from each object file
17
18
  util.handleDirFiles(appDir, ["xml", "xsd"], 2, 2, function (obPath) {
18
- extractObProps(objectMaps, uuidCollector, fileCount, obPath, usageType);
19
+ extractObProps(objectMaps, uuidCollector, namespaceCollector, fileCount, obPath, usageType);
19
20
  });
20
21
  // Set the datatype UUIDs from their name & namespace
21
22
  updateDatatypeUuids(objectMaps);
22
- util.print(`${retriveObjectMapCount(objectMaps)} of ${fileCount.count} object files mapped`);
23
+ util.print(`${retrieveObjectMapCount(objectMaps)} of ${fileCount.count} object files collected`);
23
24
  },
24
25
 
25
26
  // Sets the datatype UUIDs from their name & namespace
@@ -28,7 +29,17 @@ module.exports = {
28
29
  },
29
30
 
30
31
  // Returns the count of all objects in the objectMap
31
- retriveObjectMapCount: retriveObjectMapCount,
32
+ retrieveObjectMapCount: retrieveObjectMapCount,
33
+
34
+ // Sorts the namespaces by their relative lengths
35
+ sortNamespaces: function (namespaceCollector) {
36
+ return lodash.flatten([
37
+ Object.keys(namespaceCollector).indexOf(cons.noNamespace) >= 0 ? cons.noNamespace : [],
38
+ lodash.sortBy(lodash.difference(Object.keys(namespaceCollector), [cons.noNamespace]).sort(), function (namespace) {
39
+ return namespace.split("_").length;
40
+ }),
41
+ ]);
42
+ },
32
43
  };
33
44
 
34
45
  // ---------------------------------------------------
@@ -43,7 +54,7 @@ function initObjectMaps(objectMaps) {
43
54
  }
44
55
 
45
56
  // Extracts the name, uuid, etc. from a single object file
46
- function extractObProps(objectMaps, uuidCollector, fileCount, obPath, usageType) {
57
+ function extractObProps(objectMaps, uuidCollector, namespaceCollector, fileCount, obPath, usageType) {
47
58
  fileCount.count = fileCount.count + 1;
48
59
  const obType = util.getFolderName(obPath);
49
60
  let isParsed = false;
@@ -56,20 +67,28 @@ function extractObProps(objectMaps, uuidCollector, fileCount, obPath, usageType)
56
67
  // Collect all uuids from the file content
57
68
  uuidCollector["uuids"] = lodash.union(uuidCollector["uuids"], util.regexMatches(fileContent, cons.uuidRegex));
58
69
 
70
+ // Collect all metadata from the object
59
71
  const builder = {};
60
72
  haulType.fields.forEach(haulField => {
61
73
  if (lodash.includes(haulField.usage, usageType)) {
62
74
  const xpath = util.replaceKeys(haulField.xpath, { "{OBTYPE}": obType });
63
75
  let result = util.xpath(xpath, fileContent, haulField.isArray, obProps.xmlNamespaces);
64
76
  if (!util.isBlank(haulField.extractRegex) && !util.isBlank(result)) {
65
- let matches = result.match(haulField.extractRegex);
66
- result = !util.isBlank(matches) ? matches[0] : "";
77
+ result = util.firstMatch(result, haulField.extractRegex, "");
67
78
  }
68
79
  builder[haulField.fieldName] = result;
69
80
  }
70
81
  });
71
- objectMaps[haulType.haulName].push({ current: builder });
72
82
 
83
+ // Get the namespace of the object
84
+ let objectName = builder[obProps.fieldNames.NAME];
85
+ let objectNamespace = extractObjectNamespace(objectName);
86
+ if (Object.keys(namespaceCollector).indexOf(objectNamespace) == -1) {
87
+ namespaceCollector[objectNamespace] = { newNamespace: null };
88
+ }
89
+
90
+ // Set the object metadata in the objectMaps
91
+ objectMaps[haulType.haulName].push({ current: builder });
73
92
  isParsed = true;
74
93
  }
75
94
  });
@@ -85,7 +104,8 @@ function updateDatatypeUuids(objectMaps) {
85
104
  obProps.haulTypes.forEach(haulType => {
86
105
  if (lodash.includes(haulType.obs, obProps.obTypes.DATATYPE)) {
87
106
  objectMaps[haulType.haulName].forEach(function (objectMap, objectMapIndex) {
88
- Object.keys(objectMap).forEach(key => {
107
+ let keysToProcess = lodash.intersection(["current", "new"], Object.keys(objectMap));
108
+ keysToProcess.forEach(key => {
89
109
  const cdtUuid = util.buildCdtUuid(objectMap[key][obProps.fieldNames.CDT_NAMESPACE], objectMap[key][obProps.fieldNames.NAME]);
90
110
  objectMaps[haulType.haulName][objectMapIndex][key][obProps.fieldNames.UUID] = cdtUuid;
91
111
  objectMaps[haulType.haulName][objectMapIndex][key][obProps.fieldNames.CDT_UUID_ENCODED] = util.encodeCdtUuid(cdtUuid);
@@ -96,7 +116,7 @@ function updateDatatypeUuids(objectMaps) {
96
116
  }
97
117
 
98
118
  // Returns the count of all objects in the objectMap
99
- function retriveObjectMapCount(objectMaps) {
119
+ function retrieveObjectMapCount(objectMaps) {
100
120
  let count = 0;
101
121
  Object.keys(objectMaps).forEach(key => {
102
122
  count = count + objectMaps[key].length;
@@ -104,6 +124,18 @@ function retriveObjectMapCount(objectMaps) {
104
124
  return count;
105
125
  }
106
126
 
127
+ // Returns the namespace from an object name
128
+ function extractObjectNamespace(objectName) {
129
+ // See regex here with test cases: https://regexr.com/6trnd
130
+ let namespaceRegex = new RegExp(`^(?:[A-Z0-9]*${cons.namespaceDelimiterRegex}?){0,3}(?=${cons.namespaceDelimiterRegex})`, "g");
131
+ let namespaceMatch = util.firstMatch(objectName, namespaceRegex, null);
132
+ if (namespaceMatch) {
133
+ return namespaceMatch.replace(new RegExp(cons.namespaceDelimiterRegex, "g"), "_");
134
+ } else {
135
+ return cons.noNamespace;
136
+ }
137
+ }
138
+
107
139
  // Utility function to differenciate between debug logging and real logging
108
140
  function debug(input) {
109
141
  console.log(input);
@@ -11,9 +11,9 @@ const { remove } = require("fs-extra");
11
11
  // Exports
12
12
  module.exports = {
13
13
  // Goes through all of the objectMaps and creates new name, uuid, etc.
14
- renameObjectMaps: function (objectMaps, uuidCollector, curNamespace, newNamespace, usageType) {
14
+ renameObjectMaps: function (objectMaps, uuidCollector, namespaceCollector, usageType) {
15
15
  // Update all objectMaps with new names
16
- renameObjectMaps(objectMaps, uuidCollector, curNamespace, newNamespace, usageType);
16
+ renameObjectMaps(objectMaps, uuidCollector, namespaceCollector, usageType);
17
17
  },
18
18
  };
19
19
 
@@ -22,27 +22,41 @@ module.exports = {
22
22
  // ---------------------------------------------------
23
23
 
24
24
  // Goes through all of the objectMaps and creates new name, uuid, etc.
25
- function renameObjectMaps(objectMaps, uuidCollector, curNamespace, newNamespace, usageType) {
25
+ function renameObjectMaps(objectMaps, uuidCollector, namespaceCollector, usageType) {
26
26
  obProps.haulTypes.forEach(haulType => {
27
27
  objectMaps[haulType.haulName].forEach(function (objectMap, objectMapIndex) {
28
- objectMaps[haulType.haulName][objectMapIndex] = { current: objectMap["current"], new: {} };
29
- haulType.fields.forEach(haulField => {
30
- if (lodash.includes(haulField.usage, usageType)) {
31
- let newValue;
32
- let curValue = objectMap["current"][haulField.fieldName];
33
- removeFoundUuids(uuidCollector, haulField, curValue);
34
- if (haulField.isArray) {
35
- let newValueArray = [];
36
- curValue.forEach(curValueIter => {
37
- newValueArray.push(renameObjectProp(curValueIter, haulField, curNamespace, newNamespace));
38
- });
39
- newValue = newValueArray;
40
- } else {
41
- newValue = renameObjectProp(curValue, haulField, curNamespace, newNamespace);
28
+ let curNamespaceForObject = findNamespaceForObject(objectMap["current"][obProps.fieldNames.NAME], namespaceCollector);
29
+ let newNamespaceForObject = namespaceCollector[curNamespaceForObject]["newNamespace"];
30
+ if (util.isBlank(newNamespaceForObject)) {
31
+ // Object will not be cloned
32
+ objectMaps[haulType.haulName][objectMapIndex]["new"] = objectMap["current"];
33
+ haulType.fields.forEach(haulField => {
34
+ if (lodash.includes(haulField.usage, usageType)) {
35
+ let curValue = objectMap["current"][haulField.fieldName];
36
+ removeFoundUuids(uuidCollector, haulField, curValue);
42
37
  }
43
- objectMaps[haulType.haulName][objectMapIndex]["new"][haulField.fieldName] = newValue;
44
- }
45
- });
38
+ });
39
+ } else {
40
+ // Object will be cloned
41
+ objectMaps[haulType.haulName][objectMapIndex]["new"] = {};
42
+ haulType.fields.forEach(haulField => {
43
+ if (lodash.includes(haulField.usage, usageType)) {
44
+ let newValue;
45
+ let curValue = objectMap["current"][haulField.fieldName];
46
+ removeFoundUuids(uuidCollector, haulField, curValue);
47
+ if (haulField.isArray) {
48
+ let newValueArray = [];
49
+ curValue.forEach(curValueIter => {
50
+ newValueArray.push(renameObjectProp(curValueIter, haulField, curNamespaceForObject, newNamespaceForObject));
51
+ });
52
+ newValue = newValueArray;
53
+ } else {
54
+ newValue = renameObjectProp(curValue, haulField, curNamespaceForObject, newNamespaceForObject);
55
+ }
56
+ objectMaps[haulType.haulName][objectMapIndex]["new"][haulField.fieldName] = newValue;
57
+ }
58
+ });
59
+ }
46
60
  });
47
61
  });
48
62
  // Set UUIDs on the Datatypes
@@ -54,13 +68,23 @@ function renameObjectProp(curVal, haulField, curNamespace, newNamespace) {
54
68
  if (util.isBlank(curVal)) {
55
69
  return "";
56
70
  } else {
57
- let replacementRegexMap = util.buildNamespaceReplacementRegex(curNamespace, newNamespace, "gi");
71
+ let replacementRegexMap = buildNamespaceReplacementRegex(curNamespace, newNamespace, "gi");
72
+ let nampespaceReplacement;
58
73
  switch (haulField.type) {
74
+ // UUIDs - just append the new namespace
59
75
  case obProps.propTypes.UUID:
60
76
  return `${curVal}-${util.toLower(newNamespace)}`;
77
+ // URL_STUBs - attempt a replacement, but fall back to prefixing if unchanged to ensure they are unique
61
78
  case obProps.propTypes.URL_STUB:
62
79
  nampespaceReplacement = curVal.replace(replacementRegexMap.from, util.toLower(replacementRegexMap.to));
63
80
  return nampespaceReplacement != curVal ? nampespaceReplacement : `${util.toLower(newNamespace)}-${nampespaceReplacement}`;
81
+ // NAMEs - attempt a replacement, but fall back to prefixing if unchanged to ensure they are unique
82
+ case obProps.propTypes.NAME:
83
+ nampespaceReplacement = curVal.replace(replacementRegexMap.from, replacementRegexMap.to);
84
+ return nampespaceReplacement != curVal
85
+ ? nampespaceReplacement
86
+ : `${newNamespace}${util.firstMatch(curVal, new RegExp(cons.namespaceDelimiterRegex, "g"), "_")}${nampespaceReplacement}`;
87
+ // TEXTs - just do a replacement, they do not need to be unique
64
88
  case obProps.propTypes.TEXT:
65
89
  return curVal.replace(replacementRegexMap.from, replacementRegexMap.to);
66
90
  }
@@ -74,6 +98,46 @@ function removeFoundUuids(uuidCollector, haulField, uuids) {
74
98
  }
75
99
  }
76
100
 
101
+ // Creates the regex replacement for a namespace
102
+ function buildNamespaceReplacementRegex(curNamespace, newNamespace, regexTags) {
103
+ if (curNamespace == cons.noNamespace) {
104
+ return { from: "", to: "" };
105
+ } else {
106
+ // See regex examples here:
107
+ // 1 prefix -> 1 prefix: regexr.com/6t509
108
+ // 1 prefix -> 2 prefix: regexr.com/6t50f
109
+ // 1 prefix -> 3 prefix: regexr.com/6t50i
110
+ // 2 prefix -> 1 prefix: regexr.com/6t50u
111
+ // 2 prefix -> 2 prefix: regexr.com/6t511
112
+ // 2 prefix -> 3 prefix: regexr.com/6t514
113
+ // 3 prefix -> 1 prefix: regexr.com/6t51d
114
+ // 3 prefix -> 2 prefix: regexr.com/6t51j
115
+ // 3 prefix -> 3 prefix: regexr.com/6t51p
116
+
117
+ let namespaceDelimiterRegexStringGrouped = `(${cons.namespaceDelimiterRegex}?)`;
118
+ let findRegex = new RegExp(
119
+ namespaceDelimiterRegexStringGrouped +
120
+ curNamespace.split("_").join(namespaceDelimiterRegexStringGrouped) +
121
+ namespaceDelimiterRegexStringGrouped,
122
+ regexTags
123
+ );
124
+ let curNamespaceLevelCount = curNamespace.split("_").length;
125
+ let replaceStringGroupDelim = curNamespaceLevelCount == 1 ? "$1$2" : "$2";
126
+ let replaceString = "$1" + newNamespace.split("_").join(replaceStringGroupDelim) + "$" + (curNamespaceLevelCount + 1);
127
+ return { from: findRegex, to: replaceString };
128
+ }
129
+ }
130
+
131
+ // Returns the namespace for an object
132
+ function findNamespaceForObject(objectName, namespaceCollector) {
133
+ for (const namespace of obExtract.sortNamespaces(namespaceCollector).reverse()) {
134
+ if (objectName.replace(new RegExp(cons.namespaceDelimiterRegex, "g"), "_").startsWith(namespace)) {
135
+ return namespace;
136
+ }
137
+ }
138
+ return cons.noNamespace;
139
+ }
140
+
77
141
  // Utility function to differenciate between debug logging and real logging
78
142
  function debug(input) {
79
143
  console.log(input);
@@ -9,13 +9,13 @@ const obProps = require("../objectProps");
9
9
  const obExtract = require("../handlers/objectExtractHandler");
10
10
  const { fstat } = require("fs");
11
11
 
12
+ // Global variables
13
+ const progressToShow = [0.05, 0.25, 0.5, 0.75, 0.95];
14
+
12
15
  // Exports
13
16
  module.exports = {
14
17
  // Actually modifies all of the objects based on the objectMaps
15
- modifyObjectFiles: function (appDir, objectMaps, usageType, additionalReplacementMaps) {
16
- // Modify the object files
17
- modifyObjectFiles(appDir, objectMaps, usageType, additionalReplacementMaps);
18
- },
18
+ modifyObjectFiles: modifyObjectFiles,
19
19
 
20
20
  // Modify a single file
21
21
  modifySingleFile: function (filePath, outPath, objectMaps, usageType, additionalReplacementMaps) {
@@ -28,6 +28,16 @@ module.exports = {
28
28
  modifyObjectFile(replacementMaps, null, outPath, false);
29
29
  }
30
30
  },
31
+
32
+ // Renames a single file
33
+ renameSingleFile: function (filePath, objectMaps, usageType, additionalReplacementMaps) {
34
+ if (filePath) {
35
+ // Convert the extracted names & uuids to a flat replacement array
36
+ let replacementMaps = buildReplacementMaps(objectMaps, usageType, additionalReplacementMaps);
37
+ // Actually modify the file
38
+ renameObjectFile(replacementMaps, filePath);
39
+ }
40
+ },
31
41
  };
32
42
 
33
43
  // ---------------------------------------------------
@@ -39,7 +49,11 @@ function modifyObjectFiles(appDir, objectMaps, usageType, additionalReplacementM
39
49
  // Convert the extracted names & uuids to a flat replacement array
40
50
  let replacementMaps = buildReplacementMaps(objectMaps, usageType, additionalReplacementMaps);
41
51
  // Replace all object files with the new names & uuids
42
- let fileCount = { count: 0 };
52
+ let fileCount = {
53
+ count: 0,
54
+ total: obExtract.retrieveObjectMapCount(objectMaps),
55
+ nextProgress: progressToShow[0],
56
+ };
43
57
  util.handleDirFiles(appDir, ["xml", "xsd", "properties"], 3, 2, function (obPath) {
44
58
  modifyObjectFile(replacementMaps, fileCount, obPath, true);
45
59
  });
@@ -76,9 +90,6 @@ function buildReplacementMaps(objectMaps, usageType, additionalReplacementMaps)
76
90
  });
77
91
  });
78
92
  replacementMaps = lodash.compact(replacementMaps);
79
- replacementMaps = lodash.filter(replacementMaps, function (replacementMap) {
80
- return !(replacementMap.from == replacementMap.to);
81
- });
82
93
  if (!util.isBlank(additionalReplacementMaps)) {
83
94
  additionalReplacementMaps.forEach(map => {
84
95
  replacementMaps.push(map);
@@ -111,7 +122,10 @@ function modifyObjectFile(replacementMaps, fileCount, obPath, trackProgress) {
111
122
  renameObjectFile(replacementMaps, obPath);
112
123
  if (trackProgress) {
113
124
  fileCount.count = fileCount.count + 1;
114
- util.print(`${fileCount.count} object files cloned`, true);
125
+ if (fileCount.count / fileCount.total > fileCount.nextProgress) {
126
+ util.print(`${fileCount.nextProgress * 100}% complete`, true);
127
+ fileCount.nextProgress = progressToShow[progressToShow.indexOf(fileCount.nextProgress) + 1];
128
+ }
115
129
  }
116
130
  }
117
131
 
@@ -3,6 +3,7 @@
3
3
  // ---------------------------------------------------
4
4
 
5
5
  const propTypes = {
6
+ NAME: "NAME",
6
7
  UUID: "UUID",
7
8
  TEXT: "TEXT",
8
9
  URL_STUB: "URL_STUB",
@@ -84,7 +85,7 @@ const haulTypes = [
84
85
  {
85
86
  fieldName: fieldNames.NAME,
86
87
  xpath: `xsd:schema/xsd:complexType/@name`,
87
- type: propTypes.TEXT,
88
+ type: propTypes.NAME,
88
89
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
89
90
  },
90
91
  {
@@ -135,7 +136,7 @@ const haulTypes = [
135
136
  {
136
137
  fieldName: fieldNames.NAME,
137
138
  xpath: `dataStoreHaul/*[contains(name(), '')]/name/node()`,
138
- type: propTypes.TEXT,
139
+ type: propTypes.NAME,
139
140
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
140
141
  },
141
142
  {
@@ -187,7 +188,7 @@ const haulTypes = [
187
188
  {
188
189
  fieldName: fieldNames.NAME,
189
190
  xpath: `processModelHaul/*[local-name()='process_model_port']/*[local-name()='pm']/*[local-name()='meta']/*[local-name()='name']/*[local-name()='string-map']/*[local-name()='pair']/*[local-name()='value']/node()`,
190
- type: propTypes.TEXT,
191
+ type: propTypes.NAME,
191
192
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
192
193
  },
193
194
  {
@@ -239,7 +240,7 @@ const haulTypes = [
239
240
  {
240
241
  fieldName: fieldNames.NAME,
241
242
  xpath: `recordTypeHaul/recordType/@name`,
242
- type: propTypes.TEXT,
243
+ type: propTypes.NAME,
243
244
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
244
245
  },
245
246
  {
@@ -346,7 +347,7 @@ const haulTypes = [
346
347
  {
347
348
  fieldName: fieldNames.NAME,
348
349
  xpath: `applicationHaul/application/name/node()`,
349
- type: propTypes.TEXT,
350
+ type: propTypes.NAME,
350
351
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
351
352
  },
352
353
  {
@@ -398,7 +399,7 @@ const haulTypes = [
398
399
  {
399
400
  fieldName: fieldNames.NAME,
400
401
  xpath: `{OBTYPE}Haul/{OBTYPE}/@name`,
401
- type: propTypes.TEXT,
402
+ type: propTypes.NAME,
402
403
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
403
404
  },
404
405
  {
@@ -443,7 +444,7 @@ const haulTypes = [
443
444
  {
444
445
  fieldName: fieldNames.NAME,
445
446
  xpath: `{OBTYPE}Haul/*[contains(name(), '')]/name/node()`,
446
- type: propTypes.TEXT,
447
+ type: propTypes.NAME,
447
448
  usage: [usageTypes.CLONE, usageTypes.BUILD, usageTypes.RENAME],
448
449
  },
449
450
  {