mcdev 4.1.11 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -1
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +2 -3
- package/.nycrc.json +5 -0
- package/README.md +34 -1528
- package/boilerplate/config.json +2 -6
- package/boilerplate/files/.vscode/extensions.json +1 -0
- package/boilerplate/files/.vscode/settings.json +13 -9
- package/boilerplate/forcedUpdates.json +4 -0
- package/docs/dist/documentation.md +434 -29
- package/lib/Deployer.js +10 -8
- package/lib/MetadataTypeDefinitions.js +3 -0
- package/lib/MetadataTypeInfo.js +3 -0
- package/lib/Retriever.js +16 -8
- package/lib/cli.js +1 -0
- package/lib/index.js +5 -5
- package/lib/metadataTypes/AccountUser.js +2 -2
- package/lib/metadataTypes/Asset.js +45 -35
- package/lib/metadataTypes/Automation.js +4 -4
- package/lib/metadataTypes/DataExtension.js +14 -8
- package/lib/metadataTypes/DataExtensionField.js +44 -9
- package/lib/metadataTypes/Discovery.js +5 -5
- package/lib/metadataTypes/Folder.js +30 -6
- package/lib/metadataTypes/List.js +115 -17
- package/lib/metadataTypes/MetadataType.js +73 -40
- package/lib/metadataTypes/Query.js +2 -2
- package/lib/metadataTypes/Script.js +2 -2
- package/lib/metadataTypes/TransactionalEmail.js +163 -0
- package/lib/metadataTypes/TransactionalMessage.js +127 -0
- package/lib/metadataTypes/TransactionalPush.js +77 -0
- package/lib/metadataTypes/TransactionalSMS.js +354 -0
- package/lib/metadataTypes/TriggeredSendDefinition.js +11 -9
- package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +145 -0
- package/lib/metadataTypes/definitions/TransactionalPush.definition.js +109 -0
- package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +103 -0
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +36 -36
- package/lib/util/auth.js +2 -2
- package/lib/util/businessUnit.js +1 -1
- package/lib/util/cli.js +19 -20
- package/lib/util/config.js +10 -10
- package/lib/util/devops.js +4 -4
- package/lib/util/init.config.js +7 -7
- package/lib/util/init.git.js +12 -24
- package/lib/util/init.js +65 -1
- package/lib/util/util.js +25 -14
- package/package.json +19 -12
- package/test/dataExtension.test.js +36 -19
- package/test/mockRoot/.mcdevrc.json +13 -2
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/childBU_dataextension_test.dataExtension-meta.json +27 -7
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.sql +3 -1
- package/test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.json → testNewQuery.query-meta.json} +3 -3
- package/test/mockRoot/deploy/testInstance/testBU/query/{testQuery.query-meta.sql → testNewQuery.query-meta.sql} +0 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +24 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +24 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testExisting_tpush.transactionalPush-meta.json +18 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalPush/testNew_tpush.transactionalPush-meta.json +18 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.amp +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +15 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.amp +4 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +15 -0
- package/test/query.test.js +57 -23
- package/test/resources/1111111/businessUnit/retrieve-response.xml +33 -0
- package/test/resources/1111111/list/retrieve-response.xml +39 -0
- package/test/resources/9999999/asset/v1/content/assets/query/post-response.json +72 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +2 -2
- package/test/resources/9999999/automation/v1/queries/get-response.json +2 -2
- package/test/resources/9999999/automation/v1/queries/post-response.json +3 -3
- package/test/resources/9999999/dataExtension/build-expected.json +2 -2
- package/test/resources/9999999/dataFolder/retrieve-response.xml +95 -2
- package/test/resources/9999999/interaction/v1/interactions/get-response.json +296 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/code/get-response.json +32 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +46 -0
- package/test/resources/9999999/list/retrieve-response.xml +78 -0
- package/test/resources/9999999/messaging/v1/email/definitions/get-response.json +15 -0
- package/test/resources/9999999/messaging/v1/email/definitions/post-response.json +19 -0
- package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +26 -0
- package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json +26 -0
- package/test/resources/9999999/messaging/v1/push/definitions/get-response.json +15 -0
- package/test/resources/9999999/messaging/v1/push/definitions/post-response.json +13 -0
- package/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/get-response.json +13 -0
- package/test/resources/9999999/messaging/v1/push/definitions/testExisting_tpush/patch-response.json +13 -0
- package/test/resources/9999999/messaging/v1/sms/definitions/get-response.json +15 -0
- package/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +17 -0
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +18 -0
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json +18 -0
- package/test/resources/9999999/query/build-expected.json +2 -2
- package/test/resources/9999999/query/build-expected.sql +6 -0
- package/test/resources/9999999/query/get-expected.json +1 -1
- package/test/resources/9999999/query/get-expected.sql +6 -0
- package/test/resources/9999999/query/patch-expected.json +1 -1
- package/test/resources/9999999/query/patch-expected.sql +6 -0
- package/test/resources/9999999/query/post-expected.json +3 -3
- package/test/resources/9999999/query/post-expected.sql +4 -0
- package/test/resources/9999999/query/template-expected.json +1 -1
- package/test/resources/9999999/query/template-expected.sql +6 -0
- package/test/resources/9999999/transactionalEmail/build-expected.json +22 -0
- package/test/resources/9999999/transactionalEmail/get-expected.json +24 -0
- package/test/resources/9999999/transactionalEmail/patch-expected.json +24 -0
- package/test/resources/9999999/transactionalEmail/post-expected.json +24 -0
- package/test/resources/9999999/transactionalEmail/template-expected.json +22 -0
- package/test/resources/9999999/transactionalPush/build-expected.json +8 -0
- package/test/resources/9999999/transactionalPush/get-expected.json +11 -0
- package/test/resources/9999999/transactionalPush/patch-expected.json +16 -0
- package/test/resources/9999999/transactionalPush/post-expected.json +16 -0
- package/test/resources/9999999/transactionalPush/template-expected.json +8 -0
- package/test/resources/9999999/transactionalSMS/build-expected.amp +4 -0
- package/test/resources/9999999/transactionalSMS/build-expected.json +13 -0
- package/test/resources/9999999/transactionalSMS/get-expected.amp +4 -0
- package/test/resources/9999999/transactionalSMS/get-expected.json +15 -0
- package/test/resources/9999999/transactionalSMS/patch-expected.amp +4 -0
- package/test/resources/9999999/transactionalSMS/patch-expected.json +15 -0
- package/test/resources/9999999/transactionalSMS/post-expected.amp +4 -0
- package/test/resources/9999999/transactionalSMS/post-expected.json +15 -0
- package/test/resources/9999999/transactionalSMS/template-expected.amp +4 -0
- package/test/resources/9999999/transactionalSMS/template-expected.json +13 -0
- package/test/transactionalEmail.test.js +120 -0
- package/test/transactionalPush.test.js +120 -0
- package/test/transactionalSMS.test.js +149 -0
- package/test/utils.js +57 -8
package/lib/util/cli.js
CHANGED
|
@@ -37,10 +37,7 @@ const Cli = {
|
|
|
37
37
|
*/
|
|
38
38
|
async addExtraCredential(properties) {
|
|
39
39
|
const skipInteraction = Util.skipInteraction;
|
|
40
|
-
if (
|
|
41
|
-
// return null here to avoid seeing 2 error messages for the same issue
|
|
42
|
-
return null;
|
|
43
|
-
} else {
|
|
40
|
+
if (await config.checkProperties(properties)) {
|
|
44
41
|
this.logExistingCredentials(properties);
|
|
45
42
|
Util.logger.info('\nPlease enter your new credentials');
|
|
46
43
|
if (skipInteraction && properties.credentials[skipInteraction.credentialName]) {
|
|
@@ -50,6 +47,9 @@ const Cli = {
|
|
|
50
47
|
return null;
|
|
51
48
|
}
|
|
52
49
|
return this._setCredential(properties, null);
|
|
50
|
+
} else {
|
|
51
|
+
// return null here to avoid seeing 2 error messages for the same issue
|
|
52
|
+
return null;
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
/**
|
|
@@ -357,11 +357,11 @@ const Cli = {
|
|
|
357
357
|
validate: (value) => {
|
|
358
358
|
if (!value || value.trim().length < 10) {
|
|
359
359
|
return 'Please enter a valid tenant identifier';
|
|
360
|
-
} else if (
|
|
361
|
-
return `Please copy the URI directly from the installed package's "API Integration" section. It looks like this: https://a1b2b3xy56z.auth.marketingcloudapis.com/`;
|
|
362
|
-
} else {
|
|
360
|
+
} else if (tenantRegex.test(value.trim())) {
|
|
363
361
|
// all good
|
|
364
362
|
return true;
|
|
363
|
+
} else {
|
|
364
|
+
return `Please copy the URI directly from the installed package's "API Integration" section. It looks like this: https://a1b2b3xy56z.auth.marketingcloudapis.com/`;
|
|
365
365
|
}
|
|
366
366
|
},
|
|
367
367
|
},
|
|
@@ -393,15 +393,19 @@ const Cli = {
|
|
|
393
393
|
*/
|
|
394
394
|
async selectTypes(properties, setTypesArr) {
|
|
395
395
|
let responses;
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
396
|
+
if (setTypesArr) {
|
|
397
|
+
responses = {
|
|
398
|
+
selectedTypes: setTypesArr,
|
|
399
|
+
};
|
|
400
|
+
} else {
|
|
401
|
+
if (Util.logger.level === 'debug') {
|
|
402
402
|
Util.logger.warn(
|
|
403
403
|
'Debug mode enabled. Allowing selection of "disabled" types. Please be aware that these might be unstable.'
|
|
404
404
|
);
|
|
405
|
+
} else {
|
|
406
|
+
Util.logger.info(
|
|
407
|
+
'Run mcdev selectTypes --debug if you need to use "disabled" types.'
|
|
408
|
+
);
|
|
405
409
|
}
|
|
406
410
|
const flattenedDefinitions = [];
|
|
407
411
|
for (const el in MetadataDefinitions) {
|
|
@@ -436,9 +440,8 @@ const Cli = {
|
|
|
436
440
|
? ' \x1B[1;30;40m(non-default)\u001B[0m'
|
|
437
441
|
: ''),
|
|
438
442
|
value: def.type,
|
|
439
|
-
disabled:
|
|
440
|
-
? 'disabled'
|
|
441
|
-
: false,
|
|
443
|
+
disabled:
|
|
444
|
+
Util.logger.level === 'debug' || def.typeRetrieveByDefault ? false : 'disabled',
|
|
442
445
|
// subtypes can be activated through their main type
|
|
443
446
|
checked:
|
|
444
447
|
properties.metaDataTypes.retrieve.includes(def.type) ||
|
|
@@ -475,10 +478,6 @@ const Cli = {
|
|
|
475
478
|
choices: typeChoices,
|
|
476
479
|
},
|
|
477
480
|
]);
|
|
478
|
-
} else {
|
|
479
|
-
responses = {
|
|
480
|
-
selectedTypes: setTypesArr,
|
|
481
|
-
};
|
|
482
481
|
}
|
|
483
482
|
|
|
484
483
|
if (responses.selectedTypes) {
|
package/lib/util/config.js
CHANGED
|
@@ -128,17 +128,9 @@ const config = {
|
|
|
128
128
|
const missingFields = [];
|
|
129
129
|
for (const key in defaultProps) {
|
|
130
130
|
if (Object.prototype.hasOwnProperty.call(defaultProps, key)) {
|
|
131
|
-
if (
|
|
132
|
-
errorMsgs.push(`${key}{} missing`);
|
|
133
|
-
solutionSet.add(
|
|
134
|
-
`Run 'mcdev upgrade' to fix missing or changed configuration options`
|
|
135
|
-
);
|
|
136
|
-
missingFields.push(key);
|
|
137
|
-
} else {
|
|
131
|
+
if (Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
138
132
|
if (!silent && key === 'credentials') {
|
|
139
|
-
if (
|
|
140
|
-
errorMsgs.push(`no Credential defined`);
|
|
141
|
-
} else {
|
|
133
|
+
if (Object.keys(properties.credentials)) {
|
|
142
134
|
for (const cred in properties.credentials) {
|
|
143
135
|
if (cred.includes('/') || cred.includes('\\')) {
|
|
144
136
|
errorMsgs.push(
|
|
@@ -176,6 +168,8 @@ const config = {
|
|
|
176
168
|
solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`);
|
|
177
169
|
}
|
|
178
170
|
}
|
|
171
|
+
} else {
|
|
172
|
+
errorMsgs.push(`no Credential defined`);
|
|
179
173
|
}
|
|
180
174
|
} else if (['directories', 'metaDataTypes', 'options'].includes(key)) {
|
|
181
175
|
for (const subkey in defaultProps[key]) {
|
|
@@ -222,6 +216,12 @@ const config = {
|
|
|
222
216
|
}
|
|
223
217
|
}
|
|
224
218
|
}
|
|
219
|
+
} else {
|
|
220
|
+
errorMsgs.push(`${key}{} missing`);
|
|
221
|
+
solutionSet.add(
|
|
222
|
+
`Run 'mcdev upgrade' to fix missing or changed configuration options`
|
|
223
|
+
);
|
|
224
|
+
missingFields.push(key);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
}
|
package/lib/util/devops.js
CHANGED
|
@@ -113,13 +113,13 @@ const DevOps = {
|
|
|
113
113
|
.filter((file) => filterPaths.some((path) => file.file.startsWith(path)))
|
|
114
114
|
// ensure badly named files on unsupported metadata types are not in our subset
|
|
115
115
|
.filter((/** @type {TYPE.DeltaPkgItem} */ file) => {
|
|
116
|
-
if (
|
|
116
|
+
if (MetadataType[file.type]) {
|
|
117
|
+
return true;
|
|
118
|
+
} else {
|
|
117
119
|
Util.logger.debug(
|
|
118
120
|
`Unknown metadata-type found for (${file.file}): ` + file.type
|
|
119
121
|
);
|
|
120
122
|
return false;
|
|
121
|
-
} else {
|
|
122
|
-
return true;
|
|
123
123
|
}
|
|
124
124
|
})
|
|
125
125
|
.map((/** @type {TYPE.DeltaPkgItem} */ file) => {
|
|
@@ -182,7 +182,7 @@ const DevOps = {
|
|
|
182
182
|
) {
|
|
183
183
|
Util.logger.warn(
|
|
184
184
|
`- ❌ No changes found. Check what branch you are currently on and if the target branch name (${rangeUserInput}${
|
|
185
|
-
range
|
|
185
|
+
range === rangeUserInput ? '' : ' converted to ' + range
|
|
186
186
|
}) was correct`
|
|
187
187
|
);
|
|
188
188
|
return [];
|
package/lib/util/init.config.js
CHANGED
|
@@ -78,10 +78,10 @@ const Init = {
|
|
|
78
78
|
break;
|
|
79
79
|
}
|
|
80
80
|
case 'metaDataTypes.documentOnRetrieve': {
|
|
81
|
-
if (
|
|
82
|
-
properties.metaDataTypes.documentOnRetrieve = [];
|
|
83
|
-
} else {
|
|
81
|
+
if (properties.options.documentOnRetrieve) {
|
|
84
82
|
this._updateLeaf(properties, defaultProps, fieldName);
|
|
83
|
+
} else {
|
|
84
|
+
properties.metaDataTypes.documentOnRetrieve = [];
|
|
85
85
|
}
|
|
86
86
|
delete properties.options.documentOnRetrieve;
|
|
87
87
|
upgradeMsgs.push(
|
|
@@ -217,10 +217,7 @@ const Init = {
|
|
|
217
217
|
Util.boilerplateDirectory,
|
|
218
218
|
'gitignore-template'
|
|
219
219
|
);
|
|
220
|
-
if (
|
|
221
|
-
Util.logger.debug(`Dependency file not found in ${gitignoreFileName}`);
|
|
222
|
-
return false;
|
|
223
|
-
} else {
|
|
220
|
+
if (await File.pathExists(gitignoreFileName)) {
|
|
224
221
|
const fileContent = await File.readFile(gitignoreFileName, 'utf8');
|
|
225
222
|
creationLog.push(
|
|
226
223
|
await this._createIdeConfigFile(
|
|
@@ -229,6 +226,9 @@ const Init = {
|
|
|
229
226
|
fileContent
|
|
230
227
|
)
|
|
231
228
|
);
|
|
229
|
+
} else {
|
|
230
|
+
Util.logger.debug(`Dependency file not found in ${gitignoreFileName}`);
|
|
231
|
+
return false;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// load file list from boilerplate dir and initiate copy process
|
package/lib/util/init.git.js
CHANGED
|
@@ -110,7 +110,7 @@ const Init = {
|
|
|
110
110
|
},
|
|
111
111
|
]);
|
|
112
112
|
}
|
|
113
|
-
if (skipInteraction
|
|
113
|
+
if (skipInteraction?.gitPush === 'true' || responses?.gitPush) {
|
|
114
114
|
Util.execSync('git', ['push', '-u', 'origin', 'master']);
|
|
115
115
|
}
|
|
116
116
|
} else if (remoteBranchesExist === true) {
|
|
@@ -156,21 +156,22 @@ const Init = {
|
|
|
156
156
|
return 'Please enter a valid remote URL';
|
|
157
157
|
} else if (!value.startsWith('http') && !value.startsWith('ssh')) {
|
|
158
158
|
return `Your Git Remote URL should start with 'http' or 'ssh'`;
|
|
159
|
-
} else if (
|
|
160
|
-
return `Your Git Remote URL should end with '.git'`;
|
|
161
|
-
} else {
|
|
159
|
+
} else if (value.endsWith('.git')) {
|
|
162
160
|
// all good
|
|
163
161
|
return true;
|
|
162
|
+
} else {
|
|
163
|
+
return `Your Git Remote URL should end with '.git'`;
|
|
164
164
|
}
|
|
165
165
|
},
|
|
166
166
|
},
|
|
167
167
|
]);
|
|
168
168
|
}
|
|
169
169
|
/* eslint-enable unicorn/prefer-ternary */
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
if (typeof responses.gitRemoteUrl === 'string') {
|
|
171
|
+
responses.gitRemoteUrl = responses.gitRemoteUrl.trim();
|
|
172
|
+
Util.execSync('git', ['remote', 'add', 'origin', responses.gitRemoteUrl]);
|
|
173
|
+
return responses.gitRemoteUrl.split('/').pop().split('.')[0];
|
|
174
|
+
}
|
|
174
175
|
}
|
|
175
176
|
},
|
|
176
177
|
/**
|
|
@@ -251,23 +252,10 @@ const Init = {
|
|
|
251
252
|
* @returns {Promise.<{'user.name': string, 'user.email': string}>} user.name and user.email
|
|
252
253
|
*/
|
|
253
254
|
async _getGitConfigUser() {
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
delete gitConfigs.values['.git/config'];
|
|
257
|
-
const result = {};
|
|
255
|
+
const names = await git.getConfig('user.name');
|
|
256
|
+
const emails = await git.getConfig('user.email');
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
if (gitConfigs.values[file]['user.name']) {
|
|
261
|
-
result['user.name'] = gitConfigs.values[file]['user.name'];
|
|
262
|
-
}
|
|
263
|
-
if (gitConfigs.values[file]['user.email']) {
|
|
264
|
-
result['user.email'] = gitConfigs.values[file]['user.email'];
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
if (!result['user.name'] || !result['user.email']) {
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
return result;
|
|
258
|
+
return { 'user.name': names.value || '', 'user.email': emails.value || '' };
|
|
271
259
|
},
|
|
272
260
|
};
|
|
273
261
|
|
package/lib/util/init.js
CHANGED
|
@@ -161,6 +161,9 @@ const Init = {
|
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// set up markets and market lists initially
|
|
165
|
+
await Init._initMarkets();
|
|
166
|
+
|
|
164
167
|
// create first commit to backup the project configuration
|
|
165
168
|
if (initGit.status === 'init') {
|
|
166
169
|
Util.logger.info(`Committing initial setup to Git:`);
|
|
@@ -182,6 +185,67 @@ const Init = {
|
|
|
182
185
|
);
|
|
183
186
|
}
|
|
184
187
|
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* helper for @initProject that optionally creates markets and market lists for all BUs
|
|
191
|
+
*/
|
|
192
|
+
async _initMarkets() {
|
|
193
|
+
const skipInteraction = Util.skipInteraction;
|
|
194
|
+
const properties = await config.getProperties(true);
|
|
195
|
+
|
|
196
|
+
// get list of business units
|
|
197
|
+
const firstCredentialName = Object.keys(properties.credentials)[0];
|
|
198
|
+
const businessUnits = Object.keys(
|
|
199
|
+
properties.credentials[firstCredentialName].businessUnits
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// set up empty markets for them
|
|
203
|
+
const markets = {};
|
|
204
|
+
for (const bu of businessUnits) {
|
|
205
|
+
markets[bu] = { suffix: '_' + bu };
|
|
206
|
+
}
|
|
207
|
+
properties.markets = markets;
|
|
208
|
+
|
|
209
|
+
let sourceBuName;
|
|
210
|
+
// set up default deployment market lists
|
|
211
|
+
if (skipInteraction) {
|
|
212
|
+
// don't ask, list all BUs in deployment-target and set deployment-source to ???
|
|
213
|
+
if (!businessUnits.includes(skipInteraction.developmentBu)) {
|
|
214
|
+
Util.logger.warn(
|
|
215
|
+
`Could not find developmentBu=${skipInteraction.developmentBu} in business units. Skipping.`
|
|
216
|
+
);
|
|
217
|
+
delete skipInteraction.developmentBu;
|
|
218
|
+
}
|
|
219
|
+
sourceBuName = skipInteraction.developmentBu || '???';
|
|
220
|
+
if (!skipInteraction.developmentBu) {
|
|
221
|
+
Util.logger.info(
|
|
222
|
+
'Market List "deployment-source" will need to be set up manually. Marking all BUs as target BUs in "deployment-target".'
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
const responses = await inquirer.prompt([
|
|
227
|
+
{
|
|
228
|
+
type: 'list',
|
|
229
|
+
name: 'developmentBu',
|
|
230
|
+
message: 'Please select your development business unit:',
|
|
231
|
+
choices: businessUnits.map((bu) => ({ name: bu, value: bu })),
|
|
232
|
+
},
|
|
233
|
+
]);
|
|
234
|
+
sourceBuName = responses.developmentBu;
|
|
235
|
+
}
|
|
236
|
+
// set source list
|
|
237
|
+
properties.marketList['deployment-source'][firstCredentialName + '/' + sourceBuName] =
|
|
238
|
+
sourceBuName;
|
|
239
|
+
|
|
240
|
+
// set target list
|
|
241
|
+
for (const bu of businessUnits) {
|
|
242
|
+
// filter out source BU & parent BU to ensure they dont get deployed to automatically
|
|
243
|
+
if (bu !== sourceBuName && bu !== '_ParentBU_') {
|
|
244
|
+
properties.marketList['deployment-target'][firstCredentialName + '/' + bu] = bu;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
await File.saveConfigFile(properties);
|
|
248
|
+
},
|
|
185
249
|
/**
|
|
186
250
|
* helper for {@link Init.initProject}
|
|
187
251
|
*
|
|
@@ -202,7 +266,7 @@ const Init = {
|
|
|
202
266
|
},
|
|
203
267
|
]);
|
|
204
268
|
}
|
|
205
|
-
if (skipInteraction
|
|
269
|
+
if (skipInteraction?.downloadBUs === 'true' || responses?.initialRetrieveAll) {
|
|
206
270
|
Util.execSync('mcdev', ['retrieve', bu]);
|
|
207
271
|
|
|
208
272
|
if (gitStatus === 'init') {
|
package/lib/util/util.js
CHANGED
|
@@ -87,10 +87,7 @@ const Util = {
|
|
|
87
87
|
* @returns {void} throws errors if problems were found
|
|
88
88
|
*/
|
|
89
89
|
verifyMarketList(mlName, properties) {
|
|
90
|
-
if (
|
|
91
|
-
// ML does not exist
|
|
92
|
-
throw new Error(`Market List ${mlName} is not defined`);
|
|
93
|
-
} else {
|
|
90
|
+
if (properties.marketList[mlName]) {
|
|
94
91
|
// ML exists, check if it is properly set up
|
|
95
92
|
|
|
96
93
|
// check if BUs in marketList are valid
|
|
@@ -111,10 +108,10 @@ const Util = {
|
|
|
111
108
|
marketArr = [marketArr];
|
|
112
109
|
}
|
|
113
110
|
for (const market of marketArr) {
|
|
114
|
-
if (
|
|
115
|
-
throw new Error(`Market '${market}' is not defined.`);
|
|
116
|
-
} else {
|
|
111
|
+
if (properties.markets[market]) {
|
|
117
112
|
// * markets can be empty or include variables. Nothing we can test here
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error(`Market '${market}' is not defined.`);
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
117
|
}
|
|
@@ -122,6 +119,9 @@ const Util = {
|
|
|
122
119
|
if (!buCounter) {
|
|
123
120
|
throw new Error(`No BUs defined in marketList ${mlName}`);
|
|
124
121
|
}
|
|
122
|
+
} else {
|
|
123
|
+
// ML does not exist
|
|
124
|
+
throw new Error(`Market List ${mlName} is not defined`);
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
127
|
/**
|
|
@@ -130,6 +130,7 @@ const Util = {
|
|
|
130
130
|
* @returns {void}
|
|
131
131
|
*/
|
|
132
132
|
signalFatalError() {
|
|
133
|
+
Util.logger.debug('Util.signalFataError() sets process.exitCode = 1');
|
|
133
134
|
process.exitCode = 1;
|
|
134
135
|
},
|
|
135
136
|
/**
|
|
@@ -354,16 +355,24 @@ const Util = {
|
|
|
354
355
|
* @param {string} cmd to be executed command
|
|
355
356
|
* @param {string[]} [args] list of arguments
|
|
356
357
|
* @param {boolean} [hideOutput] if true, output of command will be hidden from CLI
|
|
357
|
-
* @returns {
|
|
358
|
+
* @returns {string} output of command if hideOutput is true
|
|
358
359
|
*/
|
|
359
360
|
execSync(cmd, args, hideOutput) {
|
|
360
361
|
args = args || [];
|
|
361
362
|
Util.logger.info('⚡ ' + cmd + ' ' + args.join(' '));
|
|
362
363
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
if (hideOutput) {
|
|
365
|
+
// no output displayed to user but instead returned to parsed elsewhere
|
|
366
|
+
return child_process
|
|
367
|
+
.execSync(cmd + ' ' + args.join(' '))
|
|
368
|
+
.toString()
|
|
369
|
+
.trim();
|
|
370
|
+
} else {
|
|
371
|
+
// the following options ensure the user sees the same output and
|
|
372
|
+
// interaction options as if the command was manually run
|
|
373
|
+
child_process.execSync(cmd + ' ' + args.join(' '), { stdio: [0, 1, 2] });
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
367
376
|
},
|
|
368
377
|
/**
|
|
369
378
|
* standardize check to ensure only one result is returned from template search
|
|
@@ -497,7 +506,10 @@ function startLogger() {
|
|
|
497
506
|
*/
|
|
498
507
|
errorStack: function (ex, message) {
|
|
499
508
|
if (message) {
|
|
500
|
-
|
|
509
|
+
// ! this method only sets exitCode=1 if message-param was set
|
|
510
|
+
// if not, then this method purely outputs debug information and should not change the exitCode
|
|
511
|
+
winstonError(message + ': ' + ex.message);
|
|
512
|
+
Util.signalFatalError();
|
|
501
513
|
}
|
|
502
514
|
let stack;
|
|
503
515
|
/* eslint-disable unicorn/prefer-ternary */
|
|
@@ -513,7 +525,6 @@ function startLogger() {
|
|
|
513
525
|
}
|
|
514
526
|
/* eslint-enable unicorn/prefer-ternary */
|
|
515
527
|
myWinston.debug(stack);
|
|
516
|
-
Util.signalFatalError();
|
|
517
528
|
},
|
|
518
529
|
/**
|
|
519
530
|
* errors should cause surrounding applications to take notice
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcdev",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Accenture Salesforce Marketing Cloud DevTools",
|
|
5
5
|
"author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,24 +37,29 @@
|
|
|
37
37
|
"lint-test": "eslint test/**/*.js",
|
|
38
38
|
"upgrade": "npm-check --update",
|
|
39
39
|
"manual-prepare": "husky install",
|
|
40
|
-
"test": "mocha"
|
|
40
|
+
"test": "mocha",
|
|
41
|
+
"coverage": "nyc mocha",
|
|
42
|
+
"version:major": "npm version --no-commit-hooks major",
|
|
43
|
+
"version:minor": "npm version --no-commit-hooks minor",
|
|
44
|
+
"version:patch": "npm version --no-commit-hooks patch"
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
47
|
+
"beauty-amp-core": "0.3.7",
|
|
43
48
|
"cli-progress": "3.11.2",
|
|
44
49
|
"command-exists": "1.2.9",
|
|
45
50
|
"conf": "10.2.0",
|
|
46
51
|
"console.table": "0.10.0",
|
|
47
|
-
"deep-equal": "2.0
|
|
48
|
-
"fs-extra": "
|
|
52
|
+
"deep-equal": "2.1.0",
|
|
53
|
+
"fs-extra": "11.1.0",
|
|
49
54
|
"inquirer": "8.2.2",
|
|
50
55
|
"json-to-table": "4.2.1",
|
|
51
56
|
"mustache": "4.2.0",
|
|
52
57
|
"p-limit": "3.1.0",
|
|
53
|
-
"prettier": "2.
|
|
58
|
+
"prettier": "2.8.0",
|
|
54
59
|
"prettier-plugin-sql": "0.12.1",
|
|
55
60
|
"semver": "7.3.8",
|
|
56
61
|
"sfmc-sdk": "0.6.1",
|
|
57
|
-
"simple-git": "3.
|
|
62
|
+
"simple-git": "3.15.1",
|
|
58
63
|
"toposort": "2.0.2",
|
|
59
64
|
"update-notifier": "5.1.0",
|
|
60
65
|
"winston": "3.8.2",
|
|
@@ -63,21 +68,23 @@
|
|
|
63
68
|
"devDependencies": {
|
|
64
69
|
"assert": "2.0.0",
|
|
65
70
|
"axios-mock-adapter": "1.21.2",
|
|
66
|
-
"chai": "4.3.
|
|
67
|
-
"
|
|
71
|
+
"chai": "4.3.7",
|
|
72
|
+
"chai-files": "1.4.0",
|
|
73
|
+
"eslint": "8.29.0",
|
|
68
74
|
"eslint-config-prettier": "8.5.0",
|
|
69
75
|
"eslint-config-ssjs": "1.1.11",
|
|
70
|
-
"eslint-plugin-jsdoc": "39.6.
|
|
76
|
+
"eslint-plugin-jsdoc": "39.6.4",
|
|
71
77
|
"eslint-plugin-mocha": "10.1.0",
|
|
72
78
|
"eslint-plugin-prettier": "4.2.1",
|
|
73
|
-
"eslint-plugin-unicorn": "
|
|
79
|
+
"eslint-plugin-unicorn": "45.0.1",
|
|
74
80
|
"husky": "8.0.1",
|
|
75
|
-
"jsdoc-to-markdown": "
|
|
76
|
-
"lint-staged": "13.0
|
|
81
|
+
"jsdoc-to-markdown": "8.0.0",
|
|
82
|
+
"lint-staged": "13.1.0",
|
|
77
83
|
"mocha": "10.1.0",
|
|
78
84
|
"mock-fs": "5.2.0",
|
|
79
85
|
"npm-check": "6.0.1",
|
|
80
86
|
"npm-run-all": "4.1.5",
|
|
87
|
+
"nyc": "15.1.0",
|
|
81
88
|
"prettier-eslint": "15.0.1"
|
|
82
89
|
},
|
|
83
90
|
"optionalDependencies": {
|
|
@@ -23,8 +23,8 @@ describe('dataExtension', () => {
|
|
|
23
23
|
'only one dataExtension expected'
|
|
24
24
|
);
|
|
25
25
|
assert.deepEqual(
|
|
26
|
-
await testUtils.
|
|
27
|
-
await testUtils.
|
|
26
|
+
await testUtils.getActualJson('childBU_dataextension_test', 'dataExtension'),
|
|
27
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'retrieve'),
|
|
28
28
|
|
|
29
29
|
'returned metadata was not equal expected'
|
|
30
30
|
);
|
|
@@ -37,6 +37,9 @@ describe('dataExtension', () => {
|
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
39
|
describe('Deploy ================', () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
testUtils.mockSetup(true);
|
|
42
|
+
});
|
|
40
43
|
it('Should create & upsert a data extension', async () => {
|
|
41
44
|
// WHEN
|
|
42
45
|
await handler.deploy('testInstance/testBU', ['dataExtension']);
|
|
@@ -49,19 +52,21 @@ describe('dataExtension', () => {
|
|
|
49
52
|
2,
|
|
50
53
|
'two data extensions expected'
|
|
51
54
|
);
|
|
55
|
+
// insert
|
|
52
56
|
assert.deepEqual(
|
|
53
|
-
await testUtils.
|
|
54
|
-
await testUtils.
|
|
57
|
+
await testUtils.getActualJson('testDataExtension', 'dataExtension'),
|
|
58
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'create'),
|
|
55
59
|
'returned metadata was not equal expected for create'
|
|
56
60
|
);
|
|
61
|
+
// update
|
|
57
62
|
assert.deepEqual(
|
|
58
|
-
await testUtils.
|
|
59
|
-
await testUtils.
|
|
63
|
+
await testUtils.getActualJson('childBU_dataextension_test', 'dataExtension'),
|
|
64
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'update'),
|
|
60
65
|
'returned metadata was not equal expected for update'
|
|
61
66
|
);
|
|
62
67
|
assert.equal(
|
|
63
68
|
Object.values(testUtils.getAPIHistory()).flat().length,
|
|
64
|
-
|
|
69
|
+
12,
|
|
65
70
|
'Unexpected number of requests made'
|
|
66
71
|
);
|
|
67
72
|
return;
|
|
@@ -74,7 +79,7 @@ describe('dataExtension', () => {
|
|
|
74
79
|
'testInstance/testBU',
|
|
75
80
|
'dataExtension',
|
|
76
81
|
['childBU_dataextension_test'],
|
|
77
|
-
'
|
|
82
|
+
'testSourceMarket'
|
|
78
83
|
);
|
|
79
84
|
|
|
80
85
|
// WHEN
|
|
@@ -84,8 +89,11 @@ describe('dataExtension', () => {
|
|
|
84
89
|
'only one dataExtension expected'
|
|
85
90
|
);
|
|
86
91
|
assert.deepEqual(
|
|
87
|
-
await testUtils.
|
|
88
|
-
|
|
92
|
+
await testUtils.getActualTemplateJson(
|
|
93
|
+
'childBU_dataextension_test',
|
|
94
|
+
'dataExtension'
|
|
95
|
+
),
|
|
96
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'template'),
|
|
89
97
|
'returned template was not equal expected'
|
|
90
98
|
);
|
|
91
99
|
// THEN
|
|
@@ -93,11 +101,14 @@ describe('dataExtension', () => {
|
|
|
93
101
|
'testInstance/testBU',
|
|
94
102
|
'dataExtension',
|
|
95
103
|
'childBU_dataextension_test',
|
|
96
|
-
'
|
|
104
|
+
'testTargetMarket'
|
|
97
105
|
);
|
|
98
106
|
assert.deepEqual(
|
|
99
|
-
await testUtils.
|
|
100
|
-
|
|
107
|
+
await testUtils.getActualDeployJson(
|
|
108
|
+
'childBU_dataextension_testTarget',
|
|
109
|
+
'dataExtension'
|
|
110
|
+
),
|
|
111
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'),
|
|
101
112
|
'returned deployment file was not equal expected'
|
|
102
113
|
);
|
|
103
114
|
assert.equal(
|
|
@@ -115,7 +126,7 @@ describe('dataExtension', () => {
|
|
|
115
126
|
'testInstance/testBU',
|
|
116
127
|
'dataExtension',
|
|
117
128
|
['childBU_dataextension_test'],
|
|
118
|
-
'
|
|
129
|
+
'testSourceMarket'
|
|
119
130
|
);
|
|
120
131
|
// WHEN
|
|
121
132
|
assert.equal(
|
|
@@ -124,8 +135,11 @@ describe('dataExtension', () => {
|
|
|
124
135
|
'only one dataExtension expected'
|
|
125
136
|
);
|
|
126
137
|
assert.deepEqual(
|
|
127
|
-
await testUtils.
|
|
128
|
-
|
|
138
|
+
await testUtils.getActualTemplateJson(
|
|
139
|
+
'childBU_dataextension_test',
|
|
140
|
+
'dataExtension'
|
|
141
|
+
),
|
|
142
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'template'),
|
|
129
143
|
'returned template was not equal expected'
|
|
130
144
|
);
|
|
131
145
|
// THEN
|
|
@@ -133,12 +147,15 @@ describe('dataExtension', () => {
|
|
|
133
147
|
'testInstance/testBU',
|
|
134
148
|
'dataExtension',
|
|
135
149
|
'childBU_dataextension_test',
|
|
136
|
-
'
|
|
150
|
+
'testTargetMarket'
|
|
137
151
|
);
|
|
138
152
|
|
|
139
153
|
assert.deepEqual(
|
|
140
|
-
await testUtils.
|
|
141
|
-
|
|
154
|
+
await testUtils.getActualDeployJson(
|
|
155
|
+
'childBU_dataextension_testTarget',
|
|
156
|
+
'dataExtension'
|
|
157
|
+
),
|
|
158
|
+
await testUtils.getExpectedJson('9999999', 'dataExtension', 'build'),
|
|
142
159
|
'returned deployment file was not equal expected'
|
|
143
160
|
);
|
|
144
161
|
assert.equal(
|