Skip to content

Add Support for Partial Exports and Imports #505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
116 changes: 55 additions & 61 deletions src/api/AmConfigApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { FrodoError } from '../ops/FrodoError';
import { ResultCallback } from '../ops/OpsTypes';
import Constants from '../shared/Constants';
import { State } from '../shared/State';
import { printError, printMessage } from '../utils/Console';
import { getResult } from '../utils/ExportImportUtils';
import {
getRealmPathGlobal,
getRealmsForExport,
Expand Down Expand Up @@ -259,11 +261,10 @@ export async function getConfigEntity({
state.setRealm(currentRealm);
return data;
} catch (error) {
printError({
error,
message: `Error getting config entity from resource path '${urlString}'`,
state,
});
throw new FrodoError(
`Error getting config entity from resource path '${urlString}'`,
error
);
}
}

Expand All @@ -272,17 +273,20 @@ export async function getConfigEntity({
* @param {boolean} includeReadOnly Include read only config in the export
* @param {boolean} onlyRealm Get config only from the active realm. If onlyGlobal is also active, then it will also get the global config.
* @param {boolean} onlyGlobal Get global config only. If onlyRealm is also active, then it will also get the active realm config.
* @param {ResultCallback} resultCallback Optional callback to process individual results
* @returns {Promise<ConfigSkeleton>} a promise that resolves to a config object containing global and realm config entities
*/
export async function getConfigEntities({
includeReadOnly = false,
onlyRealm = false,
onlyGlobal = false,
resultCallback = void 0,
state,
}: {
includeReadOnly: boolean;
onlyRealm: boolean;
onlyGlobal: boolean;
resultCallback: ResultCallback<ConfigEntitySkeleton>;
state: State;
}): Promise<ConfigSkeleton> {
const realms = await getRealmsForExport({ state });
Expand All @@ -305,8 +309,11 @@ export async function getConfigEntities({
entityInfo.global.deployments.includes(state.getDeploymentType())) ||
(entityInfo.global.deployments == undefined && deploymentAllowed))
) {
try {
entities.global[key] = await getConfigEntity({
entities.global[key] = await getResult(
resultCallback,
`Error getting '${key}' from resource path '${entityInfo.global.path}'`,
getConfigEntity,
{
state,
path: entityInfo.global.path,
version: entityInfo.global.version,
Expand All @@ -317,14 +324,8 @@ export async function getConfigEntities({
action: entityInfo.global.action
? entityInfo.global.action
: entityInfo.action,
});
} catch (e) {
printMessage({
message: `Error getting '${key}' from resource path '${entityInfo.global.path}': ${e.message}`,
type: 'error',
state,
});
}
}
);
}
if (
(!onlyGlobal || onlyRealm) &&
Expand All @@ -342,8 +343,11 @@ export async function getConfigEntities({
) {
continue;
}
try {
entities.realm[realms[i]][key] = await getConfigEntity({
entities.realm[realms[i]][key] = await getResult(
resultCallback,
`Error getting '${key}' from resource path '${entityInfo.realm.path}'`,
getConfigEntity,
{
state,
path: entityInfo.realm.path,
version: entityInfo.realm.version,
Expand All @@ -355,14 +359,8 @@ export async function getConfigEntities({
action: entityInfo.realm.action
? entityInfo.realm.action
: entityInfo.action,
});
} catch (e) {
printMessage({
message: `Error getting '${key}' from resource path '${entityInfo.realm.path}': ${e.message}`,
type: 'error',
state,
});
}
}
);
}
}
}
Expand Down Expand Up @@ -418,24 +416,26 @@ export async function putConfigEntity({
state.setRealm(currentRealm);
return data;
} catch (error) {
printError({
error,
message: `Error putting config entity at resource path '${urlString}'`,
state,
});
throw new FrodoError(
`Error putting config entity at resource path '${urlString}'`,
error
);
}
}

/**
* Put all other AM config entities
* @param {ConfigSkeleton} config the config object containing global and realm config entities
* @param {ResultCallback} resultCallback Optional callback to process individual results
* @returns {Promise<ConfigSkeleton>} a promise that resolves to a config object containing global and realm config entities
*/
export async function putConfigEntities({
config,
resultCallback = void 0,
state,
}: {
config: ConfigSkeleton;
resultCallback: ResultCallback<ConfigEntitySkeleton>;
state: State;
}): Promise<ConfigSkeleton> {
const realms = config.realm ? Object.keys(config.realm) : [];
Expand All @@ -459,12 +459,15 @@ export async function putConfigEntities({
config.global &&
config.global[key]
) {
try {
for (const [id, entityData] of Object.entries(config.global[key])) {
if (!entities.global[key]) {
entities.global[key] = {};
}
entities.global[key][id] = await putConfigEntity({
for (const [id, entityData] of Object.entries(config.global[key])) {
if (!entities.global[key]) {
entities.global[key] = {};
}
entities.global[key][id] = await getResult(
resultCallback,
`Error putting entity '${id}' of type '${key}' to global resource path '${entityInfo.global.path}'`,
putConfigEntity,
{
state,
entityData: entityData as ConfigEntitySkeleton,
path:
Expand All @@ -473,14 +476,8 @@ export async function putConfigEntities({
version: entityInfo.global.version,
protocol: entityInfo.global.protocol,
ifMatch: entityInfo.global.ifMatch,
});
}
} catch (e) {
printMessage({
message: `Error putting '${key}' from resource path '${entityInfo.global.path}': ${e.message}`,
type: 'error',
state,
});
}
);
}
}
if (
Expand All @@ -493,14 +490,17 @@ export async function putConfigEntities({
if (!config.realm[realms[i]][key]) {
continue;
}
try {
for (const [id, entityData] of Object.entries(
config.realm[realms[i]][key]
)) {
if (!entities.realm[realms[i]][key]) {
entities.realm[realms[i]][key] = {};
}
entities.realm[realms[i]][key][id] = await putConfigEntity({
for (const [id, entityData] of Object.entries(
config.realm[realms[i]][key]
)) {
if (!entities.realm[realms[i]][key]) {
entities.realm[realms[i]][key] = {};
}
entities.realm[realms[i]][key][id] = await getResult(
resultCallback,
`Error putting entity '${id}' of type '${key}' to realm resource path '${entityInfo.realm.path}'`,
putConfigEntity,
{
state,
entityData: entityData as ConfigEntitySkeleton,
path:
Expand All @@ -510,14 +510,8 @@ export async function putConfigEntities({
protocol: entityInfo.realm.protocol,
ifMatch: entityInfo.realm.ifMatch,
realm: stateRealms[i],
});
}
} catch (e) {
printMessage({
message: `Error putting '${key}' from resource path '${entityInfo.realm.path}': ${e.message}`,
type: 'error',
state,
});
}
);
}
}
}
Expand Down
33 changes: 0 additions & 33 deletions src/api/ScriptApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,36 +245,3 @@ export async function deleteScriptByName({
state,
});
}

/**
* Delete all non-default scripts
* @returns {Promise<ScriptSkeleton[]>} a promise that resolves to an array of script objects
*/
export async function deleteScripts({
state,
}: {
state: State;
}): Promise<ScriptSkeleton[]> {
const { result } = await getScripts({ state });
//Unable to delete default scripts, so filter them out
const scripts = result.filter((s) => !s.default);
const deletedScripts = [];
const errors = [];
for (const script of scripts) {
try {
deletedScripts.push(
await deleteScript({
scriptId: script._id,
state,
})
);
} catch (error) {
errors.push(error);
}
}
if (errors.length) {
const errorMessages = errors.map((error) => error.message).join('\n');
throw new Error(`Delete error:\n${errorMessages}`);
}
return deletedScripts;
}
17 changes: 9 additions & 8 deletions src/ops/AmConfigOps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { filterRecording } from "../utils/PollyUtils";
import * as AmConfigOps from "./AmConfigOps";
import { state } from "../lib/FrodoLib";
import Constants from "../shared/Constants";
import { snapshotResultCallback } from "../test/utils/TestUtils";

const ctx = autoSetupPolly();

Expand Down Expand Up @@ -98,28 +99,28 @@ describe('AmConfigOps', () => {
});

test('1: Export AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('2: Export importable AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: false, onlyRealm: false, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: false, onlyRealm: false, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('3: Export alpha realm AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: true, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: true, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('4: Export global AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: true, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: true, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
Expand Down Expand Up @@ -149,28 +150,28 @@ describe('AmConfigOps', () => {

describe('exportAmConfigEntities()', () => {
test('5: Export AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('6: Export importable AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: false, onlyRealm: false, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: false, onlyRealm: false, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('7: Export root realm AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: true, onlyGlobal: false, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: true, onlyGlobal: false, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
});

test('8: Export global AM Config Entities', async () => {
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: true, state });
const response = await AmConfigOps.exportAmConfigEntities({ includeReadOnly: true, onlyRealm: false, onlyGlobal: true, resultCallback: snapshotResultCallback, state });
expect(response).toMatchSnapshot({
meta: expect.any(Object),
});
Expand Down
Loading