mcdev 4.1.12 → 4.2.1
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 +1 -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 +3 -0
- package/docs/dist/documentation.md +437 -31
- package/lib/Deployer.js +10 -8
- package/lib/MetadataTypeDefinitions.js +3 -0
- package/lib/MetadataTypeInfo.js +3 -0
- package/lib/Retriever.js +14 -7
- package/lib/cli.js +1 -0
- package/lib/index.js +6 -6
- 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 +13 -12
- package/lib/util/devops.js +4 -4
- package/lib/util/init.config.js +7 -7
- package/lib/util/init.git.js +11 -23
- package/lib/util/init.js +67 -3
- package/lib/util/util.js +20 -12
- 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
|
@@ -14,9 +14,10 @@ const config = {
|
|
|
14
14
|
* loads central properties from config file
|
|
15
15
|
*
|
|
16
16
|
* @param {boolean} [silent] omit throwing errors and print messages; assuming not silent if not set
|
|
17
|
+
* @param {boolean} [isInit] don't tell the user to run init
|
|
17
18
|
* @returns {Promise.<TYPE.Mcdevrc>} central properties object
|
|
18
19
|
*/
|
|
19
|
-
async getProperties(silent) {
|
|
20
|
+
async getProperties(silent, isInit) {
|
|
20
21
|
if (config.properties) {
|
|
21
22
|
return config.properties;
|
|
22
23
|
}
|
|
@@ -65,7 +66,7 @@ const config = {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
|
-
} else if (!silent) {
|
|
69
|
+
} else if (!silent && !isInit) {
|
|
69
70
|
Util.logger.error(
|
|
70
71
|
`${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.`
|
|
71
72
|
);
|
|
@@ -128,17 +129,9 @@ const config = {
|
|
|
128
129
|
const missingFields = [];
|
|
129
130
|
for (const key in defaultProps) {
|
|
130
131
|
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 {
|
|
132
|
+
if (Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
138
133
|
if (!silent && key === 'credentials') {
|
|
139
|
-
if (
|
|
140
|
-
errorMsgs.push(`no Credential defined`);
|
|
141
|
-
} else {
|
|
134
|
+
if (Object.keys(properties.credentials)) {
|
|
142
135
|
for (const cred in properties.credentials) {
|
|
143
136
|
if (cred.includes('/') || cred.includes('\\')) {
|
|
144
137
|
errorMsgs.push(
|
|
@@ -176,6 +169,8 @@ const config = {
|
|
|
176
169
|
solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`);
|
|
177
170
|
}
|
|
178
171
|
}
|
|
172
|
+
} else {
|
|
173
|
+
errorMsgs.push(`no Credential defined`);
|
|
179
174
|
}
|
|
180
175
|
} else if (['directories', 'metaDataTypes', 'options'].includes(key)) {
|
|
181
176
|
for (const subkey in defaultProps[key]) {
|
|
@@ -222,6 +217,12 @@ const config = {
|
|
|
222
217
|
}
|
|
223
218
|
}
|
|
224
219
|
}
|
|
220
|
+
} else {
|
|
221
|
+
errorMsgs.push(`${key}{} missing`);
|
|
222
|
+
solutionSet.add(
|
|
223
|
+
`Run 'mcdev upgrade' to fix missing or changed configuration options`
|
|
224
|
+
);
|
|
225
|
+
missingFields.push(key);
|
|
225
226
|
}
|
|
226
227
|
}
|
|
227
228
|
}
|
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
|
@@ -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
|
*
|
|
@@ -277,9 +341,9 @@ const Init = {
|
|
|
277
341
|
let auth;
|
|
278
342
|
try {
|
|
279
343
|
auth = File.readJsonSync(Util.authFileName);
|
|
280
|
-
} catch
|
|
281
|
-
|
|
282
|
-
|
|
344
|
+
} catch {
|
|
345
|
+
// file not found
|
|
346
|
+
auth = [];
|
|
283
347
|
}
|
|
284
348
|
// walk through config credentials and check if the matching credential in the auth file is missing something
|
|
285
349
|
missingCredentials = Object.keys(properties.credentials).filter(
|
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
|
/**
|
|
@@ -355,16 +355,24 @@ const Util = {
|
|
|
355
355
|
* @param {string} cmd to be executed command
|
|
356
356
|
* @param {string[]} [args] list of arguments
|
|
357
357
|
* @param {boolean} [hideOutput] if true, output of command will be hidden from CLI
|
|
358
|
-
* @returns {
|
|
358
|
+
* @returns {string} output of command if hideOutput is true
|
|
359
359
|
*/
|
|
360
360
|
execSync(cmd, args, hideOutput) {
|
|
361
361
|
args = args || [];
|
|
362
362
|
Util.logger.info('⚡ ' + cmd + ' ' + args.join(' '));
|
|
363
363
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
+
}
|
|
368
376
|
},
|
|
369
377
|
/**
|
|
370
378
|
* standardize check to ensure only one result is returned from template search
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcdev",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
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(
|
|
@@ -34,12 +34,23 @@
|
|
|
34
34
|
"templateBuilds": ["retrieve/", "deploy/"]
|
|
35
35
|
},
|
|
36
36
|
"markets": {
|
|
37
|
-
"
|
|
37
|
+
"testSourceMarket": {
|
|
38
38
|
"mid": "9999999",
|
|
39
39
|
"buName": "testBU",
|
|
40
|
+
"secret": "secret",
|
|
40
41
|
"sharedFolder": "/Shared Data Extensions/test",
|
|
41
42
|
"suffix": "_test",
|
|
43
|
+
"description": "bla bla",
|
|
42
44
|
"countryCodeIn": "'test'"
|
|
45
|
+
},
|
|
46
|
+
"testTargetMarket": {
|
|
47
|
+
"mid": "1111111",
|
|
48
|
+
"buName": "testBUTarget",
|
|
49
|
+
"secret": "target secret",
|
|
50
|
+
"sharedFolder": "/Shared Data Extensions/test target",
|
|
51
|
+
"suffix": "_testTarget",
|
|
52
|
+
"description": "foobar",
|
|
53
|
+
"countryCodeIn": "'testTarget'"
|
|
43
54
|
}
|
|
44
55
|
},
|
|
45
56
|
"marketList": {},
|
|
@@ -63,5 +74,5 @@
|
|
|
63
74
|
"triggeredSendDefinition"
|
|
64
75
|
]
|
|
65
76
|
},
|
|
66
|
-
"version": "4.
|
|
77
|
+
"version": "4.2.0"
|
|
67
78
|
}
|