mcdev 4.2.1 → 4.3.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/.github/ISSUE_TEMPLATE/bug.yml +3 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +1 -2
- package/.github/pr-labeler.yml +3 -0
- package/.github/workflows/close_issues_on_merge.yml +18 -0
- package/.github/workflows/pr-labeler.yml +19 -0
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/docs/dist/documentation.md +702 -284
- package/lib/Deployer.js +21 -15
- package/lib/Retriever.js +41 -34
- package/lib/cli.js +36 -6
- package/lib/index.js +56 -10
- package/lib/metadataTypes/AccountUser.js +17 -23
- package/lib/metadataTypes/Asset.js +36 -48
- package/lib/metadataTypes/AttributeGroup.js +1 -2
- package/lib/metadataTypes/Automation.js +75 -37
- package/lib/metadataTypes/Campaign.js +4 -3
- package/lib/metadataTypes/ContentArea.js +2 -3
- package/lib/metadataTypes/DataExtension.js +56 -47
- package/lib/metadataTypes/DataExtensionField.js +9 -12
- package/lib/metadataTypes/DataExtensionTemplate.js +2 -3
- package/lib/metadataTypes/DataExtract.js +1 -2
- package/lib/metadataTypes/DataExtractType.js +1 -2
- package/lib/metadataTypes/Discovery.js +3 -4
- package/lib/metadataTypes/Email.js +20 -6
- package/lib/metadataTypes/EmailSendDefinition.js +40 -39
- package/lib/metadataTypes/EventDefinition.js +29 -2
- package/lib/metadataTypes/FileTransfer.js +1 -2
- package/lib/metadataTypes/Filter.js +1 -2
- package/lib/metadataTypes/Folder.js +12 -14
- package/lib/metadataTypes/FtpLocation.js +1 -2
- package/lib/metadataTypes/ImportFile.js +1 -2
- package/lib/metadataTypes/Interaction.js +743 -12
- package/lib/metadataTypes/List.js +36 -33
- package/lib/metadataTypes/MetadataType.js +170 -124
- package/lib/metadataTypes/MobileCode.js +1 -2
- package/lib/metadataTypes/MobileKeyword.js +1 -2
- package/lib/metadataTypes/Query.js +15 -6
- package/lib/metadataTypes/Role.js +10 -11
- package/lib/metadataTypes/Script.js +2 -5
- package/lib/metadataTypes/SetDefinition.js +1 -2
- package/lib/metadataTypes/TransactionalMessage.js +25 -32
- package/lib/metadataTypes/TransactionalSMS.js +3 -4
- package/lib/metadataTypes/TriggeredSendDefinition.js +232 -56
- package/lib/metadataTypes/definitions/Asset.definition.js +1 -1
- package/lib/metadataTypes/definitions/Automation.definition.js +1 -1
- package/lib/metadataTypes/definitions/DataExtension.definition.js +10 -1
- package/lib/metadataTypes/definitions/Email.definition.js +1 -1
- package/lib/metadataTypes/definitions/EmailSendDefinition.definition.js +1 -1
- package/lib/metadataTypes/definitions/EventDefinition.definition.js +40 -1
- package/lib/metadataTypes/definitions/Folder.definition.js +31 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +1 -1
- package/lib/metadataTypes/definitions/Interaction.definition.js +47 -26
- package/lib/metadataTypes/definitions/List.definition.js +1 -1
- package/lib/metadataTypes/definitions/Query.definition.js +1 -1
- package/lib/metadataTypes/definitions/Script.definition.js +1 -1
- package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +4 -3
- package/lib/metadataTypes/definitions/TransactionalPush.definition.js +4 -3
- package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +4 -3
- package/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js +10 -2
- package/lib/util/auth.js +15 -2
- package/lib/util/cli.js +4 -1
- package/lib/util/file.js +7 -3
- package/lib/util/init.js +62 -0
- package/lib/util/util.js +173 -11
- package/package.json +23 -9
- package/test/dataExtension.test.js +10 -10
- package/test/interaction.test.js +123 -0
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/interaction/testExisting_interaction.interaction-meta.json +266 -0
- package/test/mockRoot/deploy/testInstance/testBU/interaction/testNew_interaction.interaction-meta.json +266 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +0 -3
- package/test/query.test.js +8 -8
- package/test/resourceFactory.js +30 -14
- package/test/resources/1111111/dataExtension/retrieve-response.xml +26 -0
- package/test/resources/9999999/data/v1/customobjectdata/key/childBU_dataextension_test/rowset/get-response.json +13 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +22 -0
- package/test/resources/9999999/eventDefinition/get-expected.json +34 -0
- package/test/resources/9999999/interaction/build-expected.json +260 -0
- package/test/resources/9999999/interaction/get-expected.json +264 -0
- package/test/resources/9999999/interaction/post-expected.json +264 -0
- package/test/resources/9999999/interaction/put-expected.json +264 -0
- package/test/resources/9999999/interaction/template-expected.json +260 -0
- package/test/resources/9999999/interaction/v1/EventDefinitions/get-response.json +43 -0
- package/test/resources/9999999/interaction/v1/interactions/get-response.json +222 -3
- package/test/resources/9999999/interaction/v1/interactions/key_0b76dccf-594c-b6dc-1acf-10c4493dcb84/get-response.json +219 -0
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json +280 -0
- package/test/resources/9999999/interaction/v1/interactions/post-response.json +280 -0
- package/test/resources/9999999/interaction/v1/interactions/put-response.json +280 -0
- package/test/resources/9999999/messaging/v1/email/definitions/post-response.json +1 -1
- package/test/resources/9999999/query/post-expected.sql +1 -1
- package/test/resources/9999999/transactionalEmail/post-expected.json +1 -1
- package/test/resources/9999999/triggeredSendDefinition/retrieve-response.xml +68 -0
- package/test/transactionalEmail.test.js +7 -7
- package/test/transactionalPush.test.js +7 -7
- package/test/transactionalSMS.test.js +7 -7
- package/test/utils.js +50 -0
- package/types/mcdev.d.js +1 -0
|
@@ -5,6 +5,15 @@ const MetadataType = require('./MetadataType');
|
|
|
5
5
|
const File = require('../util/file');
|
|
6
6
|
const cache = require('../util/cache');
|
|
7
7
|
const Mustache = require('mustache');
|
|
8
|
+
/**
|
|
9
|
+
* ensure that Mustache does not escape any characters
|
|
10
|
+
*
|
|
11
|
+
* @param {string} text -
|
|
12
|
+
* @returns {string} text
|
|
13
|
+
*/
|
|
14
|
+
Mustache.escape = function (text) {
|
|
15
|
+
return text;
|
|
16
|
+
};
|
|
8
17
|
|
|
9
18
|
/**
|
|
10
19
|
* Query MetadataType
|
|
@@ -18,11 +27,10 @@ class Query extends MetadataType {
|
|
|
18
27
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
19
28
|
* @param {void} [_] unused parameter
|
|
20
29
|
* @param {void} [__] unused parameter
|
|
21
|
-
* @param {void} [___] unused parameter
|
|
22
30
|
* @param {string} [key] customer key of single item to retrieve
|
|
23
31
|
* @returns {Promise.<{metadata: TYPE.QueryMap, type: string}>} Promise of metadata
|
|
24
32
|
*/
|
|
25
|
-
static async retrieve(retrieveDir, _, __,
|
|
33
|
+
static async retrieve(retrieveDir, _, __, key) {
|
|
26
34
|
await File.initPrettier('sql');
|
|
27
35
|
const objectId = key ? await this._getObjectIdForSingleRetrieve(key) : null;
|
|
28
36
|
return super.retrieveREST(
|
|
@@ -129,8 +137,9 @@ class Query extends MetadataType {
|
|
|
129
137
|
'CustomerKey',
|
|
130
138
|
'CustomerKey'
|
|
131
139
|
);
|
|
132
|
-
|
|
133
|
-
|
|
140
|
+
// folder
|
|
141
|
+
super.setFolderId(metadata);
|
|
142
|
+
|
|
134
143
|
metadata.targetUpdateTypeId =
|
|
135
144
|
this.definition.targetUpdateTypeMapping[metadata.targetUpdateTypeName];
|
|
136
145
|
return metadata;
|
|
@@ -156,7 +165,7 @@ class Query extends MetadataType {
|
|
|
156
165
|
.join('}}}');
|
|
157
166
|
|
|
158
167
|
// replace template variables with their values
|
|
159
|
-
return Mustache.render(code, templateVariables);
|
|
168
|
+
return Mustache.render(code, templateVariables, {}, ['{{{', '}}}']);
|
|
160
169
|
}
|
|
161
170
|
/**
|
|
162
171
|
* helper for {@link MetadataType.buildDefinition}
|
|
@@ -327,7 +336,7 @@ class Query extends MetadataType {
|
|
|
327
336
|
* Standardizes a check for multiple messages but adds query specific filters to error texts
|
|
328
337
|
*
|
|
329
338
|
* @param {object} ex response payload from REST API
|
|
330
|
-
* @returns {string[]} formatted Error Message
|
|
339
|
+
* @returns {string[] | void} formatted Error Message
|
|
331
340
|
*/
|
|
332
341
|
static checkForErrors(ex) {
|
|
333
342
|
const errors = super.checkForErrors(ex);
|
|
@@ -16,13 +16,12 @@ class Role extends MetadataType {
|
|
|
16
16
|
*
|
|
17
17
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
18
18
|
* @param {string[]} _ Returns specified fields even if their retrieve definition is not set to true
|
|
19
|
-
* @param {TYPE.BuObject} buObject properties for auth
|
|
20
19
|
* @param {void} [___] unused parameter
|
|
21
20
|
* @param {string} [key] customer key of single item to retrieve
|
|
22
21
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Metadata store object
|
|
23
22
|
*/
|
|
24
|
-
static async retrieve(retrieveDir, _,
|
|
25
|
-
if (retrieveDir && buObject.eid !== buObject.mid) {
|
|
23
|
+
static async retrieve(retrieveDir, _, ___, key) {
|
|
24
|
+
if (retrieveDir && this.buObject.eid !== this.buObject.mid) {
|
|
26
25
|
// don't run for BUs other than Parent BU
|
|
27
26
|
// this check does not work during caching
|
|
28
27
|
Util.logger.info(' - Skipping Role retrieval on non-parent BU');
|
|
@@ -67,10 +66,11 @@ class Role extends MetadataType {
|
|
|
67
66
|
if (retrieveDir) {
|
|
68
67
|
const savedMetadata = await super.saveResults(parsed, retrieveDir, null);
|
|
69
68
|
Util.logger.info(
|
|
70
|
-
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
|
|
69
|
+
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
70
|
+
Util.getKeysString(key)
|
|
71
71
|
);
|
|
72
72
|
if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) {
|
|
73
|
-
await this.document(
|
|
73
|
+
await this.document(savedMetadata);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
return { metadata: parsed, type: this.definition.type };
|
|
@@ -113,12 +113,11 @@ class Role extends MetadataType {
|
|
|
113
113
|
/**
|
|
114
114
|
* Creates markdown documentation of all roles
|
|
115
115
|
*
|
|
116
|
-
* @param {TYPE.BuObject} buObject properties for auth
|
|
117
116
|
* @param {TYPE.MetadataTypeMap} [metadata] role definitions
|
|
118
117
|
* @returns {Promise.<void>} -
|
|
119
118
|
*/
|
|
120
|
-
static async document(
|
|
121
|
-
if (buObject.eid !== buObject.mid) {
|
|
119
|
+
static async document(metadata) {
|
|
120
|
+
if (this.buObject.eid !== this.buObject.mid) {
|
|
122
121
|
Util.logger.error(
|
|
123
122
|
`Roles can only be retrieved & documented for the ${Util.parentBuName}`
|
|
124
123
|
);
|
|
@@ -129,7 +128,7 @@ class Role extends MetadataType {
|
|
|
129
128
|
metadata = this.readBUMetadataForType(
|
|
130
129
|
File.normalizePath([
|
|
131
130
|
this.properties.directories.retrieve,
|
|
132
|
-
buObject.credential,
|
|
131
|
+
this.buObject.credential,
|
|
133
132
|
Util.parentBuName,
|
|
134
133
|
]),
|
|
135
134
|
true
|
|
@@ -159,7 +158,7 @@ class Role extends MetadataType {
|
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
// Create output markdown
|
|
162
|
-
let output = `# Permission Overview - ${buObject.credential}\n\n`;
|
|
161
|
+
let output = `# Permission Overview - ${this.buObject.credential}\n\n`;
|
|
163
162
|
output += `> **Legend**
|
|
164
163
|
>
|
|
165
164
|
> <hr>
|
|
@@ -211,7 +210,7 @@ class Role extends MetadataType {
|
|
|
211
210
|
output += '\n';
|
|
212
211
|
}
|
|
213
212
|
try {
|
|
214
|
-
const filename = buObject.credential;
|
|
213
|
+
const filename = this.buObject.credential;
|
|
215
214
|
// write to disk
|
|
216
215
|
await File.writeToFile(directory, filename + '.roles', 'md', output);
|
|
217
216
|
Util.logger.info(`Created ${File.normalizePath([directory, filename])}.roles.md`);
|
|
@@ -4,7 +4,6 @@ const TYPE = require('../../types/mcdev.d');
|
|
|
4
4
|
const MetadataType = require('./MetadataType');
|
|
5
5
|
const Util = require('../util/util');
|
|
6
6
|
const File = require('../util/file');
|
|
7
|
-
const cache = require('../util/cache');
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* Script MetadataType
|
|
@@ -19,11 +18,10 @@ class Script extends MetadataType {
|
|
|
19
18
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
20
19
|
* @param {void} [_] unused parameter
|
|
21
20
|
* @param {void} [__] unused parameter
|
|
22
|
-
* @param {void} [___] unused parameter
|
|
23
21
|
* @param {string} [key] customer key of single item to retrieve
|
|
24
22
|
* @returns {Promise.<{metadata: TYPE.ScriptMap, type: string}>} Promise
|
|
25
23
|
*/
|
|
26
|
-
static async retrieve(retrieveDir, _, __,
|
|
24
|
+
static async retrieve(retrieveDir, _, __, key) {
|
|
27
25
|
await File.initPrettier('ssjs');
|
|
28
26
|
return super.retrieveREST(retrieveDir, '/automation/v1/scripts/', null, null, key);
|
|
29
27
|
}
|
|
@@ -128,8 +126,7 @@ class Script extends MetadataType {
|
|
|
128
126
|
*/
|
|
129
127
|
static async preDeployTasks(metadata, dir) {
|
|
130
128
|
// folder
|
|
131
|
-
|
|
132
|
-
delete metadata.r__folder_Path;
|
|
129
|
+
super.setFolderId(metadata);
|
|
133
130
|
|
|
134
131
|
// code
|
|
135
132
|
metadata.script = await this._mergeCode(metadata, dir);
|
|
@@ -15,11 +15,10 @@ class SetDefinition extends MetadataType {
|
|
|
15
15
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
16
16
|
* @param {void} [_] unused parameter
|
|
17
17
|
* @param {void} [__] unused parameter
|
|
18
|
-
* @param {void} [___] unused parameter
|
|
19
18
|
* @param {string} [key] customer key of single item to retrieve
|
|
20
19
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise
|
|
21
20
|
*/
|
|
22
|
-
static retrieve(retrieveDir, _, __,
|
|
21
|
+
static retrieve(retrieveDir, _, __, key) {
|
|
23
22
|
return super.retrieveREST(
|
|
24
23
|
retrieveDir,
|
|
25
24
|
'/hub/v1/contacts/schema/setDefinitions',
|
|
@@ -19,11 +19,10 @@ class TransactionalMessage extends MetadataType {
|
|
|
19
19
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
20
20
|
* @param {void} [_] unused parameter
|
|
21
21
|
* @param {void} [__] unused parameter
|
|
22
|
-
* @param {void} [___] unused parameter
|
|
23
22
|
* @param {string} [key] customer key of single item to retrieve
|
|
24
23
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
25
24
|
*/
|
|
26
|
-
static async retrieve(retrieveDir, _, __,
|
|
25
|
+
static async retrieve(retrieveDir, _, __, key) {
|
|
27
26
|
let keyList;
|
|
28
27
|
const baseUri = '/messaging/v1/' + this.subType + '/definitions/';
|
|
29
28
|
if (key) {
|
|
@@ -31,20 +30,16 @@ class TransactionalMessage extends MetadataType {
|
|
|
31
30
|
keyList = [key];
|
|
32
31
|
} else {
|
|
33
32
|
// Retrieve all
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
` - Filtered ${this.definition.type} with status 'deleted': ${filteredCount} (downloaded but not saved to disk)`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
33
|
+
// * keep deleted items for caching (and to decide on update vs create)
|
|
34
|
+
const parsed = (
|
|
35
|
+
await this.retrieveREST(
|
|
36
|
+
null,
|
|
37
|
+
baseUri + (retrieveDir ? '?$filter=status%20neq%20deleted' : '')
|
|
38
|
+
)
|
|
39
|
+
).metadata;
|
|
40
|
+
keyList = Object.keys(parsed);
|
|
45
41
|
}
|
|
46
|
-
|
|
47
|
-
// get all sms with additional details not given by the list endpoint
|
|
42
|
+
// get all transactionalX items with additional details not given by the list endpoint
|
|
48
43
|
const details = (
|
|
49
44
|
await Promise.all(
|
|
50
45
|
keyList.map(async (key) => {
|
|
@@ -57,20 +52,17 @@ class TransactionalMessage extends MetadataType {
|
|
|
57
52
|
)
|
|
58
53
|
).filter(Boolean);
|
|
59
54
|
const parsed = this.parseResponseBody({ definitions: details });
|
|
55
|
+
let savedMetadata;
|
|
56
|
+
if (retrieveDir) {
|
|
57
|
+
// * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
|
|
58
|
+
savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
|
|
59
|
+
Util.logger.info(
|
|
60
|
+
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
61
|
+
Util.getKeysString(key)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
|
|
63
|
-
// defined colors for optionally printing the keys we filtered by
|
|
64
|
-
const color = {
|
|
65
|
-
reset: '\x1B[0m',
|
|
66
|
-
dim: '\x1B[2m',
|
|
67
|
-
};
|
|
68
|
-
Util.logger.info(
|
|
69
|
-
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
70
|
-
(key === null ? '' : ` ${color.dim}(Key: ${key})${color.reset}`)
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return { metadata: savedMetadata, type: this.definition.type };
|
|
65
|
+
return { metadata: savedMetadata || parsed, type: this.definition.type };
|
|
74
66
|
}
|
|
75
67
|
|
|
76
68
|
/**
|
|
@@ -79,7 +71,10 @@ class TransactionalMessage extends MetadataType {
|
|
|
79
71
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
80
72
|
*/
|
|
81
73
|
static retrieveForCache() {
|
|
82
|
-
|
|
74
|
+
// the call to /messaging/v1/email/definitions/ does not return definitionId
|
|
75
|
+
// definitionId is required for resolving dependencies on interactions.
|
|
76
|
+
// we should therefore use the already defined retrieve method
|
|
77
|
+
return this.retrieve();
|
|
83
78
|
}
|
|
84
79
|
/**
|
|
85
80
|
* Updates a single item
|
|
@@ -106,13 +101,11 @@ class TransactionalMessage extends MetadataType {
|
|
|
106
101
|
/**
|
|
107
102
|
* Delete a metadata item from the specified business unit
|
|
108
103
|
*
|
|
109
|
-
* @param {TYPE.BuObject} buObject references credentials
|
|
110
104
|
* @param {string} key Identifier of item
|
|
111
105
|
* @returns {Promise.<boolean>} deletion success status
|
|
112
106
|
*/
|
|
113
|
-
static deleteByKey(
|
|
107
|
+
static deleteByKey(key) {
|
|
114
108
|
return super.deleteByKeyREST(
|
|
115
|
-
buObject,
|
|
116
109
|
'/messaging/v1/' + this.subType + '/definitions/' + key,
|
|
117
110
|
key,
|
|
118
111
|
false
|
|
@@ -17,16 +17,15 @@ class TransactionalSMS extends TransactionalMessage {
|
|
|
17
17
|
/**
|
|
18
18
|
* clean up after deleting a metadata item
|
|
19
19
|
*
|
|
20
|
-
* @param {TYPE.BuObject} buObject references credentials
|
|
21
20
|
* @param {string} customerKey Identifier of metadata item
|
|
22
21
|
* @returns {void}
|
|
23
22
|
*/
|
|
24
|
-
static async postDeleteTasks(
|
|
23
|
+
static async postDeleteTasks(customerKey) {
|
|
25
24
|
// delete local copy: retrieve/cred/bu/type/...json
|
|
26
25
|
const fileName = File.normalizePath([
|
|
27
26
|
this.properties.directories.retrieve,
|
|
28
|
-
buObject.credential,
|
|
29
|
-
buObject.businessUnit,
|
|
27
|
+
this.buObject.credential,
|
|
28
|
+
this.buObject.businessUnit,
|
|
30
29
|
this.definition.type,
|
|
31
30
|
`${customerKey}.${this.definition.type}-meta.`,
|
|
32
31
|
]);
|
|
@@ -5,6 +5,12 @@ const MetadataType = require('./MetadataType');
|
|
|
5
5
|
const Util = require('../util/util');
|
|
6
6
|
const cache = require('../util/cache');
|
|
7
7
|
|
|
8
|
+
const cacheTypes = {
|
|
9
|
+
asset: require('./Asset'),
|
|
10
|
+
folder: require('./Folder'),
|
|
11
|
+
list: require('./List'),
|
|
12
|
+
};
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* MessageSendActivity MetadataType
|
|
10
16
|
*
|
|
@@ -17,26 +23,16 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
17
23
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
18
24
|
* @param {void} [_] unused parameter
|
|
19
25
|
* @param {void} [__] unused parameter
|
|
20
|
-
* @param {void} [___] unused parameter
|
|
21
26
|
* @param {string} [key] customer key of single item to retrieve
|
|
22
27
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of metadata
|
|
23
28
|
*/
|
|
24
|
-
static retrieve(retrieveDir, _, __,
|
|
29
|
+
static retrieve(retrieveDir, _, __, key) {
|
|
25
30
|
/** @type {TYPE.SoapRequestParams} */
|
|
26
31
|
let requestParams = {
|
|
27
32
|
filter: {
|
|
28
|
-
leftOperand:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
operator: 'equals',
|
|
32
|
-
rightOperand: false,
|
|
33
|
-
},
|
|
34
|
-
operator: 'AND',
|
|
35
|
-
rightOperand: {
|
|
36
|
-
leftOperand: 'TriggeredSendStatus',
|
|
37
|
-
operator: 'IN',
|
|
38
|
-
rightOperand: ['New', 'Active', 'Inactive'], // New, Active=Running, Inactive=Paused, (Deleted)
|
|
39
|
-
},
|
|
33
|
+
leftOperand: 'TriggeredSendStatus',
|
|
34
|
+
operator: 'IN',
|
|
35
|
+
rightOperand: ['New', 'Active', 'Inactive', 'Moved', 'Canceled'], // New, Active=Running, Inactive=Paused, (Deleted)
|
|
40
36
|
},
|
|
41
37
|
};
|
|
42
38
|
if (key) {
|
|
@@ -54,7 +50,7 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
54
50
|
};
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
return super.retrieveSOAP(retrieveDir,
|
|
53
|
+
return super.retrieveSOAP(retrieveDir, requestParams);
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
/**
|
|
@@ -71,73 +67,75 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
71
67
|
* Updates a single TSD.
|
|
72
68
|
*
|
|
73
69
|
* @param {TYPE.MetadataTypeItem} metadata single metadata entry
|
|
70
|
+
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
74
71
|
* @returns {Promise} Promise
|
|
75
72
|
*/
|
|
76
|
-
static update(metadata) {
|
|
73
|
+
static update(metadata, handleOutside) {
|
|
77
74
|
// * in case of update and active definition, we need to pause first.
|
|
78
|
-
// * this should be done manually to not accidentally
|
|
79
|
-
return super.updateSOAP(metadata);
|
|
75
|
+
// * this should be done manually to not accidentally pause production queues without restarting them
|
|
76
|
+
return super.updateSOAP(metadata, null, handleOutside);
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
/**
|
|
83
80
|
* Delete a metadata item from the specified business unit
|
|
84
81
|
*
|
|
85
|
-
* @param {TYPE.BuObject} buObject references credentials
|
|
86
82
|
* @param {string} customerKey Identifier of data extension
|
|
87
83
|
* @returns {Promise.<boolean>} deletion success status
|
|
88
84
|
*/
|
|
89
|
-
static deleteByKey(
|
|
90
|
-
return super.deleteByKeySOAP(
|
|
85
|
+
static deleteByKey(customerKey) {
|
|
86
|
+
return super.deleteByKeySOAP(customerKey, false);
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
/**
|
|
94
|
-
*
|
|
90
|
+
* manages post retrieve steps
|
|
95
91
|
*
|
|
96
|
-
* @
|
|
97
|
-
* @
|
|
98
|
-
* @returns {boolean} if false, do not save
|
|
99
|
-
* @memberof MetadataType
|
|
92
|
+
* @param {TYPE.MetadataTypeItem} metadata a single query
|
|
93
|
+
* @returns {TYPE.MetadataTypeItem} Array with one metadata object and one query string
|
|
100
94
|
*/
|
|
101
|
-
static
|
|
95
|
+
static postRetrieveTasks(metadata) {
|
|
96
|
+
return this.parseMetadata(metadata);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* generic script that retrieves the folder path from cache and updates the given metadata with it after retrieve
|
|
100
|
+
*
|
|
101
|
+
* @param {TYPE.MetadataTypeItem} metadata a single script activity definition
|
|
102
|
+
*/
|
|
103
|
+
static setFolderPath(metadata) {
|
|
102
104
|
try {
|
|
103
|
-
|
|
104
|
-
const folderPath = cache.searchForField(
|
|
105
|
+
metadata.r__folder_Path = cache.searchForField(
|
|
105
106
|
'folder',
|
|
106
|
-
|
|
107
|
+
metadata[this.definition.folderIdField],
|
|
107
108
|
'ID',
|
|
108
109
|
'Path'
|
|
109
110
|
);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
delete metadata[this.definition.folderIdField];
|
|
112
|
+
} catch (ex) {
|
|
113
|
+
Util.logger.verbose(
|
|
114
|
+
` - skipping ${this.definition.type} '${metadata[this.definition.nameField]}' (${
|
|
115
|
+
metadata[this.definition.keyField]
|
|
116
|
+
}): Could not find folder (${ex.message})`
|
|
117
|
+
);
|
|
118
|
+
throw ex;
|
|
117
119
|
}
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* manages post retrieve steps
|
|
123
|
-
*
|
|
124
|
-
* @param {TYPE.MetadataTypeItem} metadata a single query
|
|
125
|
-
* @returns {TYPE.MetadataTypeItem} Array with one metadata object and one query string
|
|
126
|
-
*/
|
|
127
|
-
static postRetrieveTasks(metadata) {
|
|
128
|
-
return this.parseMetadata(metadata);
|
|
129
120
|
}
|
|
130
121
|
/**
|
|
131
122
|
* parses retrieved Metadata before saving
|
|
132
123
|
*
|
|
133
124
|
* @param {TYPE.MetadataTypeItem} metadata a single query activity definition
|
|
134
|
-
* @returns {TYPE.MetadataTypeItem} Array with one metadata object and one sql string
|
|
125
|
+
* @returns {TYPE.MetadataTypeItem | void} Array with one metadata object and one sql string
|
|
135
126
|
*/
|
|
136
127
|
static parseMetadata(metadata) {
|
|
137
128
|
// remove IsPlatformObject, always has to be 'false'
|
|
138
129
|
delete metadata.IsPlatformObject;
|
|
139
130
|
// folder
|
|
140
|
-
|
|
131
|
+
try {
|
|
132
|
+
this.setFolderPath(metadata);
|
|
133
|
+
} catch {
|
|
134
|
+
Util.logger.verbose(
|
|
135
|
+
` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find folder.`
|
|
136
|
+
);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
141
139
|
|
|
142
140
|
// email
|
|
143
141
|
try {
|
|
@@ -164,9 +162,10 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
164
162
|
metadata.r__assetMessage_Key = contentBuilderEmailKey;
|
|
165
163
|
delete metadata.Email;
|
|
166
164
|
} catch {
|
|
167
|
-
Util.logger.
|
|
168
|
-
` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
|
|
165
|
+
Util.logger.verbose(
|
|
166
|
+
` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.`
|
|
169
167
|
);
|
|
168
|
+
return;
|
|
170
169
|
}
|
|
171
170
|
}
|
|
172
171
|
// List (optional)
|
|
@@ -175,9 +174,10 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
175
174
|
metadata.r__list_PathName = cache.getListPathName(metadata.List.ID, 'ID');
|
|
176
175
|
delete metadata.List;
|
|
177
176
|
} catch (ex) {
|
|
178
|
-
Util.logger.
|
|
179
|
-
` - ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
|
|
177
|
+
Util.logger.verbose(
|
|
178
|
+
` - skipping ${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}`
|
|
180
179
|
);
|
|
180
|
+
return;
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -202,8 +202,7 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
202
202
|
// re-add IsPlatformObject, required for visibility
|
|
203
203
|
metadata.IsPlatformObject = false;
|
|
204
204
|
// folder
|
|
205
|
-
|
|
206
|
-
delete metadata.r__folder_Path;
|
|
205
|
+
super.setFolderId(metadata);
|
|
207
206
|
// email
|
|
208
207
|
if (metadata.r__email_Name) {
|
|
209
208
|
// classic
|
|
@@ -243,6 +242,183 @@ class TriggeredSendDefinition extends MetadataType {
|
|
|
243
242
|
|
|
244
243
|
return metadata;
|
|
245
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* TSD-specific refresh method that finds active TSDs and refreshes them
|
|
247
|
+
*
|
|
248
|
+
* @param {string[]} [keyArr] metadata keys
|
|
249
|
+
* @returns {Promise.<void>} -
|
|
250
|
+
*/
|
|
251
|
+
static async refresh(keyArr) {
|
|
252
|
+
console.time('Time'); // eslint-disable-line no-console
|
|
253
|
+
let checkKey = true;
|
|
254
|
+
if (!keyArr) {
|
|
255
|
+
keyArr = await this._findRefreshableItems();
|
|
256
|
+
checkKey = false;
|
|
257
|
+
}
|
|
258
|
+
// then executes pause, publish, start on them.
|
|
259
|
+
const refreshList = [];
|
|
260
|
+
Util.logger.info(`Refreshing ${keyArr.length} ${this.definition.typeName}...`);
|
|
261
|
+
Util.logger.debug(`Refreshing keys: ${keyArr.join(', ')}`);
|
|
262
|
+
for (const key of keyArr) {
|
|
263
|
+
refreshList.push(this._refreshItem(key, checkKey));
|
|
264
|
+
}
|
|
265
|
+
const successCounter = (await Promise.all(refreshList)).filter(Boolean).length;
|
|
266
|
+
Util.logger.info(`Refreshed ${successCounter} of ${keyArr.length}`);
|
|
267
|
+
console.timeEnd('Time'); // eslint-disable-line no-console
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* helper for {@link refresh} that finds active TSDs on the server and filters it by the same rules that {@link retrieve} is using to avoid refreshing TSDs with broken dependencies
|
|
272
|
+
*
|
|
273
|
+
* @returns {Promise.<string[]>} keyArr
|
|
274
|
+
*/
|
|
275
|
+
static async _findRefreshableItems() {
|
|
276
|
+
Util.logger.info('Finding refreshable items...');
|
|
277
|
+
// cache dependencies to test for broken links
|
|
278
|
+
// skip deprecated classic emails here, assuming they cannot be updated and hence are not relevant for {@link refresh}
|
|
279
|
+
const requiredCache = {
|
|
280
|
+
folder: [
|
|
281
|
+
'list',
|
|
282
|
+
'mysubs',
|
|
283
|
+
'suppression_list',
|
|
284
|
+
'publication',
|
|
285
|
+
'contextual_suppression_list',
|
|
286
|
+
'triggered_send',
|
|
287
|
+
'triggered_send_journeybuilder',
|
|
288
|
+
],
|
|
289
|
+
asset: ['message'],
|
|
290
|
+
list: null,
|
|
291
|
+
};
|
|
292
|
+
for (const [type, subTypeArr] of Object.entries(requiredCache)) {
|
|
293
|
+
if (!cache.getCache()?.[type]) {
|
|
294
|
+
Util.logger.info(` - Caching dependent Metadata: ${type}`);
|
|
295
|
+
Util.logSubtypes(subTypeArr);
|
|
296
|
+
cacheTypes[type].client = this.client;
|
|
297
|
+
cacheTypes[type].buObject = this.buObject;
|
|
298
|
+
cacheTypes[type].properties = this.properties;
|
|
299
|
+
|
|
300
|
+
const result = await cacheTypes[type].retrieveForCache(null, subTypeArr);
|
|
301
|
+
cache.setMetadata(type, result.metadata);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// cache ACTIVE triggeredSends from the server
|
|
305
|
+
/** @type {TYPE.SoapRequestParams} */
|
|
306
|
+
const requestParams = {
|
|
307
|
+
filter: {
|
|
308
|
+
leftOperand: 'TriggeredSendStatus',
|
|
309
|
+
operator: 'IN',
|
|
310
|
+
rightOperand: ['dummy', 'Active'], // using equals does not work for this field for an unknown reason and IN requires at least 2 values, hence the 'dummy' entry
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
const metadata = (await super.retrieveSOAP(null, requestParams)).metadata;
|
|
314
|
+
const keyArr = Object.keys(metadata).filter((key) => {
|
|
315
|
+
const test = this.postRetrieveTasks(metadata[key]);
|
|
316
|
+
return test?.CustomerKey || false;
|
|
317
|
+
});
|
|
318
|
+
Util.logger.info(`Found ${keyArr.length} refreshable items.`);
|
|
319
|
+
return keyArr;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* helper for {@link refresh} that pauses, publishes and starts a triggered send
|
|
324
|
+
*
|
|
325
|
+
* @param {string} key external key of triggered send item
|
|
326
|
+
* @param {boolean} checkKey whether to check if key exists on the server
|
|
327
|
+
* @returns {Promise.<boolean>} true if refresh was successful
|
|
328
|
+
*/
|
|
329
|
+
static async _refreshItem(key, checkKey) {
|
|
330
|
+
const item = {};
|
|
331
|
+
let test;
|
|
332
|
+
item[this.definition.keyField] = key;
|
|
333
|
+
// check triggeredSend-key exists on the server AND its status==ACTIVE
|
|
334
|
+
if (checkKey) {
|
|
335
|
+
/** @type {TYPE.SoapRequestParams} */
|
|
336
|
+
const requestParams = {
|
|
337
|
+
filter: {
|
|
338
|
+
leftOperand: 'CustomerKey',
|
|
339
|
+
operator: 'equals',
|
|
340
|
+
rightOperand: key,
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
try {
|
|
344
|
+
test = (
|
|
345
|
+
await super.retrieveSOAP(null, requestParams, [
|
|
346
|
+
'CustomerKey',
|
|
347
|
+
'TriggeredSendStatus',
|
|
348
|
+
])
|
|
349
|
+
)?.metadata;
|
|
350
|
+
} catch (ex) {
|
|
351
|
+
const errorMsg = super.getSOAPErrorMsg(ex);
|
|
352
|
+
Util.logger.error(` ☇ skipping ${this.definition.typeName}: ${key} - ${errorMsg}}`);
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
if (!test[key]) {
|
|
356
|
+
Util.logger.error(
|
|
357
|
+
` ☇ skipping ${this.definition.typeName}: ${key} - not found on server`
|
|
358
|
+
);
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
if (test[key].TriggeredSendStatus !== 'Active') {
|
|
362
|
+
Util.logger.error(
|
|
363
|
+
` ☇ skipping ${this.definition.typeName}: ${key} - refresh only needed for running entries (TriggeredSendStatus=Active)`
|
|
364
|
+
);
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// pause
|
|
370
|
+
try {
|
|
371
|
+
item.TriggeredSendStatus = 'Inactive';
|
|
372
|
+
test = await this.update(item, true);
|
|
373
|
+
if (test.OverallStatus !== 'OK') {
|
|
374
|
+
throw new Error(test.Results[0].StatusMessage);
|
|
375
|
+
}
|
|
376
|
+
delete item.TriggeredSendStatus;
|
|
377
|
+
Util.logger.info(` - paused ${this.definition.typeName}: ${key}`);
|
|
378
|
+
} catch (ex) {
|
|
379
|
+
const errorMsg = super.getSOAPErrorMsg(ex);
|
|
380
|
+
|
|
381
|
+
Util.logger.error(
|
|
382
|
+
` - failed to pause ${this.definition.typeName}: ${key} - ${errorMsg}`
|
|
383
|
+
);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// publish
|
|
388
|
+
try {
|
|
389
|
+
item.RefreshContent = 'true';
|
|
390
|
+
test = await this.update(item, true);
|
|
391
|
+
if (test.OverallStatus !== 'OK') {
|
|
392
|
+
throw new Error(test.Results[0].StatusMessage);
|
|
393
|
+
}
|
|
394
|
+
delete item.RefreshContent;
|
|
395
|
+
Util.logger.info(` - published ${this.definition.typeName}: ${key}`);
|
|
396
|
+
} catch (ex) {
|
|
397
|
+
const errorMsg = super.getSOAPErrorMsg(ex);
|
|
398
|
+
Util.logger.error(
|
|
399
|
+
` - failed to publish ${this.definition.typeName}: ${key} - ${errorMsg}`
|
|
400
|
+
);
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// start
|
|
405
|
+
try {
|
|
406
|
+
item.TriggeredSendStatus = 'Active';
|
|
407
|
+
test = await this.update(item, true);
|
|
408
|
+
if (test.OverallStatus !== 'OK') {
|
|
409
|
+
throw new Error(test.Results[0].StatusMessage);
|
|
410
|
+
}
|
|
411
|
+
delete item.RefreshContent;
|
|
412
|
+
Util.logger.info(` - started ${this.definition.typeName}: ${key}`);
|
|
413
|
+
} catch (ex) {
|
|
414
|
+
const errorMsg = super.getSOAPErrorMsg(ex);
|
|
415
|
+
Util.logger.error(
|
|
416
|
+
` - failed to publish ${this.definition.typeName}: ${key} - ${errorMsg}`
|
|
417
|
+
);
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
246
422
|
}
|
|
247
423
|
|
|
248
424
|
// Assign definition to static attributes
|