@rockcarver/frodo-lib 0.12.5 → 0.12.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +23 -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 +9 -1
  7. package/cjs/index.js.map +1 -1
  8. package/cjs/ops/CirclesOfTrustOps.js +12 -1
  9. package/cjs/ops/CirclesOfTrustOps.js.map +1 -1
  10. package/cjs/ops/EmailTemplateOps.js +12 -0
  11. package/cjs/ops/EmailTemplateOps.js.map +1 -1
  12. package/cjs/ops/IdpOps.js +12 -1
  13. package/cjs/ops/IdpOps.js.map +1 -1
  14. package/cjs/ops/JourneyOps.js +388 -140
  15. package/cjs/ops/JourneyOps.js.map +1 -1
  16. package/cjs/ops/NodeOps.js +13 -0
  17. package/cjs/ops/NodeOps.js.map +1 -1
  18. package/cjs/ops/OpsTypes.js +6 -0
  19. package/cjs/ops/OpsTypes.js.map +1 -0
  20. package/cjs/ops/Saml2Ops.js +24 -1
  21. package/cjs/ops/Saml2Ops.js.map +1 -1
  22. package/cjs/ops/ScriptOps.js +12 -0
  23. package/cjs/ops/ScriptOps.js.map +1 -1
  24. package/cjs/ops/ThemeOps.js +12 -0
  25. package/cjs/ops/ThemeOps.js.map +1 -1
  26. package/cjs/ops/utils/Console.js +18 -1
  27. package/cjs/ops/utils/Console.js.map +1 -1
  28. package/cjs/ops/utils/ExportImportUtils.js.map +1 -1
  29. package/cjs/storage/SessionStorage.js +4 -0
  30. package/cjs/storage/SessionStorage.js.map +1 -1
  31. package/esm/api/ApiTypes.mjs +33 -0
  32. package/esm/api/Saml2Api.mjs +1 -1
  33. package/esm/index.mjs +2 -0
  34. package/esm/ops/CirclesOfTrustOps.mjs +11 -1
  35. package/esm/ops/EmailTemplateOps.mjs +11 -1
  36. package/esm/ops/IdpOps.mjs +11 -1
  37. package/esm/ops/JourneyOps.mjs +269 -82
  38. package/esm/ops/NodeOps.mjs +11 -0
  39. package/esm/ops/OpsTypes.mjs +2 -0
  40. package/esm/ops/Saml2Ops.mjs +20 -2
  41. package/esm/ops/ScriptOps.mjs +10 -0
  42. package/esm/ops/ThemeOps.mjs +10 -0
  43. package/esm/ops/utils/Console.mjs +16 -1
  44. package/esm/storage/SessionStorage.mjs +4 -0
  45. package/package.json +1 -1
  46. package/types/api/ApiTypes.d.ts +102 -0
  47. package/types/api/ApiTypes.d.ts.map +1 -0
  48. package/types/api/Saml2Api.d.ts +1 -1
  49. package/types/api/Saml2Api.d.ts.map +1 -1
  50. package/types/index.d.ts +2 -0
  51. package/types/index.d.ts.map +1 -1
  52. package/types/ops/CirclesOfTrustOps.d.ts +7 -0
  53. package/types/ops/CirclesOfTrustOps.d.ts.map +1 -1
  54. package/types/ops/EmailTemplateOps.d.ts +7 -0
  55. package/types/ops/EmailTemplateOps.d.ts.map +1 -1
  56. package/types/ops/IdpOps.d.ts +7 -0
  57. package/types/ops/IdpOps.d.ts.map +1 -1
  58. package/types/ops/JourneyOps.d.ts +107 -89
  59. package/types/ops/JourneyOps.d.ts.map +1 -1
  60. package/types/ops/NodeOps.d.ts +8 -0
  61. package/types/ops/NodeOps.d.ts.map +1 -1
  62. package/types/ops/OpsTypes.d.ts +66 -0
  63. package/types/ops/OpsTypes.d.ts.map +1 -0
  64. package/types/ops/Saml2Ops.d.ts +13 -0
  65. package/types/ops/Saml2Ops.d.ts.map +1 -1
  66. package/types/ops/ScriptOps.d.ts +7 -0
  67. package/types/ops/ScriptOps.d.ts.map +1 -1
  68. package/types/ops/ThemeOps.d.ts +7 -0
  69. package/types/ops/ThemeOps.d.ts.map +1 -1
  70. package/types/ops/utils/Console.d.ts +9 -1
  71. package/types/ops/utils/Console.d.ts.map +1 -1
  72. package/types/ops/utils/ExportImportUtils.d.ts.map +1 -1
  73. package/types/storage/SessionStorage.d.ts +4 -0
  74. package/types/storage/SessionStorage.d.ts.map +1 -1
