Skip to content

Commit

Permalink
Merge pull request #1067 from Accenture/feature/38-add_option_--execu…
Browse files Browse the repository at this point in the history
…te_and_--schedule_to_fixKeys

Feature/38 add option `--execute` and  `--schedule` to `fixKeys`
  • Loading branch information
JoernBerkefeld authored Aug 8, 2023
2 parents e7a0246 + 1443054 commit e321ca3
Show file tree
Hide file tree
Showing 27 changed files with 1,424 additions and 63 deletions.
4 changes: 4 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"editorconfig.editorconfig",
"esbenp.prettier-vscode",

// mcdev tests
"hbenl.vscode-mocha-test-adapter",
"IBM.output-colorizer",

// Markdown / Readme.md
"joernberkefeld.markdown-preview-bitbucket-innersource"
]
Expand Down
3 changes: 0 additions & 3 deletions boilerplate/files/.vscode/extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

// List of extensions which should be recommended for users of this workspace.
"recommendations": [
// mcdev vscode extension
"IBM.output-colorizer",

// collaboration
"gruntfuggly.todo-tree",
"aaron-bond.better-comments",
Expand Down
5 changes: 3 additions & 2 deletions docs/dist/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ Provides default functionality that can be overwritten by child metadata type cl
<dt><a href="#Automation.">Automation.(metadataMap, key)</a> ⇒ <code>Promise.&lt;void&gt;</code></dt>
<dd><p>helper for <a href="#Automation.postDeployTasks">postDeployTasks</a></p>
</dd>
<dt><a href="#Automation.">Automation.(metadataMap, originalMetadataMap, key)</a> ⇒ <code>Promise.&lt;{key:string, response:object}&gt;</code></dt>
<dt><a href="#Automation.">Automation.(metadataMap, originalMetadataMap, key, [oldKey])</a> ⇒ <code>Promise.&lt;{key:string, response:object}&gt;</code></dt>
<dd><p>helper for <a href="#Automation.postDeployTasks">postDeployTasks</a></p>
</dd>
<dt><a href="#getUserName">getUserName(userList, item, fieldname)</a> ⇒ <code>string</code></dt>
Expand Down Expand Up @@ -8526,7 +8526,7 @@ helper for [postDeployTasks](#Automation.postDeployTasks)

<a name="Automation."></a>

## Automation.(metadataMap, originalMetadataMap, key) ⇒ <code>Promise.&lt;{key:string, response:object}&gt;</code>
## Automation.(metadataMap, originalMetadataMap, key, [oldKey]) ⇒ <code>Promise.&lt;{key:string, response:object}&gt;</code>
helper for [postDeployTasks](#Automation.postDeployTasks)

**Kind**: global function
Expand All @@ -8537,6 +8537,7 @@ helper for [postDeployTasks](#Automation.postDeployTasks)
| metadataMap | <code>TYPE.AutomationMap</code> | metadata mapped by their keyField |
| originalMetadataMap | <code>TYPE.AutomationMap</code> | metadata to be updated (contains additioanl fields) |
| key | <code>string</code> | current customer key |
| [oldKey] | <code>string</code> | old customer key before fixKey / changeKeyValue / changeKeyField |

<a name="getUserName"></a>

Expand Down
12 changes: 12 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,18 @@ yargs
group: 'Options for fixKeys:',
describe:
'filter metadata components (can include % as wildcard or _ for a single character)',
})
.option('execute', {
type: 'boolean',
group: 'Options for fixKeys:',
describe:
'optional: executes item after deploy; this will run the item once immediately',
})
.option('schedule', {
type: 'boolean',
group: 'Options for fixKeys:',
describe:
'optionally start existing schedule instead of running item once immediately (only works for automations)',
});
},
handler: (argv) => {
Expand Down
68 changes: 42 additions & 26 deletions lib/metadataTypes/Automation.js
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,12 @@ class Automation extends MetadataType {
// Do nothing for now
}
if (metadata.steps) {
let i = 0;

for (const step of metadata.steps) {
const stepNumber = step.stepNumber || step.step;
i++;

const stepNumber = step.stepNumber || step.step || i;
delete step.stepNumber;
delete step.step;

Expand Down Expand Up @@ -491,12 +495,12 @@ class Automation extends MetadataType {
// schedule
const results = await this.retrieve(undefined, undefined, undefined, key);
if (Object.keys(results.metadata).length) {
for (const key of Object.keys(results.metadata)) {
if (this.#isValidSchedule(results.metadata[key])) {
metadataMap[key] = results.metadata[key];
for (const resultKey of Object.keys(results.metadata)) {
if (this.#isValidSchedule(results.metadata[resultKey])) {
metadataMap[resultKey] = results.metadata[resultKey];
} else {
Util.logger.error(
` - skipping ${this.definition.type} ${results.metadata[key].name}: no valid schedule settings found.`
` - skipping ${this.definition.type} ${results.metadata[resultKey].name}: no valid schedule settings found.`
);
}
}
Expand Down Expand Up @@ -878,6 +882,16 @@ class Automation extends MetadataType {
}
return deployable;
}
/**
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP} that removes old files after the key was changed
*
* @private
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
* @returns {void}
*/
static async _postChangeKeyTasks(metadataEntry) {
super._postChangeKeyTasks(metadataEntry, true);
}

/**
* Gets executed after deployment of metadata type
Expand All @@ -888,21 +902,25 @@ class Automation extends MetadataType {
*/
static async postDeployTasks(metadataMap, originalMetadataMap) {
for (const key in metadataMap) {
if (!metadataMap[key].type) {
const item = metadataMap[key];

const oldKey = Util.changedKeysMap?.[this.definition.type]?.[key] || key;
delete Util.changedKeysMap?.[this.definition.type]?.[key];

if (!item.type) {
// create response does not return the type attribute

const scheduleHelper =
metadataMap[key].schedule || metadataMap[key].startSource.schedule;
const scheduleHelper = item.schedule || item.startSource.schedule;

// el.type
metadataMap[key].type = scheduleHelper
item.type = scheduleHelper
? 'scheduled'
: metadataMap[key].fileTrigger
: item.fileTrigger
? 'triggered'
: undefined;

// el.schedule.timezoneName
if (metadataMap[key].type === 'scheduled') {
if (item.type === 'scheduled') {
// not existing for triggered automations
scheduleHelper.timezoneName ||= Util.inverseGet(
this.definition.timeZoneMapping,
Expand All @@ -911,21 +929,17 @@ class Automation extends MetadataType {
}

// el.status
metadataMap[key].status ||= Util.inverseGet(
this.definition.statusMapping,
metadataMap[key].statusId
);
item.status ||= Util.inverseGet(this.definition.statusMapping, item.statusId);
}
// need to put schedule on here if status is scheduled
await Automation.#scheduleAutomation(metadataMap, originalMetadataMap, key);
await Automation.#scheduleAutomation(metadataMap, originalMetadataMap, key, oldKey);

// need to update notifications separately if there are any
await Automation.#updateNotificationInfoREST(metadataMap, key);

// rewrite upsert to retrieve fields
const metadata = metadataMap[key];
if (metadata.steps) {
for (const step of metadata.steps) {
if (item.steps) {
for (const step of item.steps) {
step.name = step.annotation;
delete step.annotation;
}
Expand Down Expand Up @@ -989,19 +1003,21 @@ class Automation extends MetadataType {
* @param {TYPE.AutomationMap} metadataMap metadata mapped by their keyField
* @param {TYPE.AutomationMap} originalMetadataMap metadata to be updated (contains additioanl fields)
* @param {string} key current customer key
* @param {string} [oldKey] old customer key before fixKey / changeKeyValue / changeKeyField
* @returns {Promise.<{key:string, response:object}>} metadata key and API response
*/
static async #scheduleAutomation(metadataMap, originalMetadataMap, key) {
static async #scheduleAutomation(metadataMap, originalMetadataMap, key, oldKey) {
let response = null;
if (originalMetadataMap[key]?.type === 'scheduled') {
oldKey ||= key;
if (originalMetadataMap[oldKey]?.type === 'scheduled') {
// Starting Source == 'Schedule': Try starting the automation
if (originalMetadataMap[key].status === 'Scheduled') {
if (originalMetadataMap[oldKey].status === 'Scheduled') {
let schedule = null;
try {
schedule = this._buildSchedule(originalMetadataMap[key].schedule);
schedule = this._buildSchedule(originalMetadataMap[oldKey].schedule);
} catch (ex) {
Util.logger.error(
`- Could not create schedule for automation '${originalMetadataMap[key].name}' to start it: ${ex.message}`
`- Could not create schedule for automation '${originalMetadataMap[oldKey].name}' to start it: ${ex.message}`
);
}
if (schedule !== null) {
Expand Down Expand Up @@ -1033,7 +1049,7 @@ class Automation extends MetadataType {
(schedule_interval > 1 ? 's' : ''));
Util.logger.warn(
` - scheduled automation '${
originalMetadataMap[key].name
originalMetadataMap[oldKey].name
}' deployed as Active: runs every ${intervalString} starting ${
schedule_StartDateTime.split('T').join(' ').split('.')[0]
} ${schedule_timezoneString}`
Expand All @@ -1048,7 +1064,7 @@ class Automation extends MetadataType {
} else {
Util.logger.info(
Util.getGrayMsg(
` - scheduled automation '${originalMetadataMap[key].name}' deployed as Paused`
` - scheduled automation '${originalMetadataMap[oldKey].name}' deployed as Paused`
)
);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/util/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ const Util = {
* @returns {void}
*/
signalFatalError() {
Util.logger.debug('Util.signalFataError() sets process.exitCode = 1');
process.exitCode = 1;
Util.logger.debug('Util.signalFataError() sets process.exitCode = 1 unless already set');
process.exitCode ||= 1;
},
/**
* SFMC accepts multiple true values for Boolean attributes for which we are checking here.
Expand Down
35 changes: 31 additions & 4 deletions test/resourceFactory.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
const fs = require('fs-extra');
const path = require('node:path');
const { XMLParser } = require('fast-xml-parser');
const { color } = require('../lib/util/util');
const Util = require('../lib/util/util');
const parser = new XMLParser();
const attributeParser = new XMLParser({ ignoreAttributes: false });
let color;

/* eslint-disable unicorn/prefer-ternary */
if (
process.env.VSCODE_AMD_ENTRYPOINT === 'vs/workbench/api/node/extensionHostProcess' ||
process.env.VSCODE_CRASH_REPORTER_PROCESS_TYPE === 'extensionHost'
) {
// when we execute the test in a VSCode extension host, we don't want CLI color codes.
color = new Proxy(
{},
{
/**
* catch-all for color
*
* @returns {string} empty string
*/
get() {
return '';
},
}
);
} else {
// test is executed directly in a command prompt. Use colors.
color = Util.color;
}
/* eslint-enable unicorn/prefer-ternary */

/**
* gets mock SOAP metadata for responding
*
Expand All @@ -26,7 +53,7 @@ exports.loadSOAPRecords = async (mcdevAction, type, mid, filter) => {
if (filterPath) {
/* eslint-disable no-console */
console.log(
`${color.bgYellow}${color.fgBlack}test-warning${
`${color.bgYellow}${color.fgBlack}TEST-WARNING${
color.reset
}: You are loading your reponse from ${
testPath + '-response.xml'
Expand All @@ -42,7 +69,7 @@ exports.loadSOAPRecords = async (mcdevAction, type, mid, filter) => {
}
/* eslint-disable no-console */
console.log(
`${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${
`${color.bgRed}${color.fgBlack}TEST-ERROR${color.reset}: Please create file ${
filterPath ? testPath + filterPath + '-response.xml or ' : ''
}${testPath + '-response.xml'}`
);
Expand Down Expand Up @@ -228,7 +255,7 @@ exports.handleRESTRequest = async (config) => {
} else {
/* eslint-disable no-console */
console.log(
`${color.bgRed}${color.fgBlack}test-error${color.reset}: Please create file ${testPath}.json/.txt`
`${color.bgRed}${color.fgBlack}TEST-ERROR${color.reset}: Please create file ${testPath}.json/.txt`
);
/* eslint-enable no-console */
process.exitCode = 404;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"description": "testing fixKey on an a paused automation",
"key": "testExisting_automation_fixedKey_paused",
"name": "testExisting_automation_fixedKey_paused",
"r__folder_Path": "my automations",
"schedule": {
"endDate": "2022-07-30T00:00:00",
"icalRecur": "FREQ=DAILY;COUNT=1;INTERVAL=1",
"startDate": "2022-07-30T00:00:00",
"timezoneName": "W. Europe Standard Time"
},
"status": "PausedSchedule",
"steps": [
{
"activities": [
{
"name": "testExisting_dataExtract",
"r__type": "dataExtract"
},
{
"name": "testExisting_emailSend",
"r__type": "emailSend"
},
{
"name": "testExisting_fileTransfer",
"r__type": "fileTransfer"
},
{
"name": "testExisting_importFile",
"r__type": "importFile"
},
{
"name": "testExisting_query",
"r__type": "query"
},
{
"name": "testExisting_script",
"r__type": "script"
}
]
}
],
"type": "scheduled"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"description": "testing fixKey on an a scheduled automation",
"key": "testExisting_automation_fixedKey_scheduled",
"name": "testExisting_automation_fixedKey_scheduled",
"r__folder_Path": "my automations",
"schedule": {
"endDate": "2022-07-30T00:00:00",
"icalRecur": "FREQ=DAILY;COUNT=1;INTERVAL=1",
"startDate": "2022-07-30T00:00:00",
"timezoneName": "W. Europe Standard Time"
},
"status": "Scheduled",
"steps": [
{
"activities": [
{
"name": "testExisting_dataExtract",
"r__type": "dataExtract"
},
{
"name": "testExisting_emailSend",
"r__type": "emailSend"
},
{
"name": "testExisting_fileTransfer",
"r__type": "fileTransfer"
},
{
"name": "testExisting_importFile",
"r__type": "importFile"
},
{
"name": "testExisting_query",
"r__type": "query"
},
{
"name": "testExisting_script",
"r__type": "script"
}
]
}
],
"type": "scheduled"
}
Loading

0 comments on commit e321ca3

Please sign in to comment.