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
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { frodo } from '@rockcarver/frodo-lib';
import { Option } from 'commander';

import { configManagerExportCustomNodes } from '../../../configManagerOps/FrConfigCustomNodesOps';
import { getTokens } from '../../../ops/AuthenticateOps';
import { verboseMessage } from '../../../utils/Console';
import { FrodoCommand } from '../../FrodoCommand';

const { CLOUD_DEPLOYMENT_TYPE_KEY, FORGEOPS_DEPLOYMENT_TYPE_KEY } =
frodo.utils.constants;

const deploymentTypes = [
CLOUD_DEPLOYMENT_TYPE_KEY,
FORGEOPS_DEPLOYMENT_TYPE_KEY,
];

export default function setup() {
const program = new FrodoCommand(
'frodo config-manager pull custom-nodes',
[],
deploymentTypes
);

program
.description('Export custom nodes.')
.addOption(
new Option(
'-n, --node-name <node-name>',
'Custom node display name. If specified, only one custom node is exported.'
)
)
.action(async (host, realm, user, password, options, command) => {
command.handleDefaultArgsAndOpts(
host,
realm,
user,
password,
options,
command
);

if (await getTokens(false, true, deploymentTypes)) {
if (options.nodeName) {
verboseMessage(
`Fetching custom node with name '${options.nodeName}'`
);
} else {
verboseMessage('Fetching custom nodes');
}
const outcome = await configManagerExportCustomNodes(options.nodeName);
if (!outcome) process.exitCode = 1;
} else {
process.exitCode = 1;
}
});

return program;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ConnectorMappings from './config-manager-pull-connector-mappings';
import CookieDomains from './config-manager-pull-cookie-domains';
import CORS from './config-manager-pull-cors';
import CSP from './config-manager-pull-csp';
import CustomNodes from './config-manager-pull-custom-nodes';
import EmailProvider from './config-manager-pull-email-provider';
import EmailTemplates from './config-manager-pull-email-templates';
import Endpoints from './config-manager-pull-endpoints';
Expand Down Expand Up @@ -52,6 +53,7 @@ export default function setup() {
program.addCommand(CookieDomains().name('cookie-domains'));
program.addCommand(CORS().name('cors'));
program.addCommand(CSP().name('csp'));
program.addCommand(CustomNodes().name('custom-nodes'));
program.addCommand(EmailProvider().name('email-provider'));
program.addCommand(EmailTemplates().name('email-templates'));
program.addCommand(Endpoints().name('endpoints'));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { frodo } from '@rockcarver/frodo-lib';
import { Option } from 'commander';

import { configManagerImportCustomNode } from '../../../configManagerOps/FrConfigCustomNodesOps';
import { getTokens } from '../../../ops/AuthenticateOps';
import { verboseMessage } from '../../../utils/Console';
import { FrodoCommand } from '../../FrodoCommand';

const { CLOUD_DEPLOYMENT_TYPE_KEY, FORGEOPS_DEPLOYMENT_TYPE_KEY } =
frodo.utils.constants;

const deploymentTypes = [
CLOUD_DEPLOYMENT_TYPE_KEY,
FORGEOPS_DEPLOYMENT_TYPE_KEY,
];

export default function setup() {
const program = new FrodoCommand(
'frodo config-manager pull custom-nodes',
[],
deploymentTypes
);

program
.description('Export custom nodes.')
.addOption(
new Option(
'-n, --node-name <node-name>',
'Custom node display name. If specified, only one custom node is exported.'
)
)
.action(async (host, realm, user, password, options, command) => {
command.handleDefaultArgsAndOpts(
host,
realm,
user,
password,
options,
command
);

if (await getTokens(false, true, deploymentTypes)) {
if (options.nodeName) {
verboseMessage(
`Fetching custom node with name '${options.nodeName}'`
);
} else {
verboseMessage('Fetching custom nodes');
}
const outcome = await configManagerImportCustomNode(options.nodeName);
if (!outcome) process.exitCode = 1;
} else {
process.exitCode = 1;
}
});

return program;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { FrodoStubCommand } from '../../FrodoCommand';
import AccessConfig from './config-manager-push-access-config';
import Audit from './config-manager-push-audit';
import CookieDomains from './config-manager-push-cookie-domain';
import CustomNodes from './config-manager-push-custom-nodes';
import EmailProvider from './config-manager-push-email-provider';
import EmailTemplates from './config-manager-push-email-templates';
import Endpoints from './config-manager-push-endpoints';
Expand Down Expand Up @@ -39,6 +40,6 @@ export default function setup() {
program.addCommand(CookieDomains().name('cookie-domains'));
program.addCommand(ServiceObjects().name('service-objects'));
program.addCommand(UiConfig().name('ui-config'));

program.addCommand(CustomNodes().name('custom-nodes'));
return program;
}
100 changes: 100 additions & 0 deletions src/configManagerOps/FrConfigCustomNodesOps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { frodo } from '@rockcarver/frodo-lib';
import fs from 'fs';

import { printError } from '../utils/Console';

const { saveJsonToFile, getFilePath, saveTextToFile } = frodo.utils;
const { readCustomNode, readCustomNodes, importCustomNodes } = frodo.authn.node;

/**
* Export all custom nodes to 'custom-nodes/nodes' directory.
* Each custom node will be exported as a JSON file with a reference to its script file.
* The script content will be saved in a separate .js file.
* @param {string} name Optional display name of a custom node to export. If not provided, all custom nodes will be exported.
* @returns {Promise<boolean>} True if export was successful
*/
export async function configManagerExportCustomNodes(
name?: string
): Promise<boolean> {
try {
let customNodes;
if (name) {
const customNode = await readCustomNode(undefined, name);
customNodes = [customNode];
} else {
customNodes = await readCustomNodes();
}

for (const node of customNodes) {
const nodeDir = getFilePath(
`custom-nodes/nodes/${node.displayName}/`,
true
);
const scriptFileName = `${node.displayName}.js`;
const jsonFileName = `${node.displayName}.json`;

saveTextToFile(`${node.script}`, nodeDir + scriptFileName);
node.script = { file: scriptFileName };

const filePath = nodeDir + jsonFileName;
saveJsonToFile(node, filePath, false);
}

return true;
} catch (error) {
printError(error);
return false;
}
}

/**
* Import all custom nodes to specified tenant.
* @param {string} name Optional display name of a custom node to import. If not provided, all custom nodes will be exported.
* @returns {Promise<boolean>} True if export was successful
*/

export async function configManagerImportCustomNode(
nodeName?: string
): Promise<boolean> {
try {
if (nodeName) {
const nodeDir = getFilePath(`custom-nodes/nodes/${nodeName}`);
const jsonFilePath = `${nodeDir}/${nodeName}.json`;
const scriptFilePath = `${nodeDir}/${nodeName}.js`;
const customNodeData = { nodeTypes: {} };

const importData = JSON.parse(fs.readFileSync(jsonFilePath, 'utf8'));

if (fs.existsSync(scriptFilePath)) {
importData.script = fs.readFileSync(scriptFilePath, 'utf8');
}

customNodeData.nodeTypes[importData._id] = importData;
await importCustomNodes(undefined, nodeName, customNodeData);
} else {
const nodesDir = getFilePath(`custom-nodes/nodes`);
const nodeFolders = fs.readdirSync(nodesDir);
const customNodeData = { nodeTypes: {} };

for (const nodeFolder of nodeFolders) {
const jsonFilePath = `${nodesDir}/${nodeFolder}/${nodeFolder}.json`;
const scriptFilePath = `${nodesDir}/${nodeFolder}/${nodeFolder}.js`;

const importData = JSON.parse(fs.readFileSync(jsonFilePath, 'utf8'));

if (fs.existsSync(scriptFilePath)) {
importData.script = fs.readFileSync(scriptFilePath, 'utf8');
}

customNodeData.nodeTypes[importData._id] = importData;

await importCustomNodes(undefined, nodeName, customNodeData);
}
}

return true;
} catch (error) {
printError(error, `Error importing custom nodes`);
}
return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CLI help interface for 'config-manager pull custom-nodes' should be expected english 1`] = `
"Usage: frodo config-manager pull custom-nodes [options] [host] [realm] [username] [password]

[Experimental] Export custom nodes.

Arguments:
host AM base URL, e.g.:
https://cdk.iam.example.com/am. To use a
connection profile, just specify a unique
substring or alias.
realm Realm. Specify realm as '/' for the root realm or
'realm' or '/parent/child' otherwise. (default:
"alpha" for Identity Cloud tenants, "/"
otherwise.)
username Username to login with. Must be an admin user
with appropriate rights to manage authentication
journeys/trees.
password Password.

Options:
-n, --node-name <node-name> Custom node display name. If specified, only one
custom node is exported.
-h, --help Help
-hh, --help-more Help with all options.
-hhh, --help-all Help with all options, environment variables, and
usage examples.
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CLI help interface for 'config-manager push custom-nodes' should be expected english 1`] = `
"Usage: frodo config-manager push custom-nodes [options] [host] [realm] [username] [password]

[Experimental] Export custom nodes.

Arguments:
host AM base URL, e.g.:
https://cdk.iam.example.com/am. To use a
connection profile, just specify a unique
substring or alias.
realm Realm. Specify realm as '/' for the root realm or
'realm' or '/parent/child' otherwise. (default:
"alpha" for Identity Cloud tenants, "/"
otherwise.)
username Username to login with. Must be an admin user
with appropriate rights to manage authentication
journeys/trees.
password Password.

Options:
-n, --node-name <node-name> Custom node display name. If specified, only one
custom node is exported.
-h, --help Help
-hh, --help-more Help with all options.
-hhh, --help-all Help with all options, environment variables, and
usage examples.
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Commands:
access-config [Experimental] Import access configuration.
audit [Experimental] Import audit configuration.
cookie-domains [Experimental] Import cookie domains.
custom-nodes [Experimental] Export custom nodes.
email-provider [Experimental] Import email provider configuration.
email-templates [Experimental] Import email template objects.
endpoints [Experimental] Import custom endpoints objects.
Expand Down
10 changes: 10 additions & 0 deletions test/client_cli/en/config-manager-pull-custom-nodes.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import cp from 'child_process';
import { promisify } from 'util';

const exec = promisify(cp.exec);
const CMD = 'frodo config-manager pull custom-nodes --help';
const { stdout } = await exec(CMD);

test("CLI help interface for 'config-manager pull custom-nodes' should be expected english", async () => {
expect(stdout).toMatchSnapshot();
});
10 changes: 10 additions & 0 deletions test/client_cli/en/config-manager-push-custom-nodes.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import cp from 'child_process';
import { promisify } from 'util';

const exec = promisify(cp.exec);
const CMD = 'frodo config-manager push custom-nodes --help';
const { stdout } = await exec(CMD);

test("CLI help interface for 'config-manager push custom-nodes' should be expected english", async () => {
expect(stdout).toMatchSnapshot();
});
Loading