Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/cli/journey/journey-describe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export default function setup() {
"Override version. Notation: '<major>.<minor>.<patch>' e.g. '7.2.0'. Override detected version with any version. This is helpful in order to check if journeys in one environment would be compatible running in another environment (e.g. in preparation of migrating from on-prem to ForgeRock Identity Cloud."
)
)
.addOption(
new Option(
'-u, --usage',
'List all uses of the journey as an inner node for other journies.'))
.action(
// implement command logic inside action handler
async (host, realm, user, password, options, command) => {
Expand Down Expand Up @@ -119,7 +123,8 @@ export default function setup() {
if (!options.markdown) {
const outcome = await describeJourney(
journeyData,
createFileParamTreeExportResolver(options.file)
createFileParamTreeExportResolver(options.file),
options.usage
);
if (!outcome) process.exitCode = 1;
}
Expand Down Expand Up @@ -154,7 +159,7 @@ export default function setup() {
const treeData = await exportJourney(journey['_id']);
// ANSI text output
if (!options.markdown) {
const outcome = await describeJourney(treeData);
const outcome = await describeJourney(treeData, undefined, options.usage);
if (!outcome) process.exitCode = 1;
}
// Markdown output
Expand All @@ -175,7 +180,7 @@ export default function setup() {
const treeData = await exportJourney(options.journeyId);
// ANSI text output
if (!options.markdown) {
const outcome = await describeJourney(treeData);
const outcome = await describeJourney(treeData, undefined, options.usage);
if (!outcome) process.exitCode = 1;
}
// Markdown output
Expand Down
69 changes: 68 additions & 1 deletion src/ops/JourneyOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import fs from 'fs';

import {
createKeyValueTable,
createProgressIndicator,
createTable,
debugMessage,
Expand All @@ -30,6 +31,7 @@ import * as Script from './ScriptOps';
import * as Theme from './ThemeOps';
import { cloneDeep, errorHandler } from './utils/OpsUtils';
import wordwrap from './utils/Wordwrap';
import { getFullExportConfig } from '../utils/Config';

const {
getTypedFilename,
Expand Down Expand Up @@ -644,7 +646,8 @@ function describeTreeDescendentsMd(
*/
export async function describeJourney(
journeyData: SingleTreeExportInterface,
resolveTreeExport: TreeExportResolverInterface = onlineTreeExportResolver
resolveTreeExport: TreeExportResolverInterface = onlineTreeExportResolver,
usage = false
): Promise<boolean> {
const errors: Error[] = [];
try {
Expand Down Expand Up @@ -717,6 +720,34 @@ export async function describeJourney(
'data'
);
}
// Usage
if (usage) {
try {
const journeysExport = await exportJourneys({
useStringArrays: true,
deps: false,
coords: false,
});
let journey = journeyData as SingleTreeExportInterface & {
locations: string[];
};
const journeyName =
typeof journey.tree === 'string' ? journey.tree : journey.tree?._id;
journey.locations = getJourneyLocations(journeysExport, journeyName);
const table = createKeyValueTable();
table.push([
`Usage Locations (${journey.locations.length} total)`['brightCyan'],
journey.locations.length > 0 ? journey.locations[0] : '',
]);
for (let i = 1; i < journey.locations.length; i++) {
table.push(['', journey.locations[i]]);
}
printMessage(table.toString(), 'data');
return true;
} catch (error) {
return false;
}
}

// Dependency Tree
try {
Expand Down Expand Up @@ -1173,3 +1204,39 @@ export async function deleteJourneys(
}
return false;
}

/**
* Helper that finds all locations where a journey is being used as an inner journey in another journey
* @param journeysExport export data containing journeys
* @param journeyName ID of the journey to search for as an inner journey
*/
function getJourneyLocations(
journeysExport: MultiTreeExportInterface,
journeyName: string
): string[] {
const locations: string[] = [];
for (const journeyData of Object.values(journeysExport.trees)) {
interface InnerTreeNode {
_id: string;
_type?: { _id?: string };
tree?: string | { _id: string };
}

for (const node of Object.values(
journeyData.nodes ?? {}
) as InnerTreeNode[]) {
const innerTreeName =
typeof node.tree === 'string' ? node.tree : node.tree?._id;

if (
node._type?._id === 'InnerTreeEvaluatorNode' &&
innerTreeName === journeyName
) {
locations.push(
`journey.${journeyData.tree?._id ?? journeyData.tree._id}`
);
}
}
}
return locations;
}