mcdev 4.1.12 → 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 +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 +434 -29
- 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 +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 +11 -23
- package/lib/util/init.js +64 -0
- 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
|
@@ -21,16 +21,19 @@ class Folder extends MetadataType {
|
|
|
21
21
|
* @param {TYPE.BuObject} buObject properties for auth
|
|
22
22
|
* @param {void} [___] unused parameter
|
|
23
23
|
* @param {string} [key] customer key of single item to retrieve
|
|
24
|
+
* @param {string[]} [contentTypeList] content type of folder
|
|
24
25
|
* @returns {Promise} Promise
|
|
25
26
|
*/
|
|
26
|
-
static async retrieve(retrieveDir, additionalFields, buObject, ___, key) {
|
|
27
|
+
static async retrieve(retrieveDir, additionalFields, buObject, ___, key, contentTypeList) {
|
|
27
28
|
if (key) {
|
|
28
29
|
Util.logger.error(`Folder.retrieve() does not support key parameter`);
|
|
29
30
|
}
|
|
30
|
-
const queryAllFolders = await this.retrieveHelper(additionalFields, true);
|
|
31
|
+
const queryAllFolders = await this.retrieveHelper(additionalFields, true, contentTypeList);
|
|
31
32
|
|
|
32
33
|
if (buObject.eid !== buObject.mid) {
|
|
33
|
-
queryAllFolders.push(
|
|
34
|
+
queryAllFolders.push(
|
|
35
|
+
...(await this.retrieveHelper(additionalFields, false, contentTypeList))
|
|
36
|
+
);
|
|
34
37
|
}
|
|
35
38
|
const sortPairs = toposort(queryAllFolders.map((a) => [a.ParentFolder.ID, a.ID]));
|
|
36
39
|
const idMap = {};
|
|
@@ -150,10 +153,11 @@ class Folder extends MetadataType {
|
|
|
150
153
|
* Retrieves folder metadata for caching
|
|
151
154
|
*
|
|
152
155
|
* @param {TYPE.BuObject} buObject properties for auth
|
|
156
|
+
* @param {string[]} [contentTypeList] content type of folder
|
|
153
157
|
* @returns {Promise} Promise
|
|
154
158
|
*/
|
|
155
|
-
static retrieveForCache(buObject) {
|
|
156
|
-
return this.retrieve(null, null, buObject);
|
|
159
|
+
static retrieveForCache(buObject, contentTypeList) {
|
|
160
|
+
return this.retrieve(null, null, buObject, null, null, contentTypeList);
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
/**
|
|
@@ -515,10 +519,30 @@ class Folder extends MetadataType {
|
|
|
515
519
|
*
|
|
516
520
|
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
517
521
|
* @param {boolean} [queryAllAccounts] which queryAllAccounts setting to use
|
|
522
|
+
* @param {string[]} [contentTypeList] content type of folder
|
|
518
523
|
* @returns {Promise.<object>} soap object
|
|
519
524
|
*/
|
|
520
|
-
static async retrieveHelper(additionalFields, queryAllAccounts) {
|
|
525
|
+
static async retrieveHelper(additionalFields, queryAllAccounts, contentTypeList) {
|
|
521
526
|
const options = { QueryAllAccounts: !!queryAllAccounts };
|
|
527
|
+
if (contentTypeList) {
|
|
528
|
+
for (const contentType of contentTypeList) {
|
|
529
|
+
options.filter = options.filter
|
|
530
|
+
? {
|
|
531
|
+
leftOperand: {
|
|
532
|
+
leftOperand: 'ContentType',
|
|
533
|
+
operator: 'equals',
|
|
534
|
+
rightOperand: contentType,
|
|
535
|
+
},
|
|
536
|
+
operator: 'OR',
|
|
537
|
+
rightOperand: options.filter,
|
|
538
|
+
}
|
|
539
|
+
: {
|
|
540
|
+
leftOperand: 'ContentType',
|
|
541
|
+
operator: 'equals',
|
|
542
|
+
rightOperand: contentType,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
}
|
|
522
546
|
const response = await this.client.soap.retrieveBulk(
|
|
523
547
|
'DataFolder',
|
|
524
548
|
this.getFieldNamesToRetrieve(additionalFields).filter(
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
const TYPE = require('../../types/mcdev.d');
|
|
4
4
|
const MetadataType = require('./MetadataType');
|
|
5
|
+
const Folder = require('./Folder');
|
|
5
6
|
const Util = require('../util/util');
|
|
6
7
|
const cache = require('../util/cache');
|
|
8
|
+
const auth = require('../util/auth');
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* List MetadataType
|
|
@@ -16,12 +18,12 @@ class List extends MetadataType {
|
|
|
16
18
|
*
|
|
17
19
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
18
20
|
* @param {void} [_] unused parameter
|
|
19
|
-
* @param {
|
|
21
|
+
* @param {TYPE.BuObject} buObject properties for auth
|
|
20
22
|
* @param {void} [___] unused parameter
|
|
21
23
|
* @param {string} [key] customer key of single item to retrieve
|
|
22
24
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise
|
|
23
25
|
*/
|
|
24
|
-
static retrieve(retrieveDir, _,
|
|
26
|
+
static async retrieve(retrieveDir, _, buObject, ___, key) {
|
|
25
27
|
/** @type {TYPE.SoapRequestParams} */
|
|
26
28
|
let requestParams = null;
|
|
27
29
|
if (key) {
|
|
@@ -42,21 +44,112 @@ class List extends MetadataType {
|
|
|
42
44
|
},
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
|
-
|
|
47
|
+
const results = await super.retrieveSOAP(retrieveDir, null, requestParams);
|
|
48
|
+
return await this._retrieveParentAllSubs(buObject, results);
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* Gets metadata cache with limited fields and does not store value to disk
|
|
49
52
|
*
|
|
53
|
+
* @param {TYPE.BuObject} buObject properties for auth
|
|
50
54
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
51
55
|
*/
|
|
52
|
-
static async retrieveForCache() {
|
|
53
|
-
const results = await this.retrieve(null);
|
|
56
|
+
static async retrieveForCache(buObject) {
|
|
57
|
+
const results = await this.retrieve(null, null, buObject);
|
|
58
|
+
if (!cache.getCache()?.folder) {
|
|
59
|
+
Util.logger.debug('folders not cached but required for list');
|
|
60
|
+
Util.logger.info(' - Caching dependent Metadata: folder');
|
|
61
|
+
Folder.client = this.client;
|
|
62
|
+
Folder.properties = this.properties;
|
|
63
|
+
const result = await Folder.retrieveForCache(buObject, [
|
|
64
|
+
'list',
|
|
65
|
+
'mysubs',
|
|
66
|
+
'suppression_list',
|
|
67
|
+
'publication',
|
|
68
|
+
'contextual_suppression_list',
|
|
69
|
+
]);
|
|
70
|
+
cache.setMetadata('folder', result.metadata);
|
|
71
|
+
}
|
|
54
72
|
for (const metadataEntry in results.metadata) {
|
|
55
73
|
this.parseMetadata(results.metadata[metadataEntry], true);
|
|
56
74
|
}
|
|
57
75
|
return results;
|
|
58
76
|
}
|
|
59
77
|
|
|
78
|
+
/**
|
|
79
|
+
* helper for @link retrieveForCache and @link retrieve
|
|
80
|
+
*
|
|
81
|
+
* @param {TYPE.BuObject} buObject properties for auth
|
|
82
|
+
* @param {TYPE.MetadataTypeMapObj} results metadata from retrieve for current BU
|
|
83
|
+
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise
|
|
84
|
+
*/
|
|
85
|
+
static async _retrieveParentAllSubs(buObject, results) {
|
|
86
|
+
if (buObject.eid !== buObject.mid) {
|
|
87
|
+
// for caching, we want to get the All Subscriber List from the Parent Account
|
|
88
|
+
Util.logger.debug(' - Checking MasterUnsubscribeBehavior for current BU');
|
|
89
|
+
/** @type {TYPE.BuObject} */
|
|
90
|
+
const buObjectParentBu = {
|
|
91
|
+
eid: this.properties.credentials[buObject.credential].eid,
|
|
92
|
+
mid: this.properties.credentials[buObject.credential].eid,
|
|
93
|
+
businessUnit: Util.parentBuName,
|
|
94
|
+
credential: buObject.credential,
|
|
95
|
+
};
|
|
96
|
+
try {
|
|
97
|
+
this.client = auth.getSDK(buObjectParentBu);
|
|
98
|
+
} catch (ex) {
|
|
99
|
+
Util.logger.error(ex.message);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const buResult = await this.client.soap.retrieve(
|
|
103
|
+
'BusinessUnit',
|
|
104
|
+
['MasterUnsubscribeBehavior'],
|
|
105
|
+
{
|
|
106
|
+
QueryAllAccounts: true,
|
|
107
|
+
filter: {
|
|
108
|
+
leftOperand: 'ID',
|
|
109
|
+
operator: 'equals',
|
|
110
|
+
rightOperand: this.properties.credentials[buObject.credential].eid,
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
const masterUnsubscribeBehavior = buResult.Results[0]?.MasterUnsubscribeBehavior;
|
|
115
|
+
if (masterUnsubscribeBehavior === 'ENTIRE_ENTERPRISE') {
|
|
116
|
+
Util.logger.debug(` - BU uses ParentBU's All Subscriber List`);
|
|
117
|
+
Util.logger.info(
|
|
118
|
+
' - Caching dependent Metadata: All Subscriber list (on _ParentBU_)'
|
|
119
|
+
);
|
|
120
|
+
const metadataParentBu = await this.retrieve(
|
|
121
|
+
null,
|
|
122
|
+
null,
|
|
123
|
+
buObjectParentBu,
|
|
124
|
+
null,
|
|
125
|
+
'All Subscribers'
|
|
126
|
+
);
|
|
127
|
+
// manually set folder path of parent's All Subscriber List to avoid retrieving folders
|
|
128
|
+
for (const key of Object.keys(metadataParentBu.metadata)) {
|
|
129
|
+
metadataParentBu.metadata[key].r__folder_Path = 'my subscribers';
|
|
130
|
+
}
|
|
131
|
+
// find & delete local All Subscriber list to avoid referencing the wrong one
|
|
132
|
+
for (const key of Object.keys(results.metadata)) {
|
|
133
|
+
if (results.metadata[key].ListName === 'All Subscribers') {
|
|
134
|
+
delete results.metadata[key];
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// make sure to overwrite parent bu DEs with local ones
|
|
140
|
+
return {
|
|
141
|
+
metadata: { ...metadataParentBu.metadata, ...results.metadata },
|
|
142
|
+
type: results.type,
|
|
143
|
+
};
|
|
144
|
+
} else if (masterUnsubscribeBehavior === 'BUSINESS_UNIT_ONLY') {
|
|
145
|
+
Util.logger.debug(' - BU uses own All Subscriber List');
|
|
146
|
+
}
|
|
147
|
+
// revert client to current default
|
|
148
|
+
this.client = auth.getSDK(this.buObject);
|
|
149
|
+
}
|
|
150
|
+
return results;
|
|
151
|
+
}
|
|
152
|
+
|
|
60
153
|
/**
|
|
61
154
|
* Delete a metadata item from the specified business unit
|
|
62
155
|
*
|
|
@@ -85,20 +178,25 @@ class List extends MetadataType {
|
|
|
85
178
|
* @returns {TYPE.MetadataTypeItem} Array with one metadata object and one sql string
|
|
86
179
|
*/
|
|
87
180
|
static parseMetadata(metadata, parseForCache) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
metadata.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
181
|
+
if (!metadata.r__folder_Path) {
|
|
182
|
+
// if we cached all subs from parent bu, we don't need to parse the folder path again here
|
|
183
|
+
try {
|
|
184
|
+
metadata.r__folder_Path = cache.searchForField(
|
|
185
|
+
'folder',
|
|
186
|
+
metadata.Category,
|
|
187
|
+
'ID',
|
|
188
|
+
'Path'
|
|
189
|
+
);
|
|
190
|
+
if (!parseForCache) {
|
|
191
|
+
delete metadata.Category;
|
|
192
|
+
}
|
|
193
|
+
} catch (ex) {
|
|
194
|
+
Util.logger.warn(
|
|
195
|
+
` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`
|
|
196
|
+
);
|
|
97
197
|
}
|
|
98
|
-
return metadata;
|
|
99
|
-
} catch (ex) {
|
|
100
|
-
Util.logger.warn(` - List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`);
|
|
101
198
|
}
|
|
199
|
+
return metadata;
|
|
102
200
|
}
|
|
103
201
|
}
|
|
104
202
|
// Assign definition to static attributes
|
|
@@ -532,8 +532,8 @@ class MetadataType {
|
|
|
532
532
|
(filteredByPreDeploy > 0 ? ` / ${filteredByPreDeploy} filtered` : '')
|
|
533
533
|
);
|
|
534
534
|
|
|
535
|
-
// if Results then parse as SOAP
|
|
536
535
|
if (this.definition.bodyIteratorField === 'Results') {
|
|
536
|
+
// if Results then parse as SOAP
|
|
537
537
|
// put in Retrieve Format for parsing
|
|
538
538
|
// todo add handling when response does not contain items.
|
|
539
539
|
// @ts-ignore
|
|
@@ -545,11 +545,12 @@ class MetadataType {
|
|
|
545
545
|
.map((r) => r.Object);
|
|
546
546
|
return this.parseResponseBody({ Results: metadataResults });
|
|
547
547
|
} else {
|
|
548
|
+
// likely comming from one of the many REST APIs
|
|
548
549
|
// put in Retrieve Format for parsing
|
|
549
550
|
// todo add handling when response does not contain items.
|
|
550
551
|
// @ts-ignore
|
|
551
552
|
const metadataResults = createResults.concat(updateResults).filter(Boolean);
|
|
552
|
-
return this.parseResponseBody(
|
|
553
|
+
return this.parseResponseBody(metadataResults);
|
|
553
554
|
}
|
|
554
555
|
}
|
|
555
556
|
|
|
@@ -613,20 +614,7 @@ class MetadataType {
|
|
|
613
614
|
}
|
|
614
615
|
return response;
|
|
615
616
|
} catch (ex) {
|
|
616
|
-
|
|
617
|
-
const errorMsg =
|
|
618
|
-
ex.results && ex.results.length
|
|
619
|
-
? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})`
|
|
620
|
-
: ex.message;
|
|
621
|
-
Util.logger.error(
|
|
622
|
-
` ☇ error creating ${this.definition.type} '${
|
|
623
|
-
metadataEntry[this.definition.keyField]
|
|
624
|
-
}': ${errorMsg}`
|
|
625
|
-
);
|
|
626
|
-
} else {
|
|
627
|
-
throw ex;
|
|
628
|
-
}
|
|
629
|
-
|
|
617
|
+
this._handleSOAPErrors(ex, 'creating', metadataEntry, handleOutside);
|
|
630
618
|
return {};
|
|
631
619
|
}
|
|
632
620
|
}
|
|
@@ -692,22 +680,28 @@ class MetadataType {
|
|
|
692
680
|
}
|
|
693
681
|
return response;
|
|
694
682
|
} catch (ex) {
|
|
695
|
-
|
|
696
|
-
const errorMsg = ex?.json?.Results.length
|
|
697
|
-
? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})`
|
|
698
|
-
: ex.message;
|
|
699
|
-
Util.logger.error(
|
|
700
|
-
` ☇ error updating ${this.definition.type} '${
|
|
701
|
-
metadataEntry[this.definition.keyField]
|
|
702
|
-
}': ${errorMsg}`
|
|
703
|
-
);
|
|
704
|
-
} else {
|
|
705
|
-
throw ex;
|
|
706
|
-
}
|
|
707
|
-
|
|
683
|
+
this._handleSOAPErrors(ex, 'updating', metadataEntry, handleOutside);
|
|
708
684
|
return {};
|
|
709
685
|
}
|
|
710
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
*
|
|
689
|
+
* @param {Error} ex error that occured
|
|
690
|
+
* @param {'creating'|'updating'} msg what to print in the log
|
|
691
|
+
* @param {TYPE.MetadataTypeItem} [metadataEntry] single metadata entry
|
|
692
|
+
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
693
|
+
*/
|
|
694
|
+
static _handleSOAPErrors(ex, msg, metadataEntry, handleOutside) {
|
|
695
|
+
if (handleOutside) {
|
|
696
|
+
throw ex;
|
|
697
|
+
} else {
|
|
698
|
+
const errorMsg = ex?.json?.Results?.length
|
|
699
|
+
? `${ex.json.Results[0].StatusMessage} (Code ${ex.json.Results[0].ErrorCode})`
|
|
700
|
+
: ex.message;
|
|
701
|
+
const name = metadataEntry ? ` '${metadataEntry[this.definition.keyField]}'` : '';
|
|
702
|
+
Util.logger.error(` ☇ error ${msg} ${this.definition.type}${name}: ${errorMsg}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
711
705
|
/**
|
|
712
706
|
* Retrieves SOAP via generic fuel-soap wrapper based metadata of metadata type into local filesystem. executes callback with retrieved metadata
|
|
713
707
|
*
|
|
@@ -720,11 +714,17 @@ class MetadataType {
|
|
|
720
714
|
static async retrieveSOAP(retrieveDir, buObject, requestParams, additionalFields) {
|
|
721
715
|
requestParams = requestParams || {};
|
|
722
716
|
const fields = this.getFieldNamesToRetrieve(additionalFields);
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
717
|
+
let response;
|
|
718
|
+
try {
|
|
719
|
+
response = await this.client.soap.retrieveBulk(
|
|
720
|
+
this.definition.type,
|
|
721
|
+
fields,
|
|
722
|
+
requestParams
|
|
723
|
+
);
|
|
724
|
+
} catch (ex) {
|
|
725
|
+
this._handleSOAPErrors(ex, 'retrieving');
|
|
726
|
+
return {};
|
|
727
|
+
}
|
|
728
728
|
const metadata = this.parseResponseBody(response);
|
|
729
729
|
|
|
730
730
|
if (retrieveDir) {
|
|
@@ -785,9 +785,9 @@ class MetadataType {
|
|
|
785
785
|
`Downloaded: ${overrideType || this.definition.type} (${
|
|
786
786
|
Object.keys(savedMetadata).length
|
|
787
787
|
})` +
|
|
788
|
-
(singleRetrieve
|
|
789
|
-
?
|
|
790
|
-
:
|
|
788
|
+
(singleRetrieve === null
|
|
789
|
+
? ''
|
|
790
|
+
: ` ${color.dim}(Key: ${singleRetrieve})${color.reset}`)
|
|
791
791
|
);
|
|
792
792
|
}
|
|
793
793
|
|
|
@@ -1454,7 +1454,7 @@ class MetadataType {
|
|
|
1454
1454
|
|
|
1455
1455
|
try {
|
|
1456
1456
|
// write to file
|
|
1457
|
-
const targetDirArr =
|
|
1457
|
+
const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir];
|
|
1458
1458
|
|
|
1459
1459
|
for (const targetDir of targetDirArr) {
|
|
1460
1460
|
await File.writeJSONToFile(
|
|
@@ -1580,15 +1580,48 @@ class MetadataType {
|
|
|
1580
1580
|
|
|
1581
1581
|
return true;
|
|
1582
1582
|
} catch (ex) {
|
|
1583
|
-
if (
|
|
1583
|
+
if (handleOutside) {
|
|
1584
|
+
throw ex;
|
|
1585
|
+
} else {
|
|
1584
1586
|
const errorMsg = ex?.results?.length
|
|
1585
1587
|
? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})`
|
|
1586
1588
|
: ex.message;
|
|
1587
1589
|
Util.logger.error(
|
|
1588
1590
|
`- error deleting ${this.definition.type} '${customerKey}': ${errorMsg}`
|
|
1589
1591
|
);
|
|
1590
|
-
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
return false;
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
/**
|
|
1598
|
+
* Delete a data extension from the specified business unit
|
|
1599
|
+
*
|
|
1600
|
+
* @param {TYPE.BuObject} buObject references credentials
|
|
1601
|
+
* @param {string} url endpoint
|
|
1602
|
+
* @param {string} key Identifier of metadata
|
|
1603
|
+
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
1604
|
+
* @returns {boolean} deletion success flag
|
|
1605
|
+
*/
|
|
1606
|
+
static async deleteByKeyREST(buObject, url, key, handleOutside) {
|
|
1607
|
+
const keyObj = {};
|
|
1608
|
+
keyObj[this.definition.keyField] = key;
|
|
1609
|
+
try {
|
|
1610
|
+
this.client.rest.delete(url);
|
|
1611
|
+
if (!handleOutside) {
|
|
1612
|
+
Util.logger.info(`- deleted ${this.definition.type}: ${key}`);
|
|
1613
|
+
}
|
|
1614
|
+
this.postDeleteTasks(buObject, key);
|
|
1615
|
+
|
|
1616
|
+
return true;
|
|
1617
|
+
} catch (ex) {
|
|
1618
|
+
if (handleOutside) {
|
|
1591
1619
|
throw ex;
|
|
1620
|
+
} else {
|
|
1621
|
+
const errorMsg = ex?.results?.length
|
|
1622
|
+
? `${ex.results[0].StatusMessage} (Code ${ex.results[0].ErrorCode})`
|
|
1623
|
+
: ex.message;
|
|
1624
|
+
Util.logger.error(`- error deleting ${this.definition.type} '${key}': ${errorMsg}`);
|
|
1592
1625
|
}
|
|
1593
1626
|
|
|
1594
1627
|
return false;
|
|
@@ -120,7 +120,7 @@ class Query extends MetadataType {
|
|
|
120
120
|
static async preDeployTasks(metadata, deployDir) {
|
|
121
121
|
metadata.queryText = await File.readFilteredFilename(
|
|
122
122
|
deployDir + '/' + this.definition.type,
|
|
123
|
-
metadata.
|
|
123
|
+
metadata[this.definition.keyField] + '.' + this.definition.type + '-meta',
|
|
124
124
|
'sql'
|
|
125
125
|
);
|
|
126
126
|
metadata.targetKey = cache.searchForField(
|
|
@@ -292,7 +292,7 @@ class Query extends MetadataType {
|
|
|
292
292
|
const codeArr = [
|
|
293
293
|
{
|
|
294
294
|
subFolder: null,
|
|
295
|
-
fileName: metadata.
|
|
295
|
+
fileName: metadata[this.definition.keyField],
|
|
296
296
|
fileExt: 'sql',
|
|
297
297
|
content: metadata.queryText,
|
|
298
298
|
},
|
|
@@ -93,7 +93,7 @@ class Script extends MetadataType {
|
|
|
93
93
|
* @returns {Promise.<string>} content for metadata.script
|
|
94
94
|
*/
|
|
95
95
|
static async _mergeCode(metadata, deployDir, templateName) {
|
|
96
|
-
templateName = templateName || metadata.
|
|
96
|
+
templateName = templateName || metadata[this.definition.keyField];
|
|
97
97
|
let code;
|
|
98
98
|
const codePath = File.normalizePath([
|
|
99
99
|
deployDir,
|
|
@@ -275,7 +275,7 @@ class Script extends MetadataType {
|
|
|
275
275
|
delete metadata.script;
|
|
276
276
|
codeArr.push({
|
|
277
277
|
subFolder: null,
|
|
278
|
-
fileName: metadata.
|
|
278
|
+
fileName: metadata[this.definition.keyField],
|
|
279
279
|
fileExt: fileExt,
|
|
280
280
|
content: code,
|
|
281
281
|
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TYPE = require('../../types/mcdev.d');
|
|
4
|
+
const TransactionalMessage = require('./TransactionalMessage');
|
|
5
|
+
const Util = require('../util/util');
|
|
6
|
+
const cache = require('../util/cache');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* TransactionalEmail MetadataType
|
|
10
|
+
*
|
|
11
|
+
* @augments TransactionalMessage
|
|
12
|
+
*/
|
|
13
|
+
class TransactionalEmail extends TransactionalMessage {
|
|
14
|
+
static subType = 'email';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Updates a single item
|
|
18
|
+
*
|
|
19
|
+
* @param {TYPE.MetadataTypeItem} metadata how the item shall look after the update
|
|
20
|
+
* @returns {Promise} Promise
|
|
21
|
+
*/
|
|
22
|
+
static update(metadata) {
|
|
23
|
+
if (metadata.options?.createJourney) {
|
|
24
|
+
// only send this during create or else we might end up with an unexpected outcome
|
|
25
|
+
Util.logger.warn(
|
|
26
|
+
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
|
|
27
|
+
metadata[this.definition.keyField]
|
|
28
|
+
}): Cannot update journey link during update. If you need to relink this item to a new journey please delete and recreate it.`
|
|
29
|
+
);
|
|
30
|
+
delete metadata.options.createJourney;
|
|
31
|
+
}
|
|
32
|
+
return super.update(metadata);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* prepares for deployment
|
|
37
|
+
*
|
|
38
|
+
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
39
|
+
* @returns {TYPE.MetadataTypeItem} Promise
|
|
40
|
+
*/
|
|
41
|
+
static async preDeployTasks(metadata) {
|
|
42
|
+
// asset
|
|
43
|
+
if (metadata.content?.customerKey) {
|
|
44
|
+
// we merely want to be able to show an error if it does not exist
|
|
45
|
+
cache.searchForField(
|
|
46
|
+
'asset',
|
|
47
|
+
metadata.content.customerKey,
|
|
48
|
+
'customerKey',
|
|
49
|
+
'customerKey'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
// subscriptions: dataExtension
|
|
53
|
+
if (metadata.subscriptions?.dataExtension) {
|
|
54
|
+
// we merely want to be able to show an error if it does not exist
|
|
55
|
+
cache.searchForField(
|
|
56
|
+
'dataExtension',
|
|
57
|
+
metadata.subscriptions.dataExtension,
|
|
58
|
+
'CustomerKey',
|
|
59
|
+
'CustomerKey'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
// subscriptions: list
|
|
63
|
+
if (metadata.subscriptions?.r__list_PathName) {
|
|
64
|
+
metadata.subscriptions.list = cache.getListObjectId(
|
|
65
|
+
metadata.subscriptions.r__list_PathName,
|
|
66
|
+
'CustomerKey'
|
|
67
|
+
);
|
|
68
|
+
delete metadata.subscriptions.r__list_PathName;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// journey
|
|
72
|
+
if (metadata.journey?.interactionKey) {
|
|
73
|
+
// ! update & create enpoints dont accept journey.interactionKey. They only allow to create a new journey
|
|
74
|
+
// cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key');
|
|
75
|
+
metadata.options = metadata.options || {};
|
|
76
|
+
metadata.options.createJourney = true; // only send this during create or else we might end up with an unexpected outcome
|
|
77
|
+
delete metadata.journey.interactionKey;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return metadata;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* manages post retrieve steps
|
|
84
|
+
*
|
|
85
|
+
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
86
|
+
* @returns {TYPE.MetadataTypeItem} a single item
|
|
87
|
+
*/
|
|
88
|
+
static postRetrieveTasks(metadata) {
|
|
89
|
+
// asset
|
|
90
|
+
if (metadata.content?.customerKey) {
|
|
91
|
+
try {
|
|
92
|
+
// we merely want to be able to show an error if it does not exist
|
|
93
|
+
cache.searchForField(
|
|
94
|
+
'asset',
|
|
95
|
+
metadata.content.customerKey,
|
|
96
|
+
'customerKey',
|
|
97
|
+
'customerKey'
|
|
98
|
+
);
|
|
99
|
+
} catch (ex) {
|
|
100
|
+
Util.logger.warn(
|
|
101
|
+
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
|
|
102
|
+
metadata[this.definition.keyField]
|
|
103
|
+
}): ${ex.message}.`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// subscriptions: dataExtension
|
|
108
|
+
if (metadata.subscriptions?.dataExtension) {
|
|
109
|
+
try {
|
|
110
|
+
// we merely want to be able to show a warning if it does not exist
|
|
111
|
+
cache.searchForField(
|
|
112
|
+
'dataExtension',
|
|
113
|
+
metadata.subscriptions.dataExtension,
|
|
114
|
+
'CustomerKey',
|
|
115
|
+
'CustomerKey'
|
|
116
|
+
);
|
|
117
|
+
} catch (ex) {
|
|
118
|
+
Util.logger.warn(
|
|
119
|
+
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
|
|
120
|
+
metadata[this.definition.keyField]
|
|
121
|
+
}): ${ex.message}.`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// subscriptions: list
|
|
126
|
+
if (metadata.subscriptions?.list) {
|
|
127
|
+
try {
|
|
128
|
+
// List
|
|
129
|
+
metadata.subscriptions.r__list_PathName = cache.getListPathName(
|
|
130
|
+
metadata.subscriptions.list,
|
|
131
|
+
'CustomerKey'
|
|
132
|
+
);
|
|
133
|
+
delete metadata.subscriptions.list;
|
|
134
|
+
} catch (ex) {
|
|
135
|
+
Util.logger.warn(
|
|
136
|
+
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
|
|
137
|
+
metadata[this.definition.keyField]
|
|
138
|
+
}): ${ex.message}.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// journey
|
|
143
|
+
if (metadata.journey?.interactionKey) {
|
|
144
|
+
try {
|
|
145
|
+
// we merely want to be able to show a warning if it does not exist
|
|
146
|
+
cache.searchForField('interaction', metadata.journey.interactionKey, 'key', 'key');
|
|
147
|
+
} catch (ex) {
|
|
148
|
+
Util.logger.warn(
|
|
149
|
+
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
|
|
150
|
+
metadata[this.definition.keyField]
|
|
151
|
+
}): ${ex.message}.`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return metadata;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Assign definition to static attributes
|
|
161
|
+
TransactionalEmail.definition = require('../MetadataTypeDefinitions').transactionalEmail;
|
|
162
|
+
|
|
163
|
+
module.exports = TransactionalEmail;
|