Skip to content

Commit

Permalink
add new option to separate managed objects into individual files
Browse files Browse the repository at this point in the history
  • Loading branch information
cparkertrivir committed Dec 6, 2024
1 parent 7bd9496 commit d451973
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 9 deletions.
7 changes: 7 additions & 0 deletions src/cli/config/config-export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ export default function setup() {
'Export sync.idm.json mappings separately in their own directory. Ignored with -a.'
)
)
.addOption(
new Option(
'-o, --separate-objects',
'Export managed.idm.json objects separately in their own directory. Ignored with -a.'
)
)
.addOption(
new Option(
'--include-active-values',
Expand Down Expand Up @@ -145,6 +151,7 @@ export default function setup() {
const outcome = await exportEverythingToFiles(
options.extract,
options.separateMappings,
options.separateObjects,
options.metadata,
{
useStringArrays: options.useStringArrays,
Expand Down
8 changes: 8 additions & 0 deletions src/cli/idm/idm-export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export default function setup() {
'Export sync.idm.json mappings separately in their own directory. Ignored with -a.'
)
)
.addOption(
new Option(
'-o, --separate-objects',
'Export managed.idm.json objects separately in their own directory. Ignored with -a.'
)
)
.addOption(
new Option(
'-N, --no-metadata',
Expand Down Expand Up @@ -95,6 +101,7 @@ export default function setup() {
options.file,
options.envFile,
options.separateMappings,
options.separateObjects,
options.metadata
);
if (!outcome) process.exitCode = 1;
Expand Down Expand Up @@ -136,6 +143,7 @@ export default function setup() {
options.entitiesFile,
options.envFile,
options.separateMappings,
options.separateObjects,
options.metadata
);
if (!outcome) process.exitCode = 1;
Expand Down
28 changes: 23 additions & 5 deletions src/ops/ConfigOps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
FullImportOptions,
FullRealmExportInterface,
} from '@rockcarver/frodo-lib/types/ops/ConfigOps';
import { SyncSkeleton } from '@rockcarver/frodo-lib/types/ops/MappingOps';
import {
ManagedSkeleton,
SyncSkeleton,
} from '@rockcarver/frodo-lib/types/ops/MappingOps';
import { ScriptExportInterface } from '@rockcarver/frodo-lib/types/ops/ScriptOps';
import fs from 'fs';

Expand All @@ -17,7 +20,10 @@ import {
} from '../utils/Config';
import { cleanupProgressIndicators, printError } from '../utils/Console';
import { saveServersToFiles } from './classic/ServerOps';
import { writeSyncJsonToDirectory } from './MappingOps';
import {
writeManagedJsonToDirectory,
writeSyncJsonToDirectory,
} from './MappingOps';
import { extractScriptsToFiles } from './ScriptOps';

const {
Expand Down Expand Up @@ -72,13 +78,15 @@ export async function exportEverythingToFile(
* Export everything to separate files
* @param {boolean} extract Extracts the scripts from the exports into separate files if true
* @param {boolean} separateMappings separate sync.idm.json mappings if true, otherwise keep them in a single file
* @param {boolean} separateObjects separate managed.idm.json objects if true, otherwise keep them in a single file
* @param {boolean} includeMeta true to include metadata, false otherwise. Default: true
* @param {FullExportOptions} options export options
* @return {Promise<boolean>} a promise that resolves to true if successful, false otherwise
*/
export async function exportEverythingToFiles(
extract: boolean = false,
separateMappings: boolean = false,
separateObjects: boolean = false,
includeMeta: boolean = true,
options: FullExportOptions = {
useStringArrays: true,
Expand Down Expand Up @@ -106,7 +114,8 @@ export async function exportEverythingToFiles(
`${baseDirectory}/global`,
includeMeta,
extract,
separateMappings
separateMappings,
separateObjects
)
);
Object.entries(exportData.realm).forEach(([realm, data]: [string, any]) =>
Expand All @@ -118,7 +127,8 @@ export async function exportEverythingToFiles(
`${baseDirectory}/realm/${realm}`,
includeMeta,
extract,
separateMappings
separateMappings,
separateObjects
)
)
);
Expand All @@ -141,6 +151,7 @@ export async function exportEverythingToFiles(
* @param {boolean} includeMeta true to include metadata, false otherwise. Default: true
* @param {boolean} extract Extracts the scripts from the exports into separate files if true
* @param {boolean} separateMappings separate sync.idm.json mappings if true, otherwise keep them in a single file
* @param {boolean} separateObjects separate managed.idm.json objects if true, otherwise keep them in a single file
*/
function exportItem(
exportData,
Expand All @@ -149,7 +160,8 @@ function exportItem(
baseDirectory,
includeMeta,
extract,
separateMappings = false
separateMappings = false,
separateObjects = false
) {
if (!obj || !Object.keys(obj).length) {
return;
Expand Down Expand Up @@ -253,6 +265,12 @@ function exportItem(
`${baseDirectory.substring(getWorkingDirectory(false).length + 1)}/${fileType}/sync`,
includeMeta
);
} else if (separateObjects && id === 'managed') {
writeManagedJsonToDirectory(
value as ManagedSkeleton,
`${baseDirectory.substring(getWorkingDirectory(false).length + 1)}/${fileType}/managed`,
includeMeta
);
} else {
const filename = `${id}.idm.json`;
if (filename.includes('/')) {
Expand Down
49 changes: 47 additions & 2 deletions src/ops/IdmOps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { frodo, FrodoError } from '@rockcarver/frodo-lib';
import { type IdObjectSkeletonInterface } from '@rockcarver/frodo-lib/types/api/ApiTypes';
import { type ConfigEntityExportInterface } from '@rockcarver/frodo-lib/types/ops/IdmConfigOps';
import { SyncSkeleton } from '@rockcarver/frodo-lib/types/ops/MappingOps';
import {
ManagedSkeleton,
SyncSkeleton,
} from '@rockcarver/frodo-lib/types/ops/MappingOps';
import fs from 'fs';
import path from 'path';
import propertiesReader from 'properties-reader';
Expand All @@ -14,6 +17,8 @@ import {
} from '../utils/Console';
import {
getLegacyMappingsFromFiles,
getManagedObjectsFromFiles,
writeManagedJsonToDirectory,
writeSyncJsonToDirectory,
} from './MappingOps';

Expand Down Expand Up @@ -83,6 +88,7 @@ export async function listAllConfigEntities(): Promise<boolean> {
* @param {string} file optional export file name (or directory name if exporting mappings separately)
* @param {string} envFile File that defines environment specific variables for replacement during configuration export/import
* @param {boolean} separateMappings separate sync.idm.json mappings if true (and id is "sync"), otherwise keep them in a single file
* @param {boolean} separateObjects separate managed.idm.json objects if true (and id is "managed"), otherwise keep them in a single file
* @param {boolean} includeMeta true to include metadata, false otherwise. Default: true
* @return {Promise<boolean>} a promise that resolves to true if successful, false otherwise
*/
Expand All @@ -91,6 +97,7 @@ export async function exportConfigEntityToFile(
file?: string,
envFile?: string,
separateMappings: boolean = false,
separateObjects: boolean = false,
includeMeta: boolean = true
): Promise<boolean> {
try {
Expand All @@ -107,6 +114,14 @@ export async function exportConfigEntityToFile(
);
return true;
}
if (separateObjects && id === 'managed') {
writeManagedJsonToDirectory(
exportData.idm[id] as ManagedSkeleton,
file,
includeMeta
);
return true;
}
let fileName = file;
if (!fileName) {
fileName = getTypedFilename(`${id}`, 'idm');
Expand Down Expand Up @@ -156,12 +171,14 @@ export async function exportAllConfigEntitiesToFile(
* @param {string} entitiesFile JSON file that specifies the config entities to export/import
* @param {string} envFile File that defines environment specific variables for replacement during configuration export/import
* @param {boolean} separateMappings separate sync.idm.json mappings if true, otherwise keep them in a single file
* @param {boolean} separateObjects separate managed.idm.json objects if true, otherwise keep them in a single file
* @return {Promise<boolean>} a promise that resolves to true if successful, false otherwise
*/
export async function exportAllConfigEntitiesToFiles(
entitiesFile?: string,
envFile?: string,
separateMappings: boolean = false,
separateObjects: boolean = false,
includeMeta: boolean = true
): Promise<boolean> {
const errors: Error[] = [];
Expand All @@ -177,6 +194,14 @@ export async function exportAllConfigEntitiesToFiles(
writeSyncJsonToDirectory(obj as SyncSkeleton, 'sync', includeMeta);
continue;
}
if (separateObjects && id === 'managed') {
writeManagedJsonToDirectory(
obj as ManagedSkeleton,
'managed',
includeMeta
);
continue;
}
saveToFile(
'idm',
obj,
Expand Down Expand Up @@ -232,6 +257,14 @@ export async function importConfigEntityByIdFromFile(
},
]);
importData = { idm: { sync: syncData } };
} else if (entityId === 'managed') {
const managedData = getManagedObjectsFromFiles([
{
content: fileData,
path: `${filePath.substring(0, filePath.lastIndexOf('/'))}/managed.idm.json`,
},
]);
importData = { idm: { managed: managedData } };
} else {
importData = JSON.parse(fileData);
}
Expand Down Expand Up @@ -292,6 +325,14 @@ export async function importFirstConfigEntityFromFile(
},
]);
}
if (entityId === 'managed') {
importData.idm.managed = getManagedObjectsFromFiles([
{
content: fileData,
path: `${filePath.substring(0, filePath.lastIndexOf('/'))}/managed.idm.json`,
},
]);
}

const options = getIdmImportExportOptions(undefined, envFile);

Expand Down Expand Up @@ -434,9 +475,13 @@ export async function getIdmImportDataFromIdmDirectory(
);
// Process sync mapping file(s)
importData.idm.sync = getLegacyMappingsFromFiles(idmConfigFiles);
importData.idm.managed = getManagedObjectsFromFiles(idmConfigFiles);
// Process other files
for (const f of idmConfigFiles.filter(
(f) => !f.path.endsWith('sync.idm.json') && f.path.endsWith('.idm.json')
(f) =>
!f.path.endsWith('sync.idm.json') &&
!f.path.endsWith('managed.idm.json') &&
f.path.endsWith('.idm.json')
)) {
const entities = Object.values(
JSON.parse(f.content).idm
Expand Down
69 changes: 69 additions & 0 deletions src/ops/MappingOps.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { frodo, FrodoError } from '@rockcarver/frodo-lib';
import {
ManagedSkeleton,
MappingExportInterface,
MappingExportOptions,
MappingImportOptions,
Expand Down Expand Up @@ -479,6 +480,31 @@ export function writeSyncJsonToDirectory(
);
}

/**
* Helper that writes mappings in a managed.idm.json config entity to a directory
* @param managed The managed.idm.json config entity
* @param directory The directory to save the mappings
*/
export function writeManagedJsonToDirectory(
managed: ManagedSkeleton,
directory: string = 'managed',
includeMeta: boolean = true
) {
const objectPaths = [];
for (const object of managed.objects) {
const fileName = getTypedFilename(object.name, 'managed');
objectPaths.push(extractDataToFile(object, fileName, directory));
}
managed.objects = objectPaths;
saveToFile(
'idm',
managed,
'_id',
getFilePath(`${directory}/managed.idm.json`, true),
includeMeta
);
}

/**
* Helper that returns the sync.idm.json object containing all the mappings in it by looking through the files
*
Expand Down Expand Up @@ -516,6 +542,49 @@ export function getLegacyMappingsFromFiles(
return sync;
}

/**
* Helper that returns the managed.idm.json object containing all the mappings in it by looking through the files
*
* @param files the files to get managed.idm.json object from
* @returns the managed.idm.json object
*/
export function getManagedObjectsFromFiles(
files: { path: string; content: string }[]
): ManagedSkeleton {
const managedFiles = files.filter((f) =>
f.path.endsWith('/managed.idm.json')
);
if (managedFiles.length > 1) {
throw new FrodoError(
'Multiple managed.idm.json files found in idm directory'
);
}
const managed = {
_id: 'managed',
objects: [],
};
if (managedFiles.length === 1) {
const jsonData = JSON.parse(managedFiles[0].content);
const managedData = jsonData.managed
? jsonData.managed
: jsonData.idm.managed;
const managedJsonDir = managedFiles[0].path.substring(
0,
managedFiles[0].path.indexOf('/managed.idm.json')
);
if (managedData.objects) {
for (const object of managedData.objects) {
if (typeof object === 'string') {
managed.objects.push(getExtractedJsonData(object, managedJsonDir));
} else {
managed.objects.push(object);
}
}
}
}
return managed;
}

/**
* Helper that gets a mapping's type (either 'sync' or 'mapping') from it's id
* @param {string} mappingId the mapping id
Expand Down
13 changes: 11 additions & 2 deletions src/utils/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import fs from 'fs';
import os from 'os';

import { readServersFromFiles } from '../ops/classic/ServerOps';
import { getLegacyMappingsFromFiles } from '../ops/MappingOps';
import {
getLegacyMappingsFromFiles,
getManagedObjectsFromFiles,
} from '../ops/MappingOps';
import { getScriptExportByScriptFile } from '../ops/ScriptOps';
import { printMessage } from './Console';

Expand Down Expand Up @@ -159,7 +162,9 @@ export async function getConfig(
!f.path.endsWith('.script.json') &&
!f.path.endsWith('.server.json') &&
!f.path.endsWith('/sync.idm.json') &&
!f.path.endsWith('sync.json')
!f.path.endsWith('sync.json') &&
!f.path.endsWith('/managed.idm.json') &&
!f.path.endsWith('managed.json')
);
// Handle all other json files
for (const f of allOtherFiles) {
Expand All @@ -183,6 +188,10 @@ export async function getConfig(
if (sync.mappings.length > 0) {
(exportConfig as FullGlobalExportInterface).sync = sync;
}
const managed = await getManagedObjectsFromFiles(jsonFiles);
if (managed.objects.length > 0) {
(exportConfig as FullGlobalExportInterface).idm.managed = managed;
}
// Handle saml files
if (
samlFiles.length > 0 &&
Expand Down

0 comments on commit d451973

Please sign in to comment.