@@ -6,11 +6,18 @@ import { getRealmManagedUser, replaceAll } from './utils/OpsUtils';
6
6
  import storage from '../storage/SessionStorage';
7
7
  import { getNode, putNode, deleteNode, getNodeTypes, getNodesByType } from '../api/NodeApi';
8
8
  import { isCloudOnlyNode, isCustomNode, isPremiumNode } from './NodeOps';
9
+ import * as Node from './NodeOps';
10
+ import * as EmailTemplate from './EmailTemplateOps';
11
+ import * as Script from './ScriptOps';
12
+ import * as Theme from './ThemeOps';
13
+ import * as Idp from './IdpOps';
14
+ import * as Saml2 from './Saml2Ops';
15
+ import * as CirclesOfTrust from './CirclesOfTrustOps';
9
16
  import { getTrees, getTree, putTree, deleteTree } from '../api/TreeApi';
10
17
  import { getEmailTemplate, putEmailTemplate } from '../api/EmailTemplateApi';
11
18
  import { getScript } from '../api/ScriptApi';
12
19
  import * as global from '../storage/StaticStorage';
13
- import { printMessage, createProgressIndicator, updateProgressIndicator, stopProgressIndicator, createTable } from './utils/Console';
20
+ import { printMessage, createProgressIndicator, updateProgressIndicator, stopProgressIndicator, createTable, debugMessage } from './utils/Console';
14
21
  import wordwrap from './utils/Wordwrap';
15
22
  import { getProviderByLocationAndId, getProviders, getProviderMetadata, createProvider, findProviders, updateProvider } from '../api/Saml2Api';
16
23
  import { createCircleOfTrust, getCirclesOfTrust, updateCircleOfTrust } from '../api/CirclesOfTrustApi';
@@ -22,8 +29,22 @@ const containerNodes = ['PageNode', 'CustomPageNode'];
22
29
  const scriptedNodes = ['ConfigProviderNode', 'ScriptedDecisionNode', 'ClientScriptNode', 'SocialProviderHandlerNode', 'CustomScriptNode'];
23
30
  const emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];
24
31
  const emptyScriptPlaceholder = '[Empty]';
