@rockcarver/frodo-lib 0.12.5 → 0.12.7

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 (49) hide show
  1. package/CHANGELOG.md +32 -8
  2. package/cjs/api/ApiTypes.js +42 -0
  3. package/cjs/api/ApiTypes.js.map +1 -0
  4. package/cjs/api/Saml2Api.js +1 -1
  5. package/cjs/api/Saml2Api.js.map +1 -1
  6. package/cjs/index.js +17 -1
  7. package/cjs/index.js.map +1 -1
  8. package/cjs/ops/JourneyOps.js +282 -151
  9. package/cjs/ops/JourneyOps.js.map +1 -1
  10. package/cjs/ops/NodeOps.js +31 -15
  11. package/cjs/ops/NodeOps.js.map +1 -1
  12. package/cjs/ops/OpsTypes.js +34 -0
  13. package/cjs/ops/OpsTypes.js.map +1 -0
  14. package/cjs/ops/Saml2Ops.js +3 -0
  15. package/cjs/ops/Saml2Ops.js.map +1 -1
  16. package/cjs/ops/utils/Console.js +18 -1
  17. package/cjs/ops/utils/Console.js.map +1 -1
  18. package/cjs/ops/utils/ExportImportUtils.js.map +1 -1
  19. package/cjs/storage/SessionStorage.js +4 -0
  20. package/cjs/storage/SessionStorage.js.map +1 -1
  21. package/esm/api/ApiTypes.mjs +33 -0
  22. package/esm/api/Saml2Api.mjs +1 -1
  23. package/esm/index.mjs +5 -0
  24. package/esm/ops/JourneyOps.mjs +208 -119
  25. package/esm/ops/NodeOps.mjs +28 -15
  26. package/esm/ops/OpsTypes.mjs +25 -0
  27. package/esm/ops/Saml2Ops.mjs +1 -1
  28. package/esm/ops/utils/Console.mjs +16 -1
  29. package/esm/storage/SessionStorage.mjs +4 -0
  30. package/package.json +3 -2
  31. package/types/api/ApiTypes.d.ts +102 -0
  32. package/types/api/ApiTypes.d.ts.map +1 -0
  33. package/types/api/Saml2Api.d.ts +1 -1
  34. package/types/api/Saml2Api.d.ts.map +1 -1
  35. package/types/index.d.ts +3 -0
  36. package/types/index.d.ts.map +1 -1
  37. package/types/ops/JourneyOps.d.ts +83 -87
  38. package/types/ops/JourneyOps.d.ts.map +1 -1
  39. package/types/ops/NodeOps.d.ts +10 -0
  40. package/types/ops/NodeOps.d.ts.map +1 -1
  41. package/types/ops/OpsTypes.d.ts +78 -0
  42. package/types/ops/OpsTypes.d.ts.map +1 -0
  43. package/types/ops/Saml2Ops.d.ts +6 -0
  44. package/types/ops/Saml2Ops.d.ts.map +1 -1
  45. package/types/ops/utils/Console.d.ts +9 -1
  46. package/types/ops/utils/Console.d.ts.map +1 -1
  47. package/types/ops/utils/ExportImportUtils.d.ts.map +1 -1
  48. package/types/storage/SessionStorage.d.ts +4 -0
  49. package/types/storage/SessionStorage.d.ts.map +1 -1
@@ -10,7 +10,7 @@ import { getTrees, getTree, putTree, deleteTree } from '../api/TreeApi';
10
10
  import { getEmailTemplate, putEmailTemplate } from '../api/EmailTemplateApi';
11
11
  import { getScript } from '../api/ScriptApi';
12
12
  import * as global from '../storage/StaticStorage';
13
- import { printMessage, createProgressIndicator, updateProgressIndicator, stopProgressIndicator, createTable } from './utils/Console';
13
+ import { printMessage, createProgressIndicator, updateProgressIndicator, stopProgressIndicator, createTable, debugMessage } from './utils/Console';
14
14
  import wordwrap from './utils/Wordwrap';
15
15
  import { getProviderByLocationAndId, getProviders, getProviderMetadata, createProvider, findProviders, updateProvider } from '../api/Saml2Api';
16
16
  import { createCircleOfTrust, getCirclesOfTrust, updateCircleOfTrust } from '../api/CirclesOfTrustApi';
@@ -18,12 +18,17 @@ import { decode, encode, encodeBase64Url, isBase64Encoded } from '../api/utils/B
18
18
  import { getSocialIdentityProviders, putProviderByTypeAndId } from '../api/SocialIdentityProvidersApi';
19
19
  import { getThemes, putThemes } from '../api/ThemeApi';
20
20
  import { createOrUpdateScript } from './ScriptOps';
21
+ import { JourneyClassification } from './OpsTypes';
21
22
  const containerNodes = ['PageNode', 'CustomPageNode'];
