Skip to content
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

Feature/984: bulk update notification email addresses #1066

Open
wants to merge 71 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
b09acd1
#984: setNotifEmailAddr command added
phjulia Aug 1, 2023
000778e
#984: added a function to update notifications for Automations
phjulia Aug 3, 2023
76dda3e
#984: move updateNotifications under runMethod
phjulia Aug 7, 2023
49440b9
#984: enable updating notes or email addresses, additional checks, mo…
phjulia Aug 7, 2023
10a3fe3
#984: added 2 test cases for updateNotifications
phjulia Aug 7, 2023
19f1318
#984: updated function name
phjulia Aug 10, 2023
24543f0
#984: updateNotifications improvements
phjulia Aug 12, 2023
581a3a1
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
phjulia Aug 12, 2023
0f1c3fe
#984: fixed tests, adjusted logic to set some notifications, check if…
phjulia Aug 13, 2023
48e37e1
#984: fixed tests - +2 api requests because there is a new test autom…
phjulia Aug 13, 2023
e7c93c2
#984: moved repetitive code into #getEncodedAutomationIDs()
phjulia Aug 13, 2023
92dfd6e
#984: added interactive questions, no email validation at this point
phjulia Aug 13, 2023
9934cbd
#984: corrected test
phjulia Aug 13, 2023
83f679d
#984: remove notifications option
phjulia Aug 13, 2023
ae4cc5d
#984: added email validator
phjulia Aug 14, 2023
309de98
#984: added a test for importFile
phjulia Aug 14, 2023
94d8c11
#984: added a test for --clear option
phjulia Aug 20, 2023
18cd195
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
phjulia Aug 20, 2023
ceefc04
#984: +1 test automations, the rest of the tests were adapted
phjulia Aug 20, 2023
127e1e4
#984: --clear option for importFile
phjulia Aug 20, 2023
d87161a
#984: exit updateNotifications for importFile if user provided option…
phjulia Aug 20, 2023
00704bf
#984: note was not set if email address was not provided
phjulia Aug 20, 2023
3910110
#984: retrive for clearNotifications is already covered
phjulia Aug 20, 2023
e8f7744
#984: some improvements and more tests
phjulia Aug 21, 2023
556d256
#984: ternary -> if
phjulia Aug 21, 2023
1d30d9e
#984: removed error variable
phjulia Aug 21, 2023
051b215
Update test/type.importFile.test.js
phjulia Aug 21, 2023
9f822f6
Update test/type.automation.test.js
phjulia Aug 21, 2023
720231c
#984: test update completion email address and error email address
phjulia Aug 21, 2023
739eaf8
#984: +9 positive test cases
phjulia Aug 23, 2023
10bf762
Merge branch 'feature/984-feature-bulk-update-notification-email-addr…
phjulia Aug 23, 2023
a38f02b
#984: +1 negative test case
phjulia Aug 24, 2023
6d87df6
#984: group:
phjulia Aug 24, 2023
eff4b1a
#984: moved properties out of static variables
phjulia Aug 24, 2023
55a3389
#984: removed static variables
phjulia Aug 24, 2023
d07cf91
Update lib/cli.js
phjulia Aug 24, 2023
47c847d
#984: made getKeysToSetNotifications in MetadataType abstract, moved …
phjulia Aug 26, 2023
e07fba6
Merge branch 'feature/984-feature-bulk-update-notification-email-addr…
phjulia Aug 26, 2023
5bdfa31
#984: clearNotifications was removed from MetadataType and index
phjulia Aug 26, 2023
4cc50b3
#984: info to verbose
phjulia Aug 26, 2023
592d3fb
#984: renamed negative test cases
phjulia Aug 26, 2023
989fed4
#984: renamed negative test cases
phjulia Aug 26, 2023
695afb0
#984: renamed test cases
phjulia Aug 26, 2023
fc03b1d
#984: split a test case into 2
phjulia Aug 26, 2023
729eb96
#984: split a test case into 3
phjulia Aug 26, 2023
f691e6c
#984: +3 tests for clearNotifications (+3 cached automations, +6 API …
phjulia Aug 27, 2023
3023492
#984: improved regex email validation
phjulia Aug 27, 2023
9abe1e5
#984: moved type specific stuff into child class
phjulia Aug 27, 2023
1609171
#984: removed options from updateNotifications command
phjulia Aug 27, 2023
9bb5be7
#984: added a test for updating a note only
phjulia Aug 28, 2023
0dc51ae
#984: +1 automation
phjulia Aug 28, 2023
23d0cea
#984: added a function to retrieve and deploy that is used for fixKey…
phjulia Aug 30, 2023
12f7b3d
#984: regex for email validation replaced
phjulia Aug 30, 2023
fe22dca
#984: add questions to an array
phjulia Aug 30, 2023
0b43078
#984: validate added
phjulia Aug 30, 2023
6c09a25
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
phjulia Aug 30, 2023
9ddbefe
#984: test cases
phjulia Aug 31, 2023
f1ff0de
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
phjulia Aug 31, 2023
c18e6ef
#984: +15 automations/+15 API calls
phjulia Aug 31, 2023
7b0de99
#984: fixed issue with email validation
phjulia Sep 3, 2023
1101102
#984: merge develop into branch
phjulia Sep 24, 2023
7fb1fb4
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
JoernBerkefeld Oct 9, 2023
af22fb9
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
JoernBerkefeld Oct 9, 2023
bd755f4
#0: auto-formatting
JoernBerkefeld Oct 9, 2023
258c067
#984: improve log message for updateNotification
JoernBerkefeld Oct 9, 2023
63f9784
#984: move updateNotification logic from custom createOrUpdate into p…
JoernBerkefeld Oct 9, 2023
8e2670a
#984: rename importFile test for updateNotification
JoernBerkefeld Oct 10, 2023
9981086
#984: refactoring
JoernBerkefeld Oct 11, 2023
801317d
Merge branch 'develop' into feature/984-feature-bulk-update-notificat…
JoernBerkefeld Feb 27, 2024
f1df201
#0: pre-commit hook logs
JoernBerkefeld Feb 27, 2024
6626df6
#984: fix tests for importFile & query
JoernBerkefeld Feb 27, 2024
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
7 changes: 5 additions & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#npm run docs
#git update-index --add docs/dist/documentation.md
echo "[PRE-COMMIT] Re-creating documentation..."
npm run docs
git update-index --add docs/dist/documentation.md

echo "[PRE-COMMIT] Running lint-staged..."
npx --no lint-staged
186 changes: 184 additions & 2 deletions docs/dist/documentation.md

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,63 @@ yargs(hideBin(process.argv))
Mcdev.fixKeys(argv.BU, argv.TYPE, csvToArray(argv.KEY));
},
})
.command({
command: 'updateNotifications <BU> <TYPE> [KEY]',
aliases: ['un'],
desc: 'bulk updates notification email addresses',
builder: (yargs) => {
yargs
.positional('BU', {
type: 'string',
describe: 'the business unit where to update notification email address',
})
.positional('TYPE', {
type: 'string',
describe: 'metadata type',
})
.positional('KEY', {
type: 'string',
describe: 'key(s) of the metadata component(s)',
})
.option('errorEmail', {
type: 'string',
phjulia marked this conversation as resolved.
Show resolved Hide resolved
group: 'Options for updateNotifications:',
describe: 'email to notify that an error occured during execution',
})
.option('completionEmail', {
type: 'string',
phjulia marked this conversation as resolved.
Show resolved Hide resolved
group: 'Options for updateNotifications:',
describe: 'email to notify about successfull completion',
})
.option('errorNote', {
type: 'string',
phjulia marked this conversation as resolved.
Show resolved Hide resolved
group: 'Options for updateNotifications:',
describe: 'run error note',
})
.option('completionNote', {
type: 'string',
phjulia marked this conversation as resolved.
Show resolved Hide resolved
group: 'Options for updateNotifications:',
describe: 'run completion note',
})
.option('clear', {
type: 'string',
phjulia marked this conversation as resolved.
Show resolved Hide resolved
group: 'Options for updateNotifications:',
describe:
"remove notification email addresses and/or notes. Possible options: 'all', 'errorEmail', 'completionEmail', 'notes' for automation. For the rest of types any value would do (true/all/etc.)",
})
.option('like', {
type: 'string',
group: 'Options for updateNotifications:',
describe:
'filter metadata components (can include % as wildcard or _ for a single character)',
});
},
handler: (argv) => {
Mcdev.setOptions(argv);
// ! do not allow multiple types to be passed in here via csvToArray
Mcdev.updateNotifications(argv.BU, argv.TYPE, csvToArray(argv.KEY));
},
})
.command({
command: 'upgrade',
aliases: ['up'],
Expand Down
222 changes: 178 additions & 44 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class Mcdev {
'_runningTest',
'schedule',
'skipInteraction',
'errorEmail',
'completionEmail',
'errorNote',
'completionNote',
'clear',
];
for (const option of knownOptions) {
if (argv[option] !== undefined) {
Expand Down Expand Up @@ -798,7 +803,7 @@ class Mcdev {
/**
* run a method across BUs
*
* @param {'execute'|'pause'|'fixKeys'} methodName what to run
* @param {'execute'|'pause'|'fixKeys'|'updateNotifications'} methodName what to run
* @param {string} businessUnit name of BU
* @param {TYPE.SupportedMetadataTypes} [selectedType] limit to given metadata types
* @param {string[]} [keys] customerkey of the metadata
Expand Down Expand Up @@ -834,6 +839,13 @@ class Mcdev {
checkMetadataSupport = false;
break;
}
case 'updateNotifications': {
lang_past = 'updated notifications for';
lang_present = 'updating notifications for';
requireKeyOrLike = true;
checkMetadataSupport = false;
break;
}
}

Util.logger.info(`mcdev:: ${methodName} ${selectedType}`);
Expand Down Expand Up @@ -961,7 +973,7 @@ class Mcdev {
/**
* helper for {@link Mcdev.#runMethod}
*
* @param {'execute'|'pause'|'fixKeys'} methodName what to run
* @param {'execute'|'pause'|'fixKeys'|'updateNotifications'} methodName what to run
* @param {string} cred name of Credential
* @param {string} bu name of BU
* @param {TYPE.SupportedMetadataTypes} [type] limit execution to given metadata type
Expand Down Expand Up @@ -999,6 +1011,21 @@ class Mcdev {
break;
}
}
case 'updateNotifications': {
if (Object.prototype.hasOwnProperty.call(MetadataTypeInfo[type], methodName)) {
resultArr.push(...(await MetadataTypeInfo[type][methodName](keyArr)));
if (resultArr.length > 0) {
Util.logger.info(`Retrieving ${type} to have most recent changes`);
const retriever = new Retriever(properties, buObject);
await retriever.retrieve([type], resultArr, null, false);
}
} else {
resultArr.push(
...(await this.#updateNotifications(cred, bu, type, keyArr))
);
}
break;
}
default: {
if (Util.OPTIONS.like && Object.keys(Util.OPTIONS.like).length) {
keyArr = await this.#retrieveKeysWithLike(type, buObject);
Expand All @@ -1012,7 +1039,6 @@ class Mcdev {
} catch (ex) {
Util.logger.errorStack(ex, 'mcdev.' + methodName + ' failed');
}

return resultArr;
}

Expand Down Expand Up @@ -1092,16 +1118,71 @@ class Mcdev {
static async #fixKeys(cred, bu, type, keyArr) {
const properties = await config.getProperties();
let actuallyFixedKeys = [];
const resultArr = [];

if (
MetadataTypeDefinitions[type].keyIsFixed === true ||
MetadataTypeDefinitions[type].keyField === MetadataTypeDefinitions[type].idField
) {
Util.logger.error(`Key cannot be updated for this type`);
return resultArr;
return actuallyFixedKeys;
}
const buObject = await Cli.getCredentialObject(
properties,
cred === null ? null : cred + '/' + bu,
null,
true
);
this.setOptions({
changeKeyField: MetadataTypeDefinitions[type].nameField,
fromRetrieve: true,
});
actuallyFixedKeys = await this.#updateItems(cred, bu, type, keyArr, 'fixKeys');
const dependentTypes = await Util.getDependentMetadata(type);
if (actuallyFixedKeys && actuallyFixedKeys.length) {
Util.logger.info(
`Successfully updated ${actuallyFixedKeys.length} key${
actuallyFixedKeys.length === 1 ? '' : 's'
} of type ${type}`
);
if (dependentTypes.length) {
Util.logger.warn(
`Please re-retrieve the following types as your local copies might now be outdated: ${Util.getGrayMsg(
dependentTypes.join(', ')
)}`
);
const reRetrieve = await Cli.postFixKeysReretrieve(type, dependentTypes);
if (reRetrieve) {
Util.logger.info(
`Retrieving latest versions of ${dependentTypes.join(', ')} from server`
);
const retriever = new Retriever(properties, buObject);
await retriever.retrieve(dependentTypes, null, null, false);
}
} else {
Util.logger.info(
`No dependent types found that need to be re-retrieved after fixing keys of type ${type}.`
);
}
} else {
Util.logger.warn(`No keys of type ${type} updated.`);
}

return actuallyFixedKeys;
}
/**
* A function to retrieve, update and deploy items
*
* @param {string} cred name of Credential
* @param {string} bu name of BU
* @param {TYPE.SupportedMetadataTypes} type limit execution to given metadata type
* @param {string[]} [keyArr] customerkey of the metadata
* @param {string} methodName name of the function to execute
* @returns {Promise.<string[]>} list of keys that were affected
*/
static async #updateItems(cred, bu, type, keyArr, methodName) {
const properties = await config.getProperties();
let actuallyUpdatedItems = [];
const resultArr = [];

const buObject = await Cli.getCredentialObject(
properties,
cred === null ? null : cred + '/' + bu,
Expand All @@ -1114,55 +1195,108 @@ class Mcdev {
const retrieved = await retriever.retrieve([type], keyArr, null, false);

const metadataMap = Object.values(retrieved)[0][0];
const keysForDeploy = MetadataTypeInfo[type].getKeysForFixing(metadataMap);
const keysForDeploy = await this.#getKeys(type, metadataMap, methodName);
if (keysForDeploy.length < 1) {
Util.logger.warn(
`No items found with a key-name mismatch that match your criteria.\n`
);
return resultArr;
}
this.setOptions({
changeKeyField: MetadataTypeDefinitions[type].nameField,
fromRetrieve: true,
});
const deployed = await Deployer.deploy(cred + '/' + bu, [type], keysForDeploy);
actuallyFixedKeys = Object.keys(Object.values(Object.values(deployed)[0])[0]);
resultArr.push(...actuallyFixedKeys);
const dependentTypes = await Util.getDependentMetadata(type);
if (actuallyFixedKeys && actuallyFixedKeys.length) {
Util.logger.info(
`Successfully updated ${actuallyFixedKeys.length} key${
actuallyFixedKeys.length === 1 ? '' : 's'
} of type ${type}`
);
if (dependentTypes.length) {
Util.logger.warn(
`Please re-retrieve the following types as your local copies might now be outdated: ${Util.getGrayMsg(
dependentTypes.join(', ')
)}`
);
const reRetrieve = await Cli.postFixKeysReretrieve(type, dependentTypes);
if (reRetrieve) {
Util.logger.info(
`Retrieving latest versions of ${dependentTypes.join(', ')} from server`
);
const retriever = new Retriever(properties, buObject);
await retriever.retrieve(dependentTypes, null, null, false);
}
} else {
Util.logger.info(
`No dependent types found that need to be re-retrieved after fixing keys of type ${type}.`
);
}
} else {
Util.logger.warn(`No keys of type ${type} updated.`);
}
actuallyUpdatedItems = Object.keys(Object.values(Object.values(deployed)[0])[0]);
resultArr.push(...actuallyUpdatedItems);
} catch (ex) {
Util.logger.errorStack(ex, 'mcdev.fixKeys failed');
Util.logger.errorStack(ex, `mcdev.${methodName} failed`);
}
Util.logger.info(`:: Done\n`);
return resultArr;
}
/**
* helper function to get keys of items to update
*
* @param {TYPE.SupportedMetadataTypes} type limit execution to given metadata type
* @param {TYPE.MetadataTypeMap} metadataMap metadata mapped by their keyField
* @param {string} methodName name of the method to execute
* @returns {string[]} list of keys
*/
static async #getKeys(type, metadataMap, methodName) {
const keys = [];
switch (methodName) {
case 'fixKeys': {
keys.push(...(await MetadataTypeInfo[type].getKeysForFixing(metadataMap)));
break;
}
case 'updateNotifications': {
keys.push(...(await MetadataTypeInfo[type].getKeysToSetNotifications(metadataMap)));
break;
}
}
return keys;
}
/**
* Updates notification email address field
*
* @param {string} businessUnit name of BU
* @param {TYPE.SupportedMetadataTypes} selectedType limit execution to given metadata type
* @param {string[]} [keys] customerkey of the metadata
* @returns {Promise.<Object.<string, string[]>>} key: business unit name, value: list of affected item keys
*/
static async updateNotifications(businessUnit, selectedType, keys) {
if (
!Util.OPTIONS.completionEmail &&
!Util.OPTIONS.errorEmail &&
!Util.OPTIONS.completionNote &&
!Util.OPTIONS.errorNote &&
!Util.OPTIONS.clear
) {
// if no options were provided there is nothing to update
Util.logger.error(`No email addresses, run notes or a clear option was provided`);
return [];
}

return await this.#runMethod('updateNotifications', businessUnit, selectedType, keys);
}
/**
* Updates notification email address field
*
* @param {string} cred name of Credential
* @param {string} bu name of BU
* @param {TYPE.SupportedMetadataTypes} type limit execution to given metadata type
* @param {string[]} [keyArr] customerkey of the metadata
* @returns {Promise.<string[]>} list of keys that were affected
*/
static async #updateNotifications(cred, bu, type, keyArr) {
let keysUpdatedNotifications = [];
if (Util.OPTIONS.errorEmail || Util.OPTIONS.errorNote || Util.OPTIONS.completionNote) {
Util.logger.error(
`--errorEmail, --errorNote and --completionNote options are not available for ${type}`
);
return keysUpdatedNotifications;
}
if (!MetadataTypeDefinitions[type].fields.sendEmailNotification) {
Util.logger.error(`Update notifications is not supported for this type`);
return keysUpdatedNotifications;
}
this.setOptions({
fromRetrieve: true,
});
keysUpdatedNotifications = await this.#updateItems(
cred,
bu,
type,
keyArr,
'updateNotifications'
);
if (keysUpdatedNotifications && keysUpdatedNotifications.length) {
Util.logger.info(
`Successfully updated notifications. Updated ${
keysUpdatedNotifications.length
} key${keysUpdatedNotifications.length === 1 ? '' : 's'} of type ${type}`
);
} else {
Util.logger.warn(`No keys of type ${type} updated.`);
}
return keysUpdatedNotifications;
}
}

export default Mcdev;
Loading
Loading