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/870 execute automation command to start scheduled automations + Feature/1018 add command to pause a scheduled automation #1019

Merged
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
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
Loading