22
23
  const scriptedNodes = ['ConfigProviderNode', 'ScriptedDecisionNode', 'ClientScriptNode', 'SocialProviderHandlerNode', 'CustomScriptNode'];
23
24
  const emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];
24
25
  const emptyScriptPlaceholder = '[Empty]';
25
- // use a function vs a template variable to avoid problems in loops
26
- export function createSingleJourneyExportTemplate() {
26
+ /**
27
+ * Create an empty single tree export template
28
+ * @returns {SingleTreeExportInterface} an empty single tree export template
29
+ */
30
+
31
+ export function createSingleTreeExportTemplate() {
27
32
  return {
28
33
  meta: {},
29
34
  innerNodes: {},
@@ -37,8 +42,12 @@ export function createSingleJourneyExportTemplate() {
37
42
  tree: {}
38
43
  };
39
44
  }
40
- // use a function vs a template variable to avoid problems in loops
41
- export function createMultiJourneyExportTemplate() {
45
+ /**
46
+ * Create an empty multi tree export template
47
+ * @returns {MultiTreeExportInterface} an empty multi tree export template
48
+ */
49
+
50
+ export function createMultiTreeExportTemplate() {
42
51
  return {
43
52
  meta: {},
44
53
  trees: {}
@@ -126,39 +135,22 @@ async function getSaml2NodeDependencies(nodeObject, allProviders, allCirclesOfTr
126
135
  };
127
136
  return saml2NodeDependencies;
128
137
  }
129
- } // export async function getTreeNodes(treeObject) {
130
- // const nodeList = Object.entries(treeObject.nodes);
131
- // const results = await Promise.allSettled(
132
- // nodeList.map(
133
- // async ([nodeId, nodeInfo]) => await getNode(nodeId, nodeInfo['nodeType'])
134
- // )
135
- // );
136
- // const nodes = results.filter((r) => r.status === 'fulfilled');
137
- // nodes.map((f) => {
138
- // return f.status;
139
- // });
140
- // const failedList = results.filter((r) => r.status === 'rejected');
141
- // return nodes;
142
- // }
143
-
144
- /**
145
- * Export options
146
- */
147
-
148
-
138
+ }
149
139
  /**
150
140
  * Create export data for a tree/journey with all its nodes and dependencies. The export data can be written to a file as is.
151
141
  * @param {string} treeId tree id/name
152
- * @param {ExportOptions} options export options
153
- * @returns {Promise<SingleJourneyExportTemplate>} a promise that resolves to an object containing the tree and all its nodes and dependencies
142
+ * @param {TreeExportOptions} options export options
143
+ * @returns {Promise<SingleTreeExportInterface>} a promise that resolves to an object containing the tree and all its nodes and dependencies
154
144
  */
145
+
146
+
155
147
  export async function exportJourney(treeId, options = {
156
148
  useStringArrays: true,
157
149
  deps: true,
158
150
  verbose: false
159
151
  }) {
160
152
  const treeObject = await getTree(treeId);
161
- const exportData = createSingleJourneyExportTemplate();
153
+ const exportData = createSingleTreeExportTemplate();
162
154
  const {
163
155
  useStringArrays,
164
156
  deps,
@@ -456,7 +448,7 @@ export async function getJourneys() {
456
448
  * Export journey by id/name to file
457
449
  * @param {string} journeyId journey id/name
458
450
  * @param {string} file optional export file name
459
- * @param {ExportOptions} options export options
451
+ * @param {TreeExportOptions} options export options
460
452
  */
461
453
 
462
454
  export async function exportJourneyToFile(journeyId, file, options) {
@@ -484,10 +476,14 @@ export async function exportJourneyToFile(journeyId, file, options) {
484
476
  /**
485
477
  * Export all journeys to file
486
478
  * @param {string} file optional export file name
487
- * @param {ExportOptions} options export options
479
+ * @param {TreeExportOptions} options export options
488
480
  */
489
481
 
490
- export async function exportJourneysToFile(file, options) {
482
+ export async function exportJourneysToFile(file, options = {
483
+ deps: false,
484
+ useStringArrays: false,
485
+ verbose: false
486
+ }) {
491
487
  let fileName = file;
492
488
 
493
489
  if (!fileName) {
@@ -495,7 +491,7 @@ export async function exportJourneysToFile(file, options) {
495
491
  }
496
492
 
497
493
  const trees = (await getTrees()).result;
498
- const fileData = createMultiJourneyExportTemplate();
494
+ const fileData = createMultiTreeExportTemplate();
499
495
  createProgressIndicator(trees.length, 'Exporting journeys...');
500
496
 
501
497
  for (const tree of trees) {
@@ -515,7 +511,7 @@ export async function exportJourneysToFile(file, options) {
515
511
  }
516
512
  /**
517
513
  * Export all journeys to separate files
518
- * @param {ExportOptions} options export options
514
+ * @param {TreeExportOptions} options export options
519
515
  */
520
516
 
521
517
  export async function exportJourneysToFiles(options) {
@@ -535,15 +531,12 @@ export async function exportJourneysToFiles(options) {
535
531
 
536
532
  stopProgressIndicator('Done');
537
533
  }
538
- /**
539
- * Import options
540
- */
541
-
542
534
  /**
543
535
  * Helper to import a tree with all dependencies from an import data object (typically read from a file)
544
- * @param {SingleJourneyExportTemplate} treeObject tree object containing tree and all its dependencies
545
- * @param {ImportOptions} options import options
536
+ * @param {SingleTreeExportInterface} treeObject tree object containing tree and all its dependencies
537
+ * @param {TreeImportOptions} options import options
546
538
  */
539
+
547
540
  export async function importJourney(treeObject, options) {
548
541
  const {
549
542
  reUuid,
@@ -754,8 +747,29 @@ export async function importJourney(treeObject, options) {
754
747
  try {
755
748
  await putNode(newUuid, nodeType, innerNodeData);
756
749
  } catch (nodeImportError) {
750
+ var _nodeImportError$resp, _nodeImportError$resp2, _nodeImportError$resp3;
751
+
757
752
  if (nodeImportError.response.status === 400 && nodeImportError.response.data.message === 'Data validation failed for the attribute, Script') {
758
753
  throw new Error(`Missing script ${innerNodeData['script']} referenced by inner node ${innerNodeId}${innerNodeId === newUuid ? '' : ` [${newUuid}]`} (${innerNodeData['_type']['_id']}) in journey ${treeId}.`);
754
+ } else if (((_nodeImportError$resp = nodeImportError.response) === null || _nodeImportError$resp === void 0 ? void 0 : _nodeImportError$resp.status) === 400 && ((_nodeImportError$resp2 = nodeImportError.response) === null || _nodeImportError$resp2 === void 0 ? void 0 : (_nodeImportError$resp3 = _nodeImportError$resp2.data) === null || _nodeImportError$resp3 === void 0 ? void 0 : _nodeImportError$resp3.message) === 'Invalid attribute specified.') {
755
+ const {
756
+ validAttributes
757
+ } = nodeImportError.response.data.detail;
758
+ validAttributes.push('_id');
759
+
760
+ for (const attribute of Object.keys(innerNodeData)) {
761
+ if (!validAttributes.includes(attribute)) {
762
+ if (verbose) printMessage(`\n - Removing invalid attribute: ${attribute}`, 'warn', false);
763
+ delete innerNodeData[attribute];
764
+ }
765
+ }
766
+
767
+ try {
768
+ await putNode(newUuid, nodeType, innerNodeData);
769
+ } catch (nodeImportError2) {
770
+ printMessage(nodeImportError2.response.data, 'error');
771
+ throw new Error(`Error importing node ${innerNodeId}${innerNodeId === newUuid ? '' : ` [${newUuid}]`} in journey ${treeId}`);
772
+ }
759
773
  } else {
760
774
  printMessage(nodeImportError.response.data, 'error');
761
775
  throw new Error(`Error importing inner node ${innerNodeId}${innerNodeId === newUuid ? '' : ` [${newUuid}]`} in journey ${treeId}`);
@@ -795,7 +809,7 @@ export async function importJourney(treeObject, options) {
795
809
  // and the node's identityResource is the same as the tree's identityResource
796
810
  // change it to the current realm managed user identityResource otherwise leave it alone.
797
811
 
798
- if (nodeData['identityResource'] && nodeData['identityResource'].endsWith('user') && nodeData['identityResource'] === treeObject.tree.identityResource) {
812
+ if (nodeData.identityResource && nodeData.identityResource.endsWith('user') && nodeData.identityResource === treeObject.tree.identityResource) {
799
813
  nodeData['identityResource'] = `managed/${getRealmManagedUser()}`;
800
814
  if (verbose) printMessage(`\n - identityResource: ${nodeData['identityResource']}`, 'info', false);
801
815
  }
@@ -803,8 +817,29 @@ export async function importJourney(treeObject, options) {
803
817
  try {
804
818
  await putNode(newUuid, nodeType, nodeData);
805
819
  } catch (nodeImportError) {
820
+ var _nodeImportError$resp4, _nodeImportError$resp5, _nodeImportError$resp6;
821
+
806
822
  if (nodeImportError.response.status === 400 && nodeImportError.response.data.message === 'Data validation failed for the attribute, Script') {
807
823
  throw new Error(`Missing script ${nodeData['script']} referenced by node ${nodeId}${nodeId === newUuid ? '' : ` [${newUuid}]`} (${nodeData['_type']['_id']}) in journey ${treeId}.`);
824
+ } else if (((_nodeImportError$resp4 = nodeImportError.response) === null || _nodeImportError$resp4 === void 0 ? void 0 : _nodeImportError$resp4.status) === 400 && ((_nodeImportError$resp5 = nodeImportError.response) === null || _nodeImportError$resp5 === void 0 ? void 0 : (_nodeImportError$resp6 = _nodeImportError$resp5.data) === null || _nodeImportError$resp6 === void 0 ? void 0 : _nodeImportError$resp6.message) === 'Invalid attribute specified.') {
825
+ const {
826
+ validAttributes
827
+ } = nodeImportError.response.data.detail;
828
+ validAttributes.push('_id');
829
+
830
+ for (const attribute of Object.keys(nodeData)) {
831
+ if (!validAttributes.includes(attribute)) {
832
+ if (verbose) printMessage(`\n - Removing invalid attribute: ${attribute}`, 'warn', false);
833
+ delete nodeData[attribute];
834
+ }
835
+ }
836
+
837
+ try {
838
+ await putNode(newUuid, nodeType, nodeData);
839
+ } catch (nodeImportError2) {
840
+ printMessage(nodeImportError2.response.data, 'error');
841
+ throw new Error(`Error importing node ${nodeId}${nodeId === newUuid ? '' : ` [${newUuid}]`} in journey ${treeId}`);
842
+ }
808
843
  } else {
809
844
  printMessage(nodeImportError.response.data, 'error');
810
845
  throw new Error(`Error importing node ${nodeId}${nodeId === newUuid ? '' : ` [${newUuid}]`} in journey ${treeId}`);
@@ -831,7 +866,7 @@ export async function importJourney(treeObject, options) {
831
866
  // Set the identityResource for the tree to the selected resource.
832
867
 
833
868
 
834
- if (treeObject.tree.identityResource && treeObject.tree['identityResource'].endsWith('user')) {
869
+ if (treeObject.tree.identityResource && treeObject.tree['identityResource'].endsWith('user') || storage.session.getDeploymentType() === global.CLOUD_DEPLOYMENT_TYPE_KEY || storage.session.getDeploymentType() === global.FORGEOPS_DEPLOYMENT_TYPE_KEY) {
835
870
  treeObject.tree.identityResource = `managed/${getRealmManagedUser()}`;
836
871
  if (verbose) printMessage(` - identityResource: ${treeObject.tree.identityResource}`, 'info', false);
837
872
  }
@@ -852,7 +887,7 @@ export async function importJourney(treeObject, options) {
852
887
 
853
888
  for (const attribute of Object.keys(treeObject.tree)) {
854
889
  if (!validAttributes.includes(attribute)) {
855
- if (verbose) printMessage(`\n - Removing invalid attribute: ${attribute}`, 'info', false);
890
+ if (verbose) printMessage(`\n - Removing invalid attribute: ${attribute}`, 'warn', false);
856
891
  delete treeObject.tree[attribute];
857
892
  }
858
893
  }
@@ -868,7 +903,7 @@ export async function importJourney(treeObject, options) {
868
903
  var _importError$response9, _importError$response10;
869
904
 
870
905
  printMessage(((_importError$response9 = importError.response) === null || _importError$response9 === void 0 ? void 0 : _importError$response9.data) || importError, 'error');
871
- console.dir(((_importError$response10 = importError.response) === null || _importError$response10 === void 0 ? void 0 : _importError$response10.data) || importError);
906
+ debugMessage(((_importError$response10 = importError.response) === null || _importError$response10 === void 0 ? void 0 : _importError$response10.data) || importError);
872
907
  throw new Error(`\nError importing journey flow ${treeId}`);
873
908
  }
874
909
  }
@@ -940,7 +975,7 @@ async function resolveDependencies(installedJorneys, journeyMap, unresolvedJourn
940
975
  * Import a journey from file
941
976
  * @param {string} journeyId journey id/name
942
977
  * @param {string} file import file name
943
- * @param {ImportOptions} options import options
978
+ * @param {TreeImportOptions} options import options
944
979
  */
945
980
 
946
981
 
@@ -996,7 +1031,7 @@ export async function importJourneyFromFile(journeyId, file, options) {
996
1031
  /**
997
1032
  * Import first journey from file
998
1033
  * @param {string} file import file name
999
- * @param {ImportOptions} options import options
1034
+ * @param {TreeImportOptions} options import options
1000
1035
  */
1001
1036
 
1002
1037
  export async function importFirstJourneyFromFile(file, options) {
@@ -1061,7 +1096,7 @@ export async function importFirstJourneyFromFile(file, options) {
1061
1096
  /**
1062
1097
  * Helper to import multiple trees from a tree map
1063
1098
  * @param {Object} treesMap map of trees object
1064
- * @param {ImportOptions} options import options
1099
+ * @param {TreeImportOptions} options import options
1065
1100
  */
1066
1101
 
1067
1102
  async function importAllJourneys(treesMap, options) {
@@ -1098,7 +1133,7 @@ async function importAllJourneys(treesMap, options) {
1098
1133
  /**
1099
1134
  * Import all journeys from file
1100
1135
  * @param {string} file import file name
1101
- * @param {ImportOptions} options import options
1136
+ * @param {TreeImportOptions} options import options
1102
1137
  */
1103
1138
 
1104
1139
 
@@ -1111,7 +1146,7 @@ export async function importJourneysFromFile(file, options) {
1111
1146
  }
1112
1147
  /**
1113
1148
  * Import all journeys from separate files
1114
- * @param {ImportOptions} options import options
1149
+ * @param {TreeImportOptions} options import options
1115
1150
  */
1116
1151
 
1117
1152
  export async function importJourneysFromFiles(options) {
@@ -1129,94 +1164,148 @@ export async function importJourneysFromFiles(options) {
1129
1164
  importAllJourneys(allJourneysData.trees, options);
1130
1165
  }
1131
1166
  /**
1132
- * Describe a journey
1133
- * @param {SingleJourneyExportTemplate} journeyData journey export object
1167
+ * Get the node reference obbject for a node object. Node reference objects
1168
+ * are used in a tree flow definition and within page nodes to reference
1169
+ * nodes. Among other things, node references contain all the non-configuration
1170
+ * meta data that exists for readaility, like the x/y coordinates of the node
1171
+ * and the display name chosen by the tree designer. The dislay name is the
1172
+ * only intuitive link between the graphical representation of the tree and
1173
+ * the node configurations that make up the tree.
1174
+ * @param nodeObj node object to retrieve the node reference object for
1175
+ * @param singleTreeExport tree export with or without dependencies
1176
+ * @returns {NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface} node reference object
1134
1177
  */
1135
1178
 
1136
- export function describeJourney(journeyData) {
1137
- var _journeyData$themes;
1138
-
1139
- const nodeTypeMap = {};
1140
-
1141
- for (const nodeData of Object.values(journeyData.nodes)) {
1142
- if (nodeTypeMap[nodeData['_type']['_id']]) {
1143
- nodeTypeMap[nodeData['_type']['_id']] += 1;
1144
- } else {
1145
- nodeTypeMap[nodeData['_type']['_id']] = 1;
1146
- }
1147
- }
1148
-
1149
- for (const nodeData of Object.values(journeyData.innerNodes)) {
1150
- if (nodeTypeMap[nodeData['_type']['_id']]) {
1151
- nodeTypeMap[nodeData['_type']['_id']] += 1;
1152
- } else {
1153
- nodeTypeMap[nodeData['_type']['_id']] = 1;
1179
+ export function getNodeRef(nodeObj, singleTreeExport) {
1180
+ if (singleTreeExport.tree.nodes[nodeObj._id]) {
1181
+ return singleTreeExport.tree.nodes[nodeObj._id];
1182
+ } else {
1183
+ for (const node of Object.values(singleTreeExport.nodes)) {
1184
+ if (containerNodes.includes(node._type._id)) {
1185
+ for (const nodeRef of node.nodes) {
1186
+ if (nodeRef._id === nodeObj._id) {
1187
+ return nodeRef;
1188
+ }
1189
+ }
1190
+ }
1154
1191
  }
1155
1192
  }
1156
1193
 
1157
- printMessage(`\n${journeyData.tree._id}`, 'data');
1158
- const line = Array(journeyData.tree._id['length']).fill('=').join('');
1159
- printMessage(line);
1194
+ return undefined;
1195
+ }
1196
+ /**
1197
+ * Default tree export resolver used to resolve a tree id/name to a full export
1198
+ * w/o dependencies of that tree from a platform instance.
1199
+ * @param {string} treeId id/name of the tree to resolve
1200
+ * @returns {TreeExportResolverInterface} tree export
1201
+ */
1160
1202
 
1161
- if (journeyData.tree.description) {
1162
- printMessage(`\n${journeyData.tree.description}\n`, 'data');
1163
- }
1203
+ export const onlineTreeExportResolver = async function (treeId) {
1204
+ debugMessage(`onlineTreeExportResolver(${treeId})`);
1205
+ return await exportJourney(treeId, {
1206
+ deps: false,
1207
+ useStringArrays: false,
1208
+ verbose: false
1209
+ });
1210
+ };
1211
+ /**
1212
+ * Tree export resolver used to resolve a tree id/name to a full export
1213
+ * of that tree from individual `treename.journey.json` export files.
1214
+ * @param {string} treeId id/name of the tree to resolve
1215
+ * @returns {TreeExportResolverInterface} tree export
1216
+ */
1164
1217
 
1165
- printMessage('\nNodes:', 'data');
1218
+ export const fileByIdTreeExportResolver = async function (treeId) {
1219
+ debugMessage(`fileByIdTreeExportResolver(${treeId})`);
1220
+ let treeExport = createSingleTreeExportTemplate();
1221
+ const file = getTypedFilename(`${treeId}`, 'journey');
1222
+ debugMessage(`fileByIdTreeExportResolver: resolving '${treeId}' to ${file}`);
1166
1223
 
1167
- if (Object.entries(nodeTypeMap).length) {
1168
- for (const [name, count] of Object.entries(nodeTypeMap)) {
1169
- printMessage(`- [${String(count)['brightCyan']}] ${name}`, 'data');
1170
- }
1171
- }
1224
+ try {
1225
+ var _jsonData$tree;
1172
1226
 
1173
- if ((_journeyData$themes = journeyData.themes) !== null && _journeyData$themes !== void 0 && _journeyData$themes.length) {
1174
- printMessage('\nThemes:', 'data');
1227
+ const jsonData = JSON.parse(fs.readFileSync(file, 'utf8')); // did we resolve the tree we were asked to resolved?
1175
1228
 
1176
- for (const themeData of journeyData.themes) {
1177
- printMessage(`- [${themeData['_id']['brightCyan']}] ${themeData['name']}`, 'data');
1229
+ if (((_jsonData$tree = jsonData.tree) === null || _jsonData$tree === void 0 ? void 0 : _jsonData$tree._id) === treeId) {
1230
+ treeExport = jsonData;
1231
+ } // check if this is a file with multiple trees and get journey by id
1232
+ else if (jsonData.trees && jsonData.trees[treeId]) {
1233
+ treeExport = jsonData.trees[treeId];
1178
1234
  }
1235
+ } catch (error) {//
1179
1236
  }
1180
1237
 
1181
- if (Object.entries(journeyData.scripts).length) {
1182
- printMessage('\nScripts:', 'data');
1238
+ return treeExport;
1239
+ };
1240
+ /**
1241
+ * Factory that creates a tree export resolver used to resolve a tree id
1242
+ * to a full export of that tree from a multi-tree export file.
1243
+ * @param {string} file multi-tree export file
1244
+ * @returns {TreeExportResolverInterface} tree export resolver
1245
+ */
1246
+
1247
+ export function createFileParamTreeExportResolver(file) {
1248
+ const fileParamTreeExportResolver = async function (treeId) {
1249
+ debugMessage(`fileParamTreeExportResolver(${treeId})`);
1250
+ let treeExport = createSingleTreeExportTemplate();
1183
1251
 
1184
- for (const scriptData of Object.values(journeyData.scripts)) {
1185
- printMessage(`- [${scriptData['_id'].brightCyan}] ${scriptData['name']}`, 'data');
1252
+ try {
1253
+ var _jsonData$tree2;
1254
+
1255
+ const jsonData = JSON.parse(fs.readFileSync(file, 'utf8')); // did we resolve the tree we were asked to resolved?
1256
+
1257
+ if (((_jsonData$tree2 = jsonData.tree) === null || _jsonData$tree2 === void 0 ? void 0 : _jsonData$tree2._id) === treeId) {
1258
+ treeExport = jsonData;
1259
+ } // check if this is a file with multiple trees and get journey by id
1260
+ else if (jsonData.trees && jsonData.trees[treeId]) {
1261
+ treeExport = jsonData.trees[treeId];
1262
+ } // fall back to fileByIdTreeExportResolver
1263
+ else {
1264
+ treeExport = await fileByIdTreeExportResolver(treeId);
1265
+ }
1266
+ } catch (error) {//
1186
1267
  }
1187
- }
1188
1268
 
1189
- if (Object.entries(journeyData.emailTemplates).length) {
1190
- printMessage('\nEmail Templates:', 'data');
1269
+ return treeExport;
1270
+ };
1191
1271
 
1192
- for (const templateData of Object.values(journeyData.emailTemplates)) {
1193
- printMessage(`- ${templateData['_id'].split('/')[1]}`, 'data');
1194
- }
1195
- }
1272
+ debugMessage('fileParamTreeExportResolver:');
1273
+ debugMessage(fileParamTreeExportResolver);
1274
+ return fileParamTreeExportResolver;
1275
+ }
1276
+ /**
1277
+ * Get tree dependencies (all descendent inner trees)
1278
+ * @param {SingleTreeExportInterface} treeExport single tree export
1279
+ * @param {string[]} resolvedTreeIds list of tree ids wich have already been resolved
1280
+ * @param {TreeExportResolverInterface} resolveTreeExport tree export resolver callback function
1281
+ * @returns {Promise<TreeDependencyMapInterface>} a promise that resolves to a tree dependency map
1282
+ */
1196
1283
 
1197
- if (Object.entries(journeyData.socialIdentityProviders).length) {
1198
- printMessage('\nSocial Identity Providers:', 'data');
1284
+ export async function getTreeDescendents(treeExport, resolveTreeExport = onlineTreeExportResolver, resolvedTreeIds = []) {
1285
+ debugMessage(`getTreeDependencies(${treeExport.tree._id}, [${resolvedTreeIds.join(', ')}])`);
1199
1286
 
1200
- for (const socialIdpData of Object.values(journeyData.socialIdentityProviders)) {
1201
- printMessage(`- ${socialIdpData['_id']} [${socialIdpData['_type']['_id'].brightCyan}]`, 'data');
1202
- }
1287
+ if (!resolvedTreeIds.includes(treeExport.tree._id)) {
1288
+ resolvedTreeIds.push(treeExport.tree._id);
1203
1289
  }
1204
1290
 
1205
- if (Object.entries(journeyData.saml2Entities).length) {
1206
- printMessage('\nSAML2 Entity Providers:', 'data');
1291
+ const treeDependencyMap = {
1292
+ [treeExport.tree._id]: []
1293
+ };
1294
+ const dependencies = [];
1207
1295
 
1208
- for (const entityProviderData of Object.values(journeyData.saml2Entities)) {
1209
- printMessage(`- ${entityProviderData['entityId']}`, 'data');
1210
- }
1211
- }
1296
+ for (const [nodeId, node] of Object.entries(treeExport.tree.nodes)) {
1297
+ const innerTreeId = treeExport.nodes[nodeId].tree;
1212
1298
 
1213
- if (Object.entries(journeyData.circlesOfTrust).length) {
1214
- printMessage('\nSAML2 Circles Of Trust:', 'data');
1299
+ if (node.nodeType === 'InnerTreeEvaluatorNode' && !resolvedTreeIds.includes(innerTreeId)) {
1300
+ const innerTreeExport = await resolveTreeExport(innerTreeId);
1301
+ debugMessage(`resolved inner tree: ${innerTreeExport.tree._id}`); // resolvedTreeIds.push(innerTreeId);
1215
1302
 
1216
- for (const cotData of Object.values(journeyData.circlesOfTrust)) {
1217
- printMessage(`- ${cotData['_id']}`, 'data');
1303
+ dependencies.push(await getTreeDescendents(innerTreeExport, resolveTreeExport, resolvedTreeIds));
1218
1304
  }
1219
1305
  }
1306
+
1307
+ treeDependencyMap[treeExport.tree._id] = dependencies;
1308
+ return treeDependencyMap;
1220
1309
  }
1221
1310
  /**
1222
1311
  * Find all node configuration objects that are no longer referenced by any tree
@@ -1322,7 +1411,7 @@ export async function removeOrphanedNodes(orphanedNodes) {
1322
1411
  }
1323
1412
  /**
1324
1413
  * Analyze if a journey contains any custom nodes considering the detected or the overridden version.
1325
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1414
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1326
1415
  * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.
1327
1416
  */
1328
1417
 
@@ -1339,7 +1428,7 @@ export function isCustomJourney(journey) {
1339
1428
  }
1340
1429
  /**
1341
1430
  * Analyze if a journey contains any premium nodes considering the detected or the overridden version.
1342
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1431
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1343
1432
  * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.
1344
1433
  */
1345
1434
 
@@ -1356,7 +1445,7 @@ export function isPremiumJourney(journey) {
1356
1445
  }
1357
1446
  /**
1358
1447
  * Analyze if a journey contains any cloud-only nodes considering the detected or the overridden version.
1359
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1448
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1360
1449
  * @returns {boolean} True if the journey/tree contains any cloud-only nodes, false otherwise.
1361
1450
  */
1362
1451
 
@@ -1376,8 +1465,8 @@ export function isCloudOnlyJourney(journey) {
1376
1465
  * - standard: can run on any instance of a ForgeRock platform
1377
1466
  * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud
1378
1467
  * - premium: utilizes nodes, which come at a premium
1379
- * @param {SingleJourneyExportTemplate} journey journey export data
1380
- * @returns {string[]} an array of one or multiple classifications
1468
+ * @param {SingleTreeExportInterface} journey journey export data
1469
+ * @returns {JourneyClassification[]} an array of one or multiple classifications
1381
1470
  */
1382
1471
 
1383
1472
  export function getJourneyClassification(journey) {
@@ -1387,14 +1476,14 @@ export function getJourneyClassification(journey) {
1387
1476
  const cloud = isCloudOnlyJourney(journey);
1388
1477
 
1389
1478
  if (custom) {
1390
- classifications.push('custom');
1479
+ classifications.push(JourneyClassification.CUSTOM);
1391
1480
  } else if (cloud) {
1392
- classifications.push('cloud');
1481
+ classifications.push(JourneyClassification.CLOUD);
1393
1482
  } else {
1394
- classifications.push('standard');
1483
+ classifications.push(JourneyClassification.STANDARD);
1395
1484
  }
1396
1485
 
1397
- if (premium) classifications.push('premium');
1486
+ if (premium) classifications.push(JourneyClassification.PREMIUM);
1398
1487
  return classifications;
1399
1488
  }
1400
1489
  /**
@@ -5,6 +5,7 @@ import { getTrees } from '../api/TreeApi';
5
5
  import { printMessage, createProgressIndicator, updateProgressIndicator, stopProgressIndicator } from './utils/Console';
6
6
  import { getProviderByLocationAndId, getProviderMetadata } from '../api/Saml2Api';
7
7
  import { encodeBase64Url } from '../api/utils/Base64';
8
+ import { NodeClassification } from './OpsTypes';
8
9
  const containerNodes = ['PageNode', 'CustomPageNode'];
9
10
  const scriptedNodes = ['ConfigProviderNode', 'ScriptedDecisionNode', 'ClientScriptNode', 'SocialProviderHandlerNode', 'CustomScriptNode'];
10
11
  const emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];
@@ -91,21 +92,7 @@ async function getSaml2NodeDependencies(nodeObject, allProviders, allCirclesOfTr
91
92
  };
92
93
  return saml2NodeDependencies;
93
94
  }
94
- } // export async function getTreeNodes(treeObject) {
95
- // const nodeList = Object.entries(treeObject.nodes);
96
- // const results = await Promise.allSettled(
97
- // nodeList.map(
98
- // async ([nodeId, nodeInfo]) => await getNode(nodeId, nodeInfo['nodeType'])
99
- // )
100
- // );
101
- // const nodes = results.filter((r) => r.status === 'fulfilled');
102
- // nodes.map((f) => {
103
- // return f.status;
104
- // });
105
- // const failedList = results.filter((r) => r.status === 'rejected');
106
- // return nodes;
107
- // }
108
-
95
+ }
109
96
  /**
110
97
  * Find all node configuration objects that are no longer referenced by any tree
111
98
  * @returns {Promise<unknown[]>} a promise that resolves to an array of orphaned nodes
@@ -291,4 +278,30 @@ export function isCustomNode(nodeType) {
291
278
 
292
279
  return !ootbNodeTypes.includes(nodeType) && !isPremiumNode(nodeType) && !isCloudOnlyNode(nodeType);
293
280
  }
281
+ /**
282
+ * Get a node's classifications, which can be one or multiple of:
283
+ * - standard: can run on any instance of a ForgeRock platform
284
+ * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud
285
+ * - premium: utilizes nodes, which come at a premium
286
+ * @param {string} nodeType Node type
287
+ * @returns {NodeClassification[]} an array of one or multiple classifications
288
+ */
289
+
290
+ export function getNodeClassification(nodeType) {
291
+ const classifications = [];
292
+ const premium = isPremiumNode(nodeType);
293
+ const custom = isCustomNode(nodeType);
294
+ const cloud = isCloudOnlyNode(nodeType);
295
+
296
+ if (custom) {
297
+ classifications.push(NodeClassification.CUSTOM);
298
+ } else if (cloud) {
299
+ classifications.push(NodeClassification.CLOUD);
300
+ } else {
301
+ classifications.push(NodeClassification.STANDARD);
302
+ }
303
+
304
+ if (premium) classifications.push(NodeClassification.PREMIUM);
305
+ return classifications;
306
+ }
294
307
  //# sourceMappingURL=NodeOps.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Tree export options
3
+ */
4
+
5
+ /**
6
+ * Tree import options
7
+ */
8
+ export let NodeClassification;
9
+
10
+ (function (NodeClassification) {
11
+ NodeClassification["STANDARD"] = "standard";
12
+ NodeClassification["CUSTOM"] = "custom";
13
+ NodeClassification["CLOUD"] = "cloud";
14
+ NodeClassification["PREMIUM"] = "premium";
15
+ })(NodeClassification || (NodeClassification = {}));
16
+
17
+ export let JourneyClassification;
18
+
19
+ (function (JourneyClassification) {
20
+ JourneyClassification["STANDARD"] = "standard";
21
+ JourneyClassification["CUSTOM"] = "custom";
22
+ JourneyClassification["CLOUD"] = "cloud";
23
+ JourneyClassification["PREMIUM"] = "premium";
24
+ })(JourneyClassification || (JourneyClassification = {}));
25
+ //# sourceMappingURL=OpsTypes.js.map
@@ -6,7 +6,7 @@ import { getProviders, findProviders, getProviderByLocationAndId, getProviderMet
6
6
  import { getScript } from '../api/ScriptApi';
7
7
  import { convertBase64TextToArray, convertBase64UrlTextToArray, convertTextArrayToBase64, convertTextArrayToBase64Url, getRealmString, getTypedFilename, saveJsonToFile, saveTextToFile, validateImport } from './utils/ExportImportUtils';
8
8
  import { createOrUpdateScript } from './ScriptOps';
9
- const roleMap = {
9
+ export const roleMap = {
10
10
  identityProvider: 'IDP',
11
11
  serviceProvider: 'SP',
12
12
  attributeQueryProvider: 'AttrQuery',