From a2e58933f1874274a07f52ce000760d85fdb63a5 Mon Sep 17 00:00:00 2001 From: Preston Hales Date: Fri, 20 Sep 2024 12:15:09 -0600 Subject: [PATCH 1/2] Improve script usage commands to successfully determine usage of scripts in other scripts --- src/ops/ScriptOps.ts | 72 ++++++++++++++++--- .../script-list.e2e.test.js.snap | 24 +++---- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/ops/ScriptOps.ts b/src/ops/ScriptOps.ts index f895850d9..c7f07c94b 100644 --- a/src/ops/ScriptOps.ts +++ b/src/ops/ScriptOps.ts @@ -36,6 +36,7 @@ const { getFilePath, getWorkingDirectory, saveToFile, + decodeBase64, } = frodo.utils; const { readScript, @@ -52,6 +53,10 @@ const { const langMap = { JAVASCRIPT: 'JavaScript', GROOVY: 'Groovy' }; +type SeparatedScripts = { + realm: Record }>; +}; + /** * Get a one-line description of the script object * @param {ScriptSkeleton} scriptObj script object to describe @@ -128,6 +133,7 @@ export async function listScripts( return true; } let fullExport: FullExportInterface = null; + let scriptExport: SeparatedScripts = null; const headers = long ? ['Name', 'UUID', 'Language', 'Context', 'Description'] : ['Name']; @@ -138,10 +144,7 @@ export async function listScripts( printError(error); return false; } - //Delete scripts from full export so they aren't mistakenly used for determining usage - for (const realmExport of Object.values(fullExport.realm)) { - delete realmExport.script; - } + scriptExport = separateScriptsFromFullExport(fullExport); headers.push('Used'); } const table = createTable(headers); @@ -156,7 +159,9 @@ export async function listScripts( ] : [wordwrap(script.name, 25, ' ')]; if (usage) { - const locations = getIdLocations(fullExport, script._id, false); + const locations = getIdLocations(fullExport, script._id, false).concat( + getScriptLocations(scriptExport, script.name) + ); values.push( locations.length > 0 ? `${'yes'['brightGreen']} (${locations.length === 1 ? `at` : `${locations.length} uses, including:`} ${locations[0]})` @@ -205,11 +210,10 @@ export async function describeScript( if (usage) { try { const fullExport = await getFullExportConfig(file); - //Delete scripts from full export so they aren't mistakenly used for determining usage - for (const realmExport of Object.values(fullExport.realm)) { - delete realmExport.script; - } - script.locations = getIdLocations(fullExport, script._id, false); + const scriptExport = separateScriptsFromFullExport(fullExport); + script.locations = getIdLocations(fullExport, script._id, false).concat( + getScriptLocations(scriptExport, script.name) + ); } catch (error) { stopProgressIndicator( spinnerId, @@ -795,3 +799,51 @@ export async function deleteAllScripts(): Promise { } return false; } + +/** + * Helper that takes a full export and separates the scripts from it into their own export + * @param {FullExportInterface} fullExport The full export + * @returns {SeparatedScripts} The scripts separated from the fullExport + */ +function separateScriptsFromFullExport( + fullExport: FullExportInterface +): SeparatedScripts { + const scripts = { realm: {} }; + for (const [realm, realmExport] of Object.entries(fullExport.realm)) { + if (!scripts.realm[realm]) { + scripts.realm[realm] = {}; + } + scripts.realm[realm].script = realmExport.script; + delete realmExport.script; + } + return scripts; +} + +/** + * Helper that finds all locations where a script is being used as a library in another script + * @param {SeparatedScripts} configuration The scripts to search + * @param {string} scriptName The name of the script being searched for + */ +function getScriptLocations( + configuration: SeparatedScripts, + scriptName: string +): string[] { + const locations = []; + const regex = new RegExp(`require\\(['|"]${scriptName}['|"]\\)`); + for (const [realm, realmExport] of Object.entries(configuration.realm)) { + for (const scriptData of Object.values(realmExport.script)) { + let scriptString = scriptData.script as string; + if (Array.isArray(scriptData.script)) { + scriptString = scriptData.script.join('\n'); + } else if (isBase64Encoded(scriptData.script)) { + scriptString = decodeBase64(scriptData.script); + } + if (regex.test(scriptString)) { + locations.push( + `realm.${realm}.script.${scriptData._id}(name: '${scriptData.name}').script` + ); + } + } + } + return locations; +} diff --git a/test/e2e/__snapshots__/script-list.e2e.test.js.snap b/test/e2e/__snapshots__/script-list.e2e.test.js.snap index 0c7b5048b..112534c8a 100644 --- a/test/e2e/__snapshots__/script-list.e2e.test.js.snap +++ b/test/e2e/__snapshots__/script-list.e2e.test.js.snap @@ -76,7 +76,7 @@ Itsme Profile │3d97c436-42c0-4dd0-a571-ea6f34f752b3│Groovy │S Normalization │ │ │Transformation │from Itsme │ level │41c24257-d7fc-4654-8b46-c2666dc5b56d│JavaScript│Authentication Tree │set per level shared state │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) │ │ │Decision Node │variable │ -Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │no +Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) │ │ │ │to be referenced from other │ │ │ │ │scripts │ LinkedIn Profile │8862ca8f-7770-4af5-a888-ac0df0947f36│Groovy │Social Idp Profile │Normalizes raw profile data │no @@ -85,7 +85,7 @@ Microsoft Profile │73cecbfc-dad0-4395-be6a-6858ee3a80e5│Groovy │S Normalization │ │ │Transformation │from Microsoft │ mode │5bbdaeff-ddee-44b9-b608-8d413d7d65a6│JavaScript│Authentication Tree │Check if mode has already been│yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) │ │ │Decision Node │set. │ -My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │no +My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │bb393d07-a121-47e2-9d24-1a1066f39ec0│JavaScript│Authentication Tree │My Example Script Using │no Libraries │ │ │Decision Node │Libraries │ Normalized Profile to │ed685f9f-5909-4726-86e8-22bd38b47663│Groovy │Social Idp Profile │Converts a normalized social │no @@ -236,7 +236,7 @@ Itsme Profile │3d97c436-42c0-4dd0-a571-ea6f34f752b3│Groovy │S Normalization │ │ │Transformation │from Itsme │ level │41c24257-d7fc-4654-8b46-c2666dc5b56d│JavaScript│Authentication Tree │set per level shared state │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) │ │ │Decision Node │variable │ -Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │no +Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) │ │ │ │to be referenced from other │ │ │ │ │scripts │ LinkedIn Profile │8862ca8f-7770-4af5-a888-ac0df0947f36│Groovy │Social Idp Profile │Normalizes raw profile data │no @@ -245,7 +245,7 @@ Microsoft Profile │73cecbfc-dad0-4395-be6a-6858ee3a80e5│Groovy │S Normalization │ │ │Transformation │from Microsoft │ mode │5bbdaeff-ddee-44b9-b608-8d413d7d65a6│JavaScript│Authentication Tree │Check if mode has already been│yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) │ │ │Decision Node │set. │ -My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │no +My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │bb393d07-a121-47e2-9d24-1a1066f39ec0│JavaScript│Authentication Tree │My Example Script Using │no Libraries │ │ │Decision Node │Libraries │ Normalized Profile to │ed685f9f-5909-4726-86e8-22bd38b47663│Groovy │Social Idp Profile │Converts a normalized social │no @@ -622,7 +622,7 @@ Itsme Profile │3d97c436-42c0-4dd0-a571-ea6f34f752b3│Groovy │S Normalization │ │ │Transformation │from Itsme │ level │41c24257-d7fc-4654-8b46-c2666dc5b56d│JavaScript│Authentication Tree │set per level shared state │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) │ │ │Decision Node │variable │ -Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │no +Library Script │6c49bebe-3a62-11ed-a261-0242ac120002│JavaScript│Library │Default global library script │yes (3 uses, including: realm.root-alpha.script.6c49bebe-3a62-11ed-a261-0242ac120002(name: 'Library Script').script) │ │ │ │to be referenced from other │ │ │ │ │scripts │ LinkedIn Profile │8862ca8f-7770-4af5-a888-ac0df0947f36│Groovy │Social Idp Profile │Normalizes raw profile data │no @@ -631,7 +631,7 @@ Microsoft Profile │73cecbfc-dad0-4395-be6a-6858ee3a80e5│Groovy │S Normalization │ │ │Transformation │from Microsoft │ mode │5bbdaeff-ddee-44b9-b608-8d413d7d65a6│JavaScript│Authentication Tree │Check if mode has already been│yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) │ │ │Decision Node │set. │ -My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │no +My Example Library │2c38c998-aec0-4e56-8d46-bff6e24a704e│JavaScript│Library │My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │bb393d07-a121-47e2-9d24-1a1066f39ec0│JavaScript│Authentication Tree │My Example Script Using │no Libraries │ │ │Decision Node │Libraries │ Normalized Profile to │ed685f9f-5909-4726-86e8-22bd38b47663│Groovy │Social Idp Profile │Converts a normalized social │no @@ -773,13 +773,13 @@ Instagram Profile │no Itsme Profile │no Normalization │ level │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) -Library Script │no +Library Script │yes (3 uses, including: realm.root-alpha.script.6c49bebe-3a62-11ed-a261-0242ac120002(name: 'Library Script').script) LinkedIn Profile │no Normalization │ Microsoft Profile │yes (2 uses, including: realm.root-alpha.idp.azure.transform) Normalization │ mode │yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) -My Example Library │no +My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │no Libraries │ Normalized Profile to │no @@ -913,13 +913,13 @@ Instagram Profile │no Itsme Profile │no Normalization │ level │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) -Library Script │no +Library Script │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) LinkedIn Profile │no Normalization │ Microsoft Profile │yes (2 uses, including: realm.root-alpha.idp.azure.transform) Normalization │ mode │yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) -My Example Library │no +My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │no Libraries │ Normalized Profile to │no @@ -1053,13 +1053,13 @@ Instagram Profile │no Itsme Profile │no Normalization │ level │yes (22 uses, including: realm.root-alpha.trees.j00.nodes.39b48197-f4be-42b9-800a-866587b4b9b5.script) -Library Script │no +Library Script │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) LinkedIn Profile │no Normalization │ Microsoft Profile │yes (2 uses, including: realm.root-alpha.idp.azure.transform) Normalization │ mode │yes (11 uses, including: realm.root-alpha.trees.j00.nodes.513a2ab4-f0b8-4f94-b840-6fe14796cc84.script) -My Example Library │no +My Example Library │yes (at realm.root-alpha.script.bb393d07-a121-47e2-9d24-1a1066f39ec0(name: 'My Example Script Using Libraries').script) My Example Script Using │no Libraries │ Normalized Profile to │no From dc592508b9b88bb4ba6bae036b01bbee49e76c1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 21 Sep 2024 01:34:52 +0000 Subject: [PATCH 2/2] Updated changelog and version for release v2.0.6-2 --- CHANGELOG.md | 5 ++++- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e47085d..b6b1d0821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.0.6-2] - 2024-09-21 + ## [2.0.6-1] - 2024-09-09 ## [2.0.6-0] - 2024-08-26 @@ -1976,7 +1978,8 @@ Frodo CLI 2.x automatically refreshes session and access tokens before they expi - Fixed problem with adding connection profiles - Miscellaneous bug fixes -[unreleased]: https://github.com/rockcarver/frodo-cli/compare/v2.0.6-1...HEAD +[unreleased]: https://github.com/rockcarver/frodo-cli/compare/v2.0.6-2...HEAD +[2.0.6-2]: https://github.com/rockcarver/frodo-cli/compare/v2.0.6-1...v2.0.6-2 [2.0.6-1]: https://github.com/rockcarver/frodo-cli/compare/v2.0.6-0...v2.0.6-1 [2.0.6-0]: https://github.com/rockcarver/frodo-cli/compare/v2.0.5...v2.0.6-0 [2.0.5]: https://github.com/rockcarver/frodo-cli/compare/v2.0.5-0...v2.0.5 diff --git a/package-lock.json b/package-lock.json index 3e3be2aae..d5eadd1bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rockcarver/frodo-cli", - "version": "2.0.6-1", + "version": "2.0.6-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rockcarver/frodo-cli", - "version": "2.0.6-1", + "version": "2.0.6-2", "license": "MIT", "bin": { "frodo": "dist/launch.cjs" diff --git a/package.json b/package.json index 8545c8ed3..7b1539dc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rockcarver/frodo-cli", - "version": "2.0.6-1", + "version": "2.0.6-2", "type": "module", "description": "A command line interface to manage ForgeRock Identity Cloud tenants, ForgeOps deployments, and classic deployments.", "keywords": [