25
- // use a function vs a template variable to avoid problems in loops
26
- export function createSingleJourneyExportTemplate() {
32
+ /**
33
+ * Get a one-line description of the tree object
34
+ * @param {TreeSkeleton} treeObj circle of trust object to describe
35
+ * @returns {string} a one-line description
36
+ */
37
+
38
+ export function getOneLineDescription(treeObj) {
39
+ const description = `[${treeObj._id['brightCyan']}]`;
40
+ return description;
41
+ }
42
+ /**
43
+ * Create an empty single tree export template
44
+ * @returns {SingleTreeExportInterface} an empty single tree export template
45
+ */
46
+
47
+ export function createSingleTreeExportTemplate() {
27
48
  return {
28
49
  meta: {},
29
50
  innerNodes: {},
@@ -37,8 +58,12 @@ export function createSingleJourneyExportTemplate() {
37
58
  tree: {}
38
59
  };
39
60
  }
40
- // use a function vs a template variable to avoid problems in loops
41
- export function createMultiJourneyExportTemplate() {
61
+ /**
62
+ * Create an empty multi tree export template
63
+ * @returns {MultiTreeExportInterface} an empty multi tree export template
64
+ */
65
+
66
+ export function createMultiTreeExportTemplate() {
42
67
  return {
43
68
  meta: {},
44
69
  trees: {}
@@ -126,39 +151,22 @@ async function getSaml2NodeDependencies(nodeObject, allProviders, allCirclesOfTr
126
151
  };
127
152
  return saml2NodeDependencies;
128
153
  }
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
-
154
+ }
149
155
  /**
150
156
  * 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
157
  * @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
158
+ * @param {TreeExportOptions} options export options
159
+ * @returns {Promise<SingleTreeExportInterface>} a promise that resolves to an object containing the tree and all its nodes and dependencies
154
160
  */
161
+
162
+
155
163
  export async function exportJourney(treeId, options = {
156
164
  useStringArrays: true,
157
165
  deps: true,
158
166
  verbose: false
159
167
  }) {
160
168
  const treeObject = await getTree(treeId);
161
- const exportData = createSingleJourneyExportTemplate();
169
+ const exportData = createSingleTreeExportTemplate();
162
170
  const {
163
171
  useStringArrays,
164
172
  deps,
@@ -456,7 +464,7 @@ export async function getJourneys() {
456
464
  * Export journey by id/name to file
457
465
  * @param {string} journeyId journey id/name
458
466
  * @param {string} file optional export file name
459
- * @param {ExportOptions} options export options
467
+ * @param {TreeExportOptions} options export options
460
468
  */
461
469
 
462
470
  export async function exportJourneyToFile(journeyId, file, options) {
@@ -484,10 +492,14 @@ export async function exportJourneyToFile(journeyId, file, options) {
484
492
  /**
485
493
  * Export all journeys to file
486
494
  * @param {string} file optional export file name
487
- * @param {ExportOptions} options export options
495
+ * @param {TreeExportOptions} options export options
488
496
  */
489
497
 
490
- export async function exportJourneysToFile(file, options) {
498
+ export async function exportJourneysToFile(file, options = {
499
+ deps: false,
500
+ useStringArrays: false,
501
+ verbose: false
502
+ }) {
491
503
  let fileName = file;
492
504
 
493
505
  if (!fileName) {
@@ -495,7 +507,7 @@ export async function exportJourneysToFile(file, options) {
495
507
  }
496
508
 
497
509
  const trees = (await getTrees()).result;
498
- const fileData = createMultiJourneyExportTemplate();
510
+ const fileData = createMultiTreeExportTemplate();
499
511
  createProgressIndicator(trees.length, 'Exporting journeys...');
500
512
 
501
513
  for (const tree of trees) {
@@ -515,7 +527,7 @@ export async function exportJourneysToFile(file, options) {
515
527
  }
516
528
  /**
517
529
  * Export all journeys to separate files
518
- * @param {ExportOptions} options export options
530
+ * @param {TreeExportOptions} options export options
519
531
  */
520
532
 
521
533
  export async function exportJourneysToFiles(options) {
@@ -535,15 +547,12 @@ export async function exportJourneysToFiles(options) {
535
547
 
536
548
  stopProgressIndicator('Done');
537
549
  }
538
- /**
539
- * Import options
540
- */
541
-
542
550
  /**
543
551
  * 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
552
+ * @param {SingleTreeExportInterface} treeObject tree object containing tree and all its dependencies
553
+ * @param {TreeImportOptions} options import options
546
554
  */
555
+
547
556
  export async function importJourney(treeObject, options) {
548
557
  const {
549
558
  reUuid,
@@ -795,7 +804,7 @@ export async function importJourney(treeObject, options) {
795
804
  // and the node's identityResource is the same as the tree's identityResource
796
805
  // change it to the current realm managed user identityResource otherwise leave it alone.
797
806
 
798
- if (nodeData['identityResource'] && nodeData['identityResource'].endsWith('user') && nodeData['identityResource'] === treeObject.tree.identityResource) {
807
+ if (nodeData.identityResource && nodeData.identityResource.endsWith('user') && nodeData.identityResource === treeObject.tree.identityResource) {
799
808
  nodeData['identityResource'] = `managed/${getRealmManagedUser()}`;
800
809
  if (verbose) printMessage(`\n - identityResource: ${nodeData['identityResource']}`, 'info', false);
801
810
  }
@@ -868,7 +877,7 @@ export async function importJourney(treeObject, options) {
868
877
  var _importError$response9, _importError$response10;
869
878
 
870
879
  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);
880
+ debugMessage(((_importError$response10 = importError.response) === null || _importError$response10 === void 0 ? void 0 : _importError$response10.data) || importError);
872
881
  throw new Error(`\nError importing journey flow ${treeId}`);
873
882
  }
874
883
  }
@@ -940,7 +949,7 @@ async function resolveDependencies(installedJorneys, journeyMap, unresolvedJourn
940
949
  * Import a journey from file
941
950
  * @param {string} journeyId journey id/name
942
951
  * @param {string} file import file name
943
- * @param {ImportOptions} options import options
952
+ * @param {TreeImportOptions} options import options
944
953
  */
945
954
 
946
955
 
@@ -996,7 +1005,7 @@ export async function importJourneyFromFile(journeyId, file, options) {
996
1005
  /**
997
1006
  * Import first journey from file
998
1007
  * @param {string} file import file name
999
- * @param {ImportOptions} options import options
1008
+ * @param {TreeImportOptions} options import options
1000
1009
  */
1001
1010
 
1002
1011
  export async function importFirstJourneyFromFile(file, options) {
@@ -1061,7 +1070,7 @@ export async function importFirstJourneyFromFile(file, options) {
1061
1070
  /**
1062
1071
  * Helper to import multiple trees from a tree map
1063
1072
  * @param {Object} treesMap map of trees object
1064
- * @param {ImportOptions} options import options
1073
+ * @param {TreeImportOptions} options import options
1065
1074
  */
1066
1075
 
1067
1076
  async function importAllJourneys(treesMap, options) {
@@ -1098,7 +1107,7 @@ async function importAllJourneys(treesMap, options) {
1098
1107
  /**
1099
1108
  * Import all journeys from file
1100
1109
  * @param {string} file import file name
1101
- * @param {ImportOptions} options import options
1110
+ * @param {TreeImportOptions} options import options
1102
1111
  */
1103
1112
 
1104
1113
 
@@ -1111,7 +1120,7 @@ export async function importJourneysFromFile(file, options) {
1111
1120
  }
1112
1121
  /**
1113
1122
  * Import all journeys from separate files
1114
- * @param {ImportOptions} options import options
1123
+ * @param {TreeImportOptions} options import options
1115
1124
  */
1116
1125
 
1117
1126
  export async function importJourneysFromFiles(options) {
@@ -1129,92 +1138,270 @@ export async function importJourneysFromFiles(options) {
1129
1138
  importAllJourneys(allJourneysData.trees, options);
1130
1139
  }
1131
1140
  /**
1132
- * Describe a journey
1133
- * @param {SingleJourneyExportTemplate} journeyData journey export object
1141
+ * Get the node reference obbject for a node object. Node reference objects
1142
+ * are used in a tree flow definition and within page nodes to reference
1143
+ * nodes. Among other things, node references contain all the non-configuration
1144
+ * meta data that exists for readaility, like the x/y coordinates of the node
1145
+ * and the display name chosen by the tree designer. The dislay name is the
1146
+ * only intuitive link between the graphical representation of the tree and
1147
+ * the node configurations that make up the tree.
1148
+ * @param nodeObj node object to retrieve the node reference object for
1149
+ * @param singleTreeExport tree export with or without dependencies
1150
+ * @returns {NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface} node reference object
1134
1151
  */
1135
1152
 
1136
- export function describeJourney(journeyData) {
1137
- var _journeyData$themes;
1153
+ export function getNodeRef(nodeObj, singleTreeExport) {
1154
+ if (singleTreeExport.tree.nodes[nodeObj._id]) {
1155
+ return singleTreeExport.tree.nodes[nodeObj._id];
1156
+ } else {
1157
+ for (const node of Object.values(singleTreeExport.nodes)) {
1158
+ if (containerNodes.includes(node._type._id)) {
1159
+ for (const nodeRef of node.nodes) {
1160
+ if (nodeRef._id === nodeObj._id) {
1161
+ return nodeRef;
1162
+ }
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1138
1167
 
1139
- const nodeTypeMap = {};
1168
+ return undefined;
1169
+ }
1170
+ /**
1171
+ * Default tree export resolver used to resolve a tree id/name to a full export
1172
+ * w/o dependencies of that tree from a platform instance.
1173
+ * @param {string} treeId id/name of the tree to resolve
1174
+ * @returns {TreeExportResolverInterface} tree export
1175
+ */
1140
1176
 
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;
1177
+ export const onlineTreeExportResolver = async function (treeId) {
1178
+ debugMessage(`onlineTreeExportResolver(${treeId})`);
1179
+ return await exportJourney(treeId, {
1180
+ deps: false,
1181
+ useStringArrays: false,
1182
+ verbose: false
1183
+ });
1184
+ };
1185
+ /**
1186
+ * Tree export resolver used to resolve a tree id/name to a full export
1187
+ * of that tree from individual `treename.journey.json` export files.
1188
+ * @param {string} treeId id/name of the tree to resolve
1189
+ * @returns {TreeExportResolverInterface} tree export
1190
+ */
1191
+
1192
+ export const fileByIdTreeExportResolver = async function (treeId) {
1193
+ debugMessage(`fileByIdTreeExportResolver(${treeId})`);
1194
+ let treeExport = createSingleTreeExportTemplate();
1195
+ const file = getTypedFilename(`${treeId}`, 'journey');
1196
+ debugMessage(`fileByIdTreeExportResolver: resolving '${treeId}' to ${file}`);
1197
+
1198
+ try {
1199
+ var _jsonData$tree;
1200
+
1201
+ const jsonData = JSON.parse(fs.readFileSync(file, 'utf8')); // did we resolve the tree we were asked to resolved?
1202
+
1203
+ if (((_jsonData$tree = jsonData.tree) === null || _jsonData$tree === void 0 ? void 0 : _jsonData$tree._id) === treeId) {
1204
+ treeExport = jsonData;
1205
+ } // check if this is a file with multiple trees and get journey by id
1206
+ else if (jsonData.trees && jsonData.trees[treeId]) {
1207
+ treeExport = jsonData.trees[treeId];
1208
+ }
1209
+ } catch (error) {//
1210
+ }
1211
+
1212
+ return treeExport;
1213
+ };
1214
+ /**
1215
+ * Factory that creates a tree export resolver used to resolve a tree id
1216
+ * to a full export of that tree from a multi-tree export file.
1217
+ * @param {string} file multi-tree export file
1218
+ * @returns {TreeExportResolverInterface} tree export resolver
1219
+ */
1220
+
1221
+ export function createFileParamTreeExportResolver(file) {
1222
+ const fileParamTreeExportResolver = async function (treeId) {
1223
+ debugMessage(`fileParamTreeExportResolver(${treeId})`);
1224
+ let treeExport = createSingleTreeExportTemplate();
1225
+
1226
+ try {
1227
+ var _jsonData$tree2;
1228
+
1229
+ const jsonData = JSON.parse(fs.readFileSync(file, 'utf8')); // did we resolve the tree we were asked to resolved?
1230
+
1231
+ if (((_jsonData$tree2 = jsonData.tree) === null || _jsonData$tree2 === void 0 ? void 0 : _jsonData$tree2._id) === treeId) {
1232
+ treeExport = jsonData;
1233
+ } // check if this is a file with multiple trees and get journey by id
1234
+ else if (jsonData.trees && jsonData.trees[treeId]) {
1235
+ treeExport = jsonData.trees[treeId];
1236
+ } // fall back to fileByIdTreeExportResolver
1237
+ else {
1238
+ treeExport = await fileByIdTreeExportResolver(treeId);
1239
+ }
1240
+ } catch (error) {//
1241
+ }
1242
+
1243
+ return treeExport;
1244
+ };
1245
+
1246
+ debugMessage('fileParamTreeExportResolver:');
1247
+ debugMessage(fileParamTreeExportResolver);
1248
+ return fileParamTreeExportResolver;
1249
+ }
1250
+ /**
1251
+ * Get tree dependencies (all descendent inner trees)
1252
+ * @param {SingleTreeExportInterface} treeExport single tree export
1253
+ * @param {string[]} resolvedTreeIds list of tree ids wich have already been resolved
1254
+ * @param {TreeExportResolverInterface} resolveTreeExport tree export resolver callback function
1255
+ * @returns {Promise<TreeDependencyMapInterface>} a promise that resolves to a tree dependency map
1256
+ */
1257
+
1258
+ export async function getTreeDescendents(treeExport, resolveTreeExport = onlineTreeExportResolver, resolvedTreeIds = []) {
1259
+ debugMessage(`getTreeDependencies(${treeExport.tree._id}, [${resolvedTreeIds.join(', ')}])`);
1260
+
1261
+ if (!resolvedTreeIds.includes(treeExport.tree._id)) {
1262
+ resolvedTreeIds.push(treeExport.tree._id);
1263
+ }
1264
+
1265
+ const treeDependencyMap = {
1266
+ [treeExport.tree._id]: []
1267
+ };
1268
+ const dependencies = [];
1269
+
1270
+ for (const [nodeId, node] of Object.entries(treeExport.tree.nodes)) {
1271
+ const innerTreeId = treeExport.nodes[nodeId].tree;
1272
+
1273
+ if (node.nodeType === 'InnerTreeEvaluatorNode' && !resolvedTreeIds.includes(innerTreeId)) {
1274
+ const innerTreeExport = await resolveTreeExport(innerTreeId);
1275
+ debugMessage(`resolved inner tree: ${innerTreeExport.tree._id}`); // resolvedTreeIds.push(innerTreeId);
1276
+
1277
+ dependencies.push(await getTreeDescendents(innerTreeExport, resolveTreeExport, resolvedTreeIds));
1146
1278
  }
1147
1279
  }
1148
1280
 
1149
- for (const nodeData of Object.values(journeyData.innerNodes)) {
1150
- if (nodeTypeMap[nodeData['_type']['_id']]) {
1151
- nodeTypeMap[nodeData['_type']['_id']] += 1;
1281
+ treeDependencyMap[treeExport.tree._id] = dependencies;
1282
+ return treeDependencyMap;
1283
+ }
1284
+
1285
+ function describeTreeDescendents(descendents, depth = 0) {
1286
+ if ([Object.values(descendents)].length) {
1287
+ // heading
1288
+ if (depth === 0) {
1289
+ printMessage(`\nInner Tree Dependencies (${Object.values(descendents)[0].length}):`, 'data');
1290
+ }
1291
+
1292
+ const indent = Array(depth * 2).fill(' ').join('');
1293
+ const [tree] = Object.keys(descendents);
1294
+ printMessage(`${indent}- ${tree}`, 'data');
1295
+
1296
+ for (const descendent of descendents[tree]) {
1297
+ describeTreeDescendents(descendent, depth + 1);
1298
+ }
1299
+ }
1300
+ }
1301
+ /**
1302
+ * Describe a journey:
1303
+ * - Properties, tags, description, name, metadata
1304
+ * - Inner tree dependency tree
1305
+ * - Node type summary
1306
+ * - Nodes
1307
+ * - Themes
1308
+ * - Scripts
1309
+ * - Email templates
1310
+ * - Social identity providers
1311
+ * - SAML2 entity providers
1312
+ * - SAML2 circles of trust
1313
+ * @param {SingleTreeExportInterface} journeyData journey export object
1314
+ * @param {TreeExportResolverInterface} resolveTreeExport tree export resolver callback function
1315
+ */
1316
+
1317
+
1318
+ export async function describeJourney(journeyData, resolveTreeExport = onlineTreeExportResolver) {
1319
+ var _journeyData$themes;
1320
+
1321
+ const allNodes = { ...journeyData.nodes,
1322
+ ...journeyData.innerNodes
1323
+ };
1324
+ const nodeTypeMap = {};
1325
+
1326
+ for (const nodeData of Object.values(allNodes)) {
1327
+ if (nodeTypeMap[nodeData._type._id]) {
1328
+ nodeTypeMap[nodeData._type._id] += 1;
1152
1329
  } else {
1153
- nodeTypeMap[nodeData['_type']['_id']] = 1;
1330
+ nodeTypeMap[nodeData._type._id] = 1;
1154
1331
  }
1155
1332
  }
1156
1333
 
1157
- printMessage(`\n${journeyData.tree._id}`, 'data');
1158
- const line = Array(journeyData.tree._id['length']).fill('=').join('');
1159
- printMessage(line);
1334
+ printMessage(`${getOneLineDescription(journeyData.tree)}`, 'data');
1335
+ printMessage(Array(`[${journeyData.tree._id}]`['length']).fill('=').join(''));
1160
1336
 
1161
1337
  if (journeyData.tree.description) {
1162
- printMessage(`\n${journeyData.tree.description}\n`, 'data');
1338
+ printMessage(`\n${journeyData.tree.description['brightYellow']}`, 'data');
1163
1339
  }
1164
1340
 
1165
- printMessage('\nNodes:', 'data');
1341
+ const descendents = await getTreeDescendents(journeyData, resolveTreeExport);
1342
+ describeTreeDescendents(descendents);
1166
1343
 
1167
1344
  if (Object.entries(nodeTypeMap).length) {
1345
+ printMessage(`\nNode Types (${Object.entries(nodeTypeMap).length}):`, 'data');
1346
+
1168
1347
  for (const [name, count] of Object.entries(nodeTypeMap)) {
1169
- printMessage(`- [${String(count)['brightCyan']}] ${name}`, 'data');
1348
+ printMessage(`- ${String(count)} [${name['brightCyan']}]`, 'data');
1349
+ }
1350
+ }
1351
+
1352
+ if (Object.entries(allNodes).length) {
1353
+ printMessage(`\nNodes (${Object.entries(allNodes).length}):`, 'data');
1354
+
1355
+ for (const nodeObj of Object.values(allNodes)) {
1356
+ printMessage(`- ${Node.getOneLineDescription(nodeObj, getNodeRef(nodeObj, journeyData))}`, 'data');
1170
1357
  }
1171
1358
  }
1172
1359
 
1173
1360
  if ((_journeyData$themes = journeyData.themes) !== null && _journeyData$themes !== void 0 && _journeyData$themes.length) {
1174
- printMessage('\nThemes:', 'data');
1361
+ printMessage(`\nThemes (${journeyData.themes.length}):`, 'data');
1175
1362
 
1176
1363
  for (const themeData of journeyData.themes) {
1177
- printMessage(`- [${themeData['_id']['brightCyan']}] ${themeData['name']}`, 'data');
1364
+ printMessage(`- ${Theme.getOneLineDescription(themeData)}`, 'data');
1178
1365
  }
1179
1366
  }
1180
1367
 
1181
1368
  if (Object.entries(journeyData.scripts).length) {
1182
- printMessage('\nScripts:', 'data');
1369
+ printMessage(`\nScripts (${Object.entries(journeyData.scripts).length}):`, 'data');
1183
1370
 
1184
1371
  for (const scriptData of Object.values(journeyData.scripts)) {
1185
- printMessage(`- [${scriptData['_id'].brightCyan}] ${scriptData['name']}`, 'data');
1372
+ printMessage(`- ${Script.getOneLineDescription(scriptData)}`, 'data');
1186
1373
  }
1187
1374
  }
1188
1375
 
1189
1376
  if (Object.entries(journeyData.emailTemplates).length) {
1190
- printMessage('\nEmail Templates:', 'data');
1377
+ printMessage(`\nEmail Templates (${Object.entries(journeyData.emailTemplates).length}):`, 'data');
1191
1378
 
1192
1379
  for (const templateData of Object.values(journeyData.emailTemplates)) {
1193
- printMessage(`- ${templateData['_id'].split('/')[1]}`, 'data');
1380
+ printMessage(`- ${EmailTemplate.getOneLineDescription(templateData)}`, 'data');
1194
1381
  }
1195
1382
  }
1196
1383
 
1197
1384
  if (Object.entries(journeyData.socialIdentityProviders).length) {
1198
- printMessage('\nSocial Identity Providers:', 'data');
1385
+ printMessage(`\nSocial Identity Providers (${Object.entries(journeyData.socialIdentityProviders).length}):`, 'data');
1199
1386
 
1200
1387
  for (const socialIdpData of Object.values(journeyData.socialIdentityProviders)) {
1201
- printMessage(`- ${socialIdpData['_id']} [${socialIdpData['_type']['_id'].brightCyan}]`, 'data');
1388
+ printMessage(`- ${Idp.getOneLineDescription(socialIdpData)}`, 'data');
1202
1389
  }
1203
1390
  }
1204
1391
 
1205
1392
  if (Object.entries(journeyData.saml2Entities).length) {
1206
- printMessage('\nSAML2 Entity Providers:', 'data');
1393
+ printMessage(`\nSAML2 Entity Providers (${Object.entries(journeyData.saml2Entities).length}):`, 'data');
1207
1394
 
1208
1395
  for (const entityProviderData of Object.values(journeyData.saml2Entities)) {
1209
- printMessage(`- ${entityProviderData['entityId']}`, 'data');
1396
+ printMessage(`- ${Saml2.getOneLineDescription(entityProviderData)}`, 'data');
1210
1397
  }
1211
1398
  }
1212
1399
 
1213
1400
  if (Object.entries(journeyData.circlesOfTrust).length) {
1214
- printMessage('\nSAML2 Circles Of Trust:', 'data');
1401
+ printMessage(`\nSAML2 Circles Of Trust (${Object.entries(journeyData.circlesOfTrust).length}):`, 'data');
1215
1402
 
1216
1403
  for (const cotData of Object.values(journeyData.circlesOfTrust)) {
1217
- printMessage(`- ${cotData['_id']}`, 'data');
1404
+ printMessage(`- ${CirclesOfTrust.getOneLineDescription(cotData)}`, 'data');
1218
1405
  }
1219
1406
  }
1220
1407
  }
@@ -1322,7 +1509,7 @@ export async function removeOrphanedNodes(orphanedNodes) {
1322
1509
  }
1323
1510
  /**
1324
1511
  * Analyze if a journey contains any custom nodes considering the detected or the overridden version.
1325
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1512
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1326
1513
  * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.
1327
1514
  */
1328
1515
 
@@ -1339,7 +1526,7 @@ export function isCustomJourney(journey) {
1339
1526
  }
1340
1527
  /**
1341
1528
  * Analyze if a journey contains any premium nodes considering the detected or the overridden version.
1342
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1529
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1343
1530
  * @returns {boolean} True if the journey/tree contains any custom nodes, false otherwise.
1344
1531
  */
1345
1532
 
@@ -1356,7 +1543,7 @@ export function isPremiumJourney(journey) {
1356
1543
  }
1357
1544
  /**
1358
1545
  * Analyze if a journey contains any cloud-only nodes considering the detected or the overridden version.
1359
- * @param {SingleJourneyExportTemplate} journey Journey/tree configuration object
1546
+ * @param {SingleTreeExportInterface} journey Journey/tree configuration object
1360
1547
  * @returns {boolean} True if the journey/tree contains any cloud-only nodes, false otherwise.
1361
1548
  */
1362
1549
 
@@ -1376,7 +1563,7 @@ export function isCloudOnlyJourney(journey) {
1376
1563
  * - standard: can run on any instance of a ForgeRock platform
1377
1564
  * - cloud: utilize nodes, which are exclusively available in the ForgeRock Identity Cloud
1378
1565
  * - premium: utilizes nodes, which come at a premium
1379
- * @param {SingleJourneyExportTemplate} journey journey export data
1566
+ * @param {SingleTreeExportInterface} journey journey export data
1380
1567
  * @returns {string[]} an array of one or multiple classifications
1381
1568
  */
1382
1569
 
@@ -9,6 +9,17 @@ const containerNodes = ['PageNode', 'CustomPageNode'];
9
9
  const scriptedNodes = ['ConfigProviderNode', 'ScriptedDecisionNode', 'ClientScriptNode', 'SocialProviderHandlerNode', 'CustomScriptNode'];
10
10
  const emailTemplateNodes = ['EmailSuspendNode', 'EmailTemplateNode'];
11
11
  const emptyScriptPlaceholder = '[Empty]';
12
+ /**
13
+ * Get a one-line description of the node
14
+ * @param {NodeSkeleton} nodeObj node object to describe
15
+ * @param {NodeRefSkeletonInterface | InnerNodeRefSkeletonInterface} nodeRef node reference object
16
+ * @returns {string} a one-line description
17
+ */
18
+
19
+ export function getOneLineDescription(nodeObj, nodeRef) {
20
+ const description = `[${nodeObj._id['brightCyan']}] ${nodeObj._type._id}${nodeRef ? ' - ' + (nodeRef === null || nodeRef === void 0 ? void 0 : nodeRef.displayName['brightYellow']) : ''}`;
21
+ return description;
22
+ }
12
23
  /**
13
24
  * Helper to get all SAML2 dependencies for a given node object
14
25
  * @param {Object} nodeObject node object
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=OpsTypes.js.map
@@ -6,12 +6,30 @@ 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',
13
13
  xacmlPolicyEnforcementPoint: 'XACML PEP'
14
- }; // use a function vs a template variable to avoid problems in loops
14
+ };
15
+ /**
16
+ * Get a one-line description of the saml2 provider object
17
+ * @param {Saml2ProviderSkeleton} saml2ProviderObj saml2 provider object to describe
18
+ * @returns {string} a one-line description
19
+ */
20
+
21
+ export function getOneLineDescription(saml2ProviderObj) {
22
+ const roles = [];
23
+
24
+ for (const [key, value] of Object.entries(roleMap)) {
25
+ if (saml2ProviderObj[key]) {
26
+ roles.push(value);
27
+ }
28
+ }
29
+
30
+ const description = `[${saml2ProviderObj.entityId['brightCyan']}]${' (' + saml2ProviderObj.entityLocation}${roles.length ? ' ' + roles.join(', ') + ')' : ')'}`;
31
+ return description;
32
+ } // use a function vs a template variable to avoid problems in loops
15
33
 
16
34
  function getFileDataTemplate() {
17
35
  return {
@@ -6,6 +6,16 @@ import { getScriptByName, getScripts, putScript } from '../api/ScriptApi';
6
6
  import wordwrap from './utils/Wordwrap';
7
7
  import { convertBase64TextToArray, convertTextArrayToBase64, getTypedFilename, saveToFile, titleCase, validateImport } from './utils/ExportImportUtils';
8
8
  import storage from '../storage/SessionStorage';
9
+
10
+ /**
11
+ * Get a one-line description of the script object
12
+ * @param {ScriptSkeleton} scriptObj script object to describe
13
+ * @returns {string} a one-line description
14
+ */
15
+ export function getOneLineDescription(scriptObj) {
16
+ const description = `[${scriptObj._id['brightCyan']}] ${scriptObj.context} - ${scriptObj.name['brightYellow']}`;
17
+ return description;
18
+ }
9
19
  /**
10
20
  * List scripts
11
21
  */
@@ -2,6 +2,16 @@ import fs from 'fs';
2
2
  import { deleteTheme, deleteThemeByName, deleteThemes, getTheme, getThemeByName, getThemes, putTheme, putThemeByName, putThemes } from '../api/ThemeApi';
3
3
  import { createProgressIndicator, createTable, printMessage, stopProgressIndicator, updateProgressIndicator } from './utils/Console';
4
4
  import { getRealmString, getTypedFilename, saveToFile, validateImport } from './utils/ExportImportUtils';
5
+ /**
6
+ * Get a one-line description of the theme
7
+ * @param {ThemeSkeleton} themeObj theme object to describe
8
+ * @returns {string} a one-line description
9
+ */
10
+
11
+ export function getOneLineDescription(themeObj) {
12
+ const description = `[${themeObj._id['brightCyan']}] ${themeObj.name['brightYellow']}${themeObj.linkedTrees ? ' (' + themeObj.linkedTrees.join(', ')['brightCyan'] + ')' : ''}`;
13
+ return description;
14
+ }
5
15
  /**
6
16
  * List all the themes
7
17
  * @param {boolean} long Long version, more fields