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/38 add option --execute and --schedule to fixKeys #1067

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