Skip to content

Commit

Permalink
Merge pull request #1019 from Accenture/feature/870-feature-execute-a…
Browse files Browse the repository at this point in the history
…utomation-command-to-start-scheduled-automations

Feature/870 `execute` automation command to start scheduled automations + Feature/1018 add command to `pause` a scheduled automation
  • Loading branch information
JoernBerkefeld authored Jul 11, 2023
2 parents 581e621 + ef4d868 commit 1337dcf
Show file tree
Hide file tree
Showing 38 changed files with 1,105 additions and 282 deletions.
264 changes: 198 additions & 66 deletions docs/dist/documentation.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/Deployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class Deployer {
return buMultiMetadataTypeMap;
}
/**
* helper for {@link deploy}
* helper for {@link Deployer.deploy}
*
* @param {string} cred name of Credential
* @param {string} bu name of BU
Expand Down
2 changes: 1 addition & 1 deletion lib/Retriever.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ class Retriever {
}

/**
* helper for {@link retrieve} to get all dependencies of the given types
* helper for {@link Retriever.retrieve} to get all dependencies of the given types
*
* @param {TYPE.SupportedMetadataTypes[]} metadataTypes list of metadata types to retrieve; can include subtypes!
* @returns {TYPE.SupportedMetadataTypes[]} unique list dependent metadata types
Expand Down
31 changes: 31 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,37 @@ yargs
Mcdev.execute(argv.BU, argv.TYPE, csvToArray(argv.KEY));
},
})
.command({
command: 'pause <BU> <TYPE> [KEY]',
aliases: ['p', 'stop'],
desc: 'pauses the entity (automation etc.)',
builder: (yargs) => {
yargs
.positional('BU', {
type: 'string',
describe: 'the business unit where to start an item',
})
.positional('TYPE', {
type: 'string',
describe: 'metadata type',
})
.positional('KEY', {
type: 'string',
describe: 'key(s) of the metadata component(s)',
})
.option('like', {
type: 'string',
group: 'Options for pause:',
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.pause(argv.BU, argv.TYPE, csvToArray(argv.KEY));
},
})
.command({
command: 'upgrade',
aliases: ['up'],
Expand Down
128 changes: 91 additions & 37 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,20 @@ class Mcdev {
}

if (businessUnit === '*') {
Util.logger.info('\n :: Retrieving all BUs for all credentials');
Util.logger.info(':: Retrieving all BUs for all credentials');
let counter_credTotal = 0;
for (const cred in properties.credentials) {
Util.logger.info(`\n :: Retrieving all BUs for ${cred}`);
Util.logger.info(`:: Retrieving all BUs for ${cred}`);
let counter_credBu = 0;
for (const bu in properties.credentials[cred].businessUnits) {
await this._retrieveBU(cred, bu, selectedTypesArr, keys);
await this.#retrieveBU(cred, bu, selectedTypesArr, keys);
counter_credBu++;
Util.startLogger(true);
}
counter_credTotal += counter_credBu;
Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`);
Util.logger.info(`:: ${counter_credBu} BUs for ${cred}\n`);
}
Util.logger.info(`\n :: ${counter_credTotal} BUs in total\n`);
Util.logger.info(`:: ${counter_credTotal} BUs in total\n`);
} else {
let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
// to allow all-BU via user selection we need to run this here already
Expand All @@ -212,17 +212,17 @@ class Mcdev {
}

if (bu === '*' && properties.credentials && properties.credentials[cred]) {
Util.logger.info(`\n :: Retrieving all BUs for ${cred}`);
Util.logger.info(`:: Retrieving all BUs for ${cred}`);
let counter_credBu = 0;
for (const bu in properties.credentials[cred].businessUnits) {
await this._retrieveBU(cred, bu, selectedTypesArr, keys);
await this.#retrieveBU(cred, bu, selectedTypesArr, keys);
counter_credBu++;
Util.startLogger(true);
}
Util.logger.info(`\n :: ${counter_credBu} BUs for ${cred}\n`);
Util.logger.info(`:: ${counter_credBu} BUs for ${cred}\n`);
} else {
// retrieve a single BU; return
const retrieveChangelog = await this._retrieveBU(
const retrieveChangelog = await this.#retrieveBU(
cred,
bu,
selectedTypesArr,
Expand All @@ -247,7 +247,7 @@ class Mcdev {
* @param {boolean} [changelogOnly] skip saving, only create json in memory
* @returns {Promise.<object>} ensure that BUs are worked on sequentially
*/
static async _retrieveBU(cred, bu, selectedTypesArr, keys, changelogOnly) {
static async #retrieveBU(cred, bu, selectedTypesArr, keys, changelogOnly) {
const properties = await config.getProperties();
if (!(await config.checkProperties(properties))) {
return null;
Expand All @@ -272,7 +272,7 @@ class Mcdev {
triggeredSend: 'triggeredSendDefinition',
user: 'accountUser',
};
Util.logger.info(`\n :: Retrieving ${cred}/${bu}\n`);
Util.logger.info(`:: Retrieving ${cred}/${bu}\n`);
const retrieveTypesArr = [];
if (selectedTypesArr) {
for (const selectedType of Array.isArray(selectedTypesArr)
Expand Down Expand Up @@ -703,16 +703,57 @@ class Mcdev {
}
}
/**
* Start an item (query)
* Start/execute an item
*
* @param {string} businessUnit name of BU
* @param {TYPE.SupportedMetadataTypes} [selectedType] limit to given metadata types
* @param {string[]} [keys] customerkey of the metadata
* @returns {Promise.<boolean>} true if all started successfully, false if not
*/
static async execute(businessUnit, selectedType, keys) {
return this.#runMethod('execute', businessUnit, selectedType, keys);
}
/**
* pause an item
*
* @param {string} businessUnit name of BU
* @param {TYPE.SupportedMetadataTypes} [selectedType] limit to given metadata types
* @param {string[]} [keys] customerkey of the metadata
* @returns {Promise.<boolean>} true if all started successfully, false if not
*/
static async pause(businessUnit, selectedType, keys) {
return this.#runMethod('pause', businessUnit, selectedType, keys);
}
/**
* run a method across BUs
*
* @param {'execute'|'pause'} 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
* @returns {Promise.<boolean>} true if all started successfully, false if not
*/
static async #runMethod(methodName, businessUnit, selectedType, keys) {
Util.startLogger();
Util.logger.info('mcdev:: Executing ' + selectedType);
let lang_past;
let lang_present;
let requireKeyOrLike;
switch (methodName) {
case 'execute': {
lang_past = 'executed';
lang_present = 'executing';
requireKeyOrLike = true;
break;
}
case 'pause': {
lang_past = 'paused';
lang_present = 'pausing';
requireKeyOrLike = true;
break;
}
}

Util.logger.info(`mcdev:: ${methodName} ${selectedType}`);
const properties = await config.getProperties();
let counter_credBu = 0;
let counter_failed = 0;
Expand All @@ -723,13 +764,15 @@ class Mcdev {
if (!Util._isValidType(selectedType)) {
return false;
}
if (!Object.prototype.hasOwnProperty.call(MetadataTypeInfo[selectedType], 'execute')) {
if (!Object.prototype.hasOwnProperty.call(MetadataTypeInfo[selectedType], methodName)) {
Util.logger.error(
` ☇ skipping ${selectedType}: execute is not supported yet for ${selectedType}`
` ☇ skipping ${selectedType}: ${methodName} is not supported yet for ${selectedType}`
);
return false;
}

if (
requireKeyOrLike &&
(!Array.isArray(keys) || !keys.length) &&
(!Util.OPTIONS.like || !Object.keys(Util.OPTIONS.like).length)
) {
Expand All @@ -744,24 +787,31 @@ class Mcdev {
Util.logger.error('You can either specify keys OR a --like filter.');
return false;
}

if (businessUnit === '*') {
Util.logger.info(':: Executing the entity on all BUs for all credentials');
Util.logger.info(
`:: ${lang_present} the ${selectedType} on all BUs for all credentials`
);
let counter_credTotal = 0;
for (const cred in properties.credentials) {
Util.logger.info(`:: Executing the entity on all BUs for ${cred}`);
Util.logger.info(`:: ${lang_present} ${selectedType} on all BUs for ${cred}`);

for (const bu in properties.credentials[cred].businessUnits) {
if (await this._executeBU(cred, bu, selectedType, keys)) {
if (await this.#runOnBU(methodName, cred, bu, selectedType, keys)) {
counter_credBu++;
} else {
counter_failed++;
}
Util.startLogger(true);
}
counter_credTotal += counter_credBu;
Util.logger.info(`:: Executed the entity on ${counter_credBu} BUs for ${cred}\n`);
Util.logger.info(
`:: ${lang_past} ${selectedType} on ${counter_credBu} BUs for ${cred}`
);
}
Util.logger.info(`:: Executed the entity on ${counter_credTotal} BUs in total\n`);
Util.logger.info(
`:: ${lang_past} ${selectedType} on ${counter_credTotal} BUs in total\n`
);
} else {
let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null];
// to allow all-BU via user selection we need to run this here already
Expand All @@ -784,44 +834,45 @@ class Mcdev {
}
}
if (bu === '*' && properties.credentials && properties.credentials[cred]) {
Util.logger.info(`\n :: Executing the entity on all BUs for ${cred}`);
Util.logger.info(`:: ${lang_present} ${selectedType} on all BUs for ${cred}`);
let counter_credBu = 0;
for (const bu in properties.credentials[cred].businessUnits) {
if (await this._executeBU(cred, bu, selectedType, keys)) {
if (await this.#runOnBU(methodName, cred, bu, selectedType, keys)) {
counter_credBu++;
} else {
counter_failed++;
}
Util.startLogger(true);
}
Util.logger.info(
`\n :: Executed the entity on ${counter_credBu} BUs for ${cred}\n`
`:: ${lang_past} ${selectedType} on ${counter_credBu} BUs for ${cred}`
);
} else {
// execute the entity on one BU only
if (await this._executeBU(cred, bu, selectedType, keys)) {
// execute runMethod for the entity on one BU only
if (await this.#runOnBU(methodName, cred, bu, selectedType, keys)) {
counter_credBu++;
} else {
counter_failed++;
}
Util.logger.info(`\n :: Done\n`);
Util.logger.info(`:: Done`);
}
}
if (counter_credBu !== 0) {
Util.logger.info(`\n :: Executed query on ${counter_credBu} BUs\n`);
if (counter_credBu > 1) {
Util.logger.info(`:: ${lang_past} ${selectedType} on ${counter_credBu} BUs`);
}
return counter_failed === 0 ? true : false;
}
/**
* helper for {@link Mcdev.execute}
* helper for {@link Mcdev.#runMethod}
*
* @param {'execute'|'pause'} 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
* @param {string[]} keyArr customerkey of the metadata
* @returns {Promise.<boolean>} true if all items were executed, false otherwise
*/
static async _executeBU(cred, bu, type, keyArr) {
static async #runOnBU(methodName, cred, bu, type, keyArr) {
const properties = await config.getProperties();
let counter_failed = 0;
const buObject = await Cli.getCredentialObject(
Expand All @@ -839,34 +890,37 @@ class Mcdev {
cred = buObject.credential;
bu = buObject.businessUnit;
}
Util.logger.info(`\n :: Executing ${type} on ${cred}/${bu}\n`);
Util.logger.info(`:: ${methodName} ${type} on ${cred}/${bu}`);
MetadataTypeInfo[type].client = auth.getSDK(buObject);
if (Util.OPTIONS.like && Object.keys(Util.OPTIONS.like).length) {
keyArr = await this._retrieveKeysWithLike(type, buObject);
keyArr = await this.#retrieveKeysWithLike(type, buObject);
} else {
MetadataTypeInfo[type].properties = properties;
MetadataTypeInfo[type].buObject = buObject;
}
if (!keyArr || (Array.isArray(keyArr) && !keyArr.length)) {
throw new Error('No keys were provided');
}

// result will be undefined (false) if execute is not supported for the type
if (!(await MetadataTypeInfo[type].execute(keyArr))) {
// result will be undefined (false) if methodName is not supported for the type
if (!(await MetadataTypeInfo[type][methodName](keyArr))) {
counter_failed++;
}
} catch (ex) {
Util.logger.errorStack(ex, 'mcdev.execute failed');
Util.logger.errorStack(ex, 'mcdev.' + methodName + ' failed');
}

return counter_failed === 0 ? true : false;
}

/**
* helper for {@link Mcdev._executeBU}
* helper for {@link Mcdev.#runOnBU}
*
* @param {TYPE.SupportedMetadataTypes} selectedType limit execution to given metadata type
* @param {TYPE.BuObject} buObject properties for auth
* @returns {string[]} keyArr
*/
static async _retrieveKeysWithLike(selectedType, buObject) {
static async #retrieveKeysWithLike(selectedType, buObject) {
const properties = await config.getProperties();

// cache depenencies
Expand Down
12 changes: 6 additions & 6 deletions lib/metadataTypes/Asset.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Asset extends MetadataType {
return { metadata: Object.values(metadata)[0], type: this.definition.type };
}
/**
* helper for {@link retrieve} + {@link retrieveAsTemplate}
* helper for {@link Asset.retrieve} + {@link Asset.retrieveAsTemplate}
*
* @private
* @returns {TYPE.AssetSubType[]} subtype array
Expand Down Expand Up @@ -448,7 +448,7 @@ class Asset extends MetadataType {
);
}
/**
* helper for {@link preDeployTasks}
* helper for {@link Asset.preDeployTasks}
* Some metadata types store their actual content as a separate file, e.g. images
* This method reads these from the local FS stores them in the metadata object allowing to deploy it
*
Expand Down Expand Up @@ -520,7 +520,7 @@ class Asset extends MetadataType {
}

/**
* helper for {@link postDeployTasks}. triggers a refresh of active triggerredSendDefinitions associated with the updated asset-message items. Gets executed if refresh option has been set.
* helper for {@link Asset.postDeployTasks}. triggers a refresh of active triggerredSendDefinitions associated with the updated asset-message items. Gets executed if refresh option has been set.
*
* @private
* @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField
Expand Down Expand Up @@ -866,7 +866,7 @@ class Asset extends MetadataType {
}

/**
* helper for {@link preDeployTasks} that loads extracted code content back into JSON
* helper for {@link Asset.preDeployTasks} that loads extracted code content back into JSON
*
* @param {TYPE.AssetItem} metadata a single asset definition
* @param {string} deployDir directory of deploy files
Expand Down Expand Up @@ -1150,7 +1150,7 @@ class Asset extends MetadataType {
return fileList;
}
/**
* helper for {@link preDeployTasks} that loads extracted code content back into JSON
* helper for {@link Asset.preDeployTasks} that loads extracted code content back into JSON
*
* @param {string} prefix usually the customerkey
* @param {object} metadataSlots metadata.views.html.slots or deeper slots.<>.blocks.<>.slots
Expand Down Expand Up @@ -1230,7 +1230,7 @@ class Asset extends MetadataType {
}
}
/**
* helper for {@link postRetrieveTasks} that finds code content in JSON and extracts it
* helper for {@link Asset.postRetrieveTasks} that finds code content in JSON and extracts it
* to allow saving that separately and formatted
*
* @param {TYPE.AssetItem} metadata a single asset definition
Expand Down
Loading

0 comments on commit 1337dcf

Please sign in to comment.