mcdev 8.3.1 → 8.4.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/.github/ISSUE_TEMPLATE/bug.yml +1 -0
- package/.github/workflows/close_issues_on_merge.yml +4 -0
- package/.github/workflows/coverage-base-update.yml +2 -2
- package/.github/workflows/coverage-develop-branch.yml +1 -1
- package/.github/workflows/coverage-main-branch.yml +1 -1
- package/.github/workflows/coverage.yml +2 -2
- package/@types/lib/index.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataFilter.d.ts +1 -0
- package/@types/lib/metadataTypes/DataFilter.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataFilterHidden.d.ts +1 -0
- package/@types/lib/metadataTypes/DataFilterHidden.d.ts.map +1 -1
- package/@types/lib/metadataTypes/FileLocation.d.ts +74 -0
- package/@types/lib/metadataTypes/FileLocation.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Journey.d.ts +6 -0
- package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Script.d.ts +14 -0
- package/@types/lib/metadataTypes/Script.d.ts.map +1 -1
- package/@types/lib/metadataTypes/definitions/DataFilter.definition.d.ts +1 -0
- package/@types/lib/metadataTypes/definitions/DataFilterHidden.definition.d.ts +1 -0
- package/@types/lib/metadataTypes/definitions/FileLocation.definition.d.ts +62 -4
- package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +6 -0
- package/@types/lib/metadataTypes/definitions/Script.definition.d.ts +10 -0
- package/lib/cli.js +6 -0
- package/lib/index.js +1 -0
- package/lib/metadataTypes/DataFilter.js +36 -22
- package/lib/metadataTypes/Event.js +2 -2
- package/lib/metadataTypes/FileLocation.js +204 -4
- package/lib/metadataTypes/Journey.js +72 -45
- package/lib/metadataTypes/Script.js +16 -0
- package/lib/metadataTypes/definitions/DataFilter.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataFilterHidden.definition.js +1 -0
- package/lib/metadataTypes/definitions/FileLocation.definition.js +52 -7
- package/lib/metadataTypes/definitions/Journey.definition.js +6 -0
- package/lib/metadataTypes/definitions/Script.definition.js +6 -0
- package/package.json +11 -11
- package/test/general.test.js +11 -11
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/ExactTarget Enhanced FTP.fileLocation-meta.json +5 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/testExisting_fileLocation_aws.fileLocation-meta.json +14 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileLocation/testExisting_fileLocation_exsftp.fileLocation-meta.json +12 -0
- package/test/resourceFactory.js +9 -2
- package/test/resources/9999999/automation/v1/ftplocations/get-response.json +26 -1
- package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/get-response.json +10 -0
- package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/patch-response.json +2 -2
- package/test/resources/9999999/data/v1/filetransferlocation/Salesforce%20Objects%20%26%20Reports/get-response.json +4 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_aws/patch-response.json +18 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_azure/delete-response.json +4 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_azure/get-response.json +18 -0
- package/test/resources/9999999/data/v1/filetransferlocation/testExisting_fileLocation_exsftp/patch-response.json +16 -0
- package/test/resources/9999999/data/v1/filetransferlocations/get-response.json +59 -0
- package/test/resources/9999999/fileLocation/build-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-aws-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-azure-expected.json +14 -0
- package/test/resources/9999999/fileLocation/get-eftp-expected.json +5 -0
- package/test/resources/9999999/fileLocation/get-exsftp-expected.json +12 -0
- package/test/resources/9999999/fileLocation/get-gcp-expected.json +10 -0
- package/test/resources/9999999/fileLocation/get-sor-expected.json +5 -0
- package/test/resources/9999999/fileLocation/patch-aws-expected.json +14 -0
- package/test/resources/9999999/fileLocation/patch-exsftp-expected.json +12 -0
- package/test/resources/9999999/fileLocation/template-expected.json +14 -0
- package/test/resources/9999999/interaction/v1/interactions/get-response-status=Published.json +40 -0
- package/test/type.automation.test.js +14 -14
- package/test/type.dataFilter.test.js +10 -2
- package/test/type.fileLocation.test.js +279 -0
- package/test/type.fileTransfer.test.js +4 -4
- package/test/type.filter.test.js +9 -2
- package/test/type.importFile.test.js +5 -5
- package/test/type.journey.test.js +26 -0
- package/test/type.query.test.js +2 -2
- package/test/type.script.test.js +1 -1
|
@@ -110,8 +110,10 @@ class DataFilter extends MetadataType {
|
|
|
110
110
|
*/
|
|
111
111
|
static async _getFilterFolderIds(recached = false) {
|
|
112
112
|
const fromCache =
|
|
113
|
-
this.cache.folderFilter || cache.getCache().folder
|
|
114
|
-
? Object.values(
|
|
113
|
+
this.cache[this.buObject.mid]?.folderFilter || cache.getCache().folder
|
|
114
|
+
? Object.values(
|
|
115
|
+
this.cache[this.buObject.mid]?.folderFilter || cache.getCache().folder
|
|
116
|
+
)
|
|
115
117
|
.filter((item) => item.ContentType === 'filterdefinition')
|
|
116
118
|
.filter(
|
|
117
119
|
(item) =>
|
|
@@ -135,7 +137,10 @@ class DataFilter extends MetadataType {
|
|
|
135
137
|
Folder.client = this.client;
|
|
136
138
|
Folder.buObject = this.buObject;
|
|
137
139
|
Folder.properties = this.properties;
|
|
138
|
-
this.cache.
|
|
140
|
+
this.cache[this.buObject.mid] ||= {};
|
|
141
|
+
this.cache[this.buObject.mid].folderFilter = (
|
|
142
|
+
await Folder.retrieveForCache(null, subTypeArr)
|
|
143
|
+
).metadata;
|
|
139
144
|
return this._getFilterFolderIds(true);
|
|
140
145
|
}
|
|
141
146
|
/**
|
|
@@ -145,9 +150,9 @@ class DataFilter extends MetadataType {
|
|
|
145
150
|
*/
|
|
146
151
|
static async _getMeasureFolderIds() {
|
|
147
152
|
const fromCache =
|
|
148
|
-
this.cache
|
|
153
|
+
this.cache[this.buObject.mid]?.folderMeasure || cache.getCache().folder
|
|
149
154
|
? Object.values(
|
|
150
|
-
this.cache
|
|
155
|
+
this.cache[this.buObject.mid]?.folderMeasure || cache.getCache().folder
|
|
151
156
|
)
|
|
152
157
|
.filter((item) => item.ContentType === 'measure')
|
|
153
158
|
.map((item) => item.ID)
|
|
@@ -163,8 +168,9 @@ class DataFilter extends MetadataType {
|
|
|
163
168
|
Folder.client = this.client;
|
|
164
169
|
Folder.buObject = this.buObject;
|
|
165
170
|
Folder.properties = this.properties;
|
|
166
|
-
this.cache.
|
|
167
|
-
this.cache
|
|
171
|
+
this.cache[this.buObject.mid] ||= {};
|
|
172
|
+
this.cache[this.buObject.mid].folderMeasure ||= {};
|
|
173
|
+
this.cache[this.buObject.mid].folderMeasure = (
|
|
168
174
|
await Folder.retrieveForCache(null, subTypeArr)
|
|
169
175
|
).metadata;
|
|
170
176
|
return this._getMeasureFolderIds();
|
|
@@ -205,8 +211,8 @@ class DataFilter extends MetadataType {
|
|
|
205
211
|
.map((item) => item.r__source_dataExtension_key)
|
|
206
212
|
.filter(
|
|
207
213
|
(deKey) =>
|
|
208
|
-
!this.cache.dataExtensionField ||
|
|
209
|
-
!this.cache.dataExtensionField[deKey]
|
|
214
|
+
!this.cache[this.buObject.mid]?.dataExtensionField ||
|
|
215
|
+
!this.cache[this.buObject.mid]?.dataExtensionField[deKey]
|
|
210
216
|
)
|
|
211
217
|
.filter(Boolean);
|
|
212
218
|
if (deKeys.length) {
|
|
@@ -246,7 +252,8 @@ class DataFilter extends MetadataType {
|
|
|
246
252
|
* @param {DataExtensionFieldMap} deFieldCache -
|
|
247
253
|
*/
|
|
248
254
|
static saveDataExtensionFieldCacheToMap(deFieldCache) {
|
|
249
|
-
this.cache.
|
|
255
|
+
this.cache[this.buObject.mid] ||= {};
|
|
256
|
+
this.cache[this.buObject.mid].dataExtensionField ||= {};
|
|
250
257
|
|
|
251
258
|
if (!deFieldCache) {
|
|
252
259
|
return;
|
|
@@ -255,11 +262,16 @@ class DataFilter extends MetadataType {
|
|
|
255
262
|
if (!field?.DataExtension?.CustomerKey) {
|
|
256
263
|
continue;
|
|
257
264
|
}
|
|
258
|
-
if (
|
|
265
|
+
if (
|
|
266
|
+
!this.cache[this.buObject.mid].dataExtensionField[field.DataExtension.CustomerKey]
|
|
267
|
+
) {
|
|
259
268
|
/** @type {Object.<string, DataExtensionFieldItem[]>} */
|
|
260
|
-
this.cache.dataExtensionField[field.DataExtension.CustomerKey] =
|
|
269
|
+
this.cache[this.buObject.mid].dataExtensionField[field.DataExtension.CustomerKey] =
|
|
270
|
+
[];
|
|
261
271
|
}
|
|
262
|
-
this.cache.dataExtensionField[field.DataExtension.CustomerKey].push(
|
|
272
|
+
this.cache[this.buObject.mid].dataExtensionField[field.DataExtension.CustomerKey].push(
|
|
273
|
+
field
|
|
274
|
+
);
|
|
263
275
|
}
|
|
264
276
|
}
|
|
265
277
|
/**
|
|
@@ -268,7 +280,7 @@ class DataFilter extends MetadataType {
|
|
|
268
280
|
* @param {DataFilterMap} metadataTypeMap -
|
|
269
281
|
*/
|
|
270
282
|
static async _cacheContactAttributes(metadataTypeMap) {
|
|
271
|
-
if (this.cache
|
|
283
|
+
if (this.cache[this.buObject.mid]?.contactAttributes) {
|
|
272
284
|
return;
|
|
273
285
|
}
|
|
274
286
|
const subscriberFilters = Object.values(metadataTypeMap)
|
|
@@ -279,8 +291,8 @@ class DataFilter extends MetadataType {
|
|
|
279
291
|
const response = await this.client.rest.get('/email/v1/Contacts/Attributes/');
|
|
280
292
|
const keyFieldBackup = this.definition.keyField;
|
|
281
293
|
this.definition.keyField = 'id';
|
|
282
|
-
this.cache.
|
|
283
|
-
this.cache
|
|
294
|
+
this.cache[this.buObject.mid] ||= {};
|
|
295
|
+
this.cache[this.buObject.mid].contactAttributes = this.parseResponseBody(response);
|
|
284
296
|
this.definition.keyField = keyFieldBackup;
|
|
285
297
|
}
|
|
286
298
|
}
|
|
@@ -290,7 +302,7 @@ class DataFilter extends MetadataType {
|
|
|
290
302
|
* @param {DataFilterMap} metadataTypeMap -
|
|
291
303
|
*/
|
|
292
304
|
static async _cacheMeasures(metadataTypeMap) {
|
|
293
|
-
if (this.cache
|
|
305
|
+
if (this.cache?.[this.buObject.mid]?.measures) {
|
|
294
306
|
return;
|
|
295
307
|
}
|
|
296
308
|
const subscriberFilters = Object.values(metadataTypeMap)
|
|
@@ -312,8 +324,8 @@ class DataFilter extends MetadataType {
|
|
|
312
324
|
|
|
313
325
|
const keyFieldBackup = this.definition.keyField;
|
|
314
326
|
this.definition.keyField = 'measureID';
|
|
315
|
-
this.cache.
|
|
316
|
-
this.cache
|
|
327
|
+
this.cache[this.buObject.mid] ||= {};
|
|
328
|
+
this.cache[this.buObject.mid].measures = this.parseResponseBody(response);
|
|
317
329
|
this.definition.keyField = keyFieldBackup;
|
|
318
330
|
}
|
|
319
331
|
}
|
|
@@ -439,7 +451,9 @@ class DataFilter extends MetadataType {
|
|
|
439
451
|
return this._resolveFields(
|
|
440
452
|
metadata,
|
|
441
453
|
mode,
|
|
442
|
-
this.cache.dataExtensionField[
|
|
454
|
+
this.cache[this.buObject.mid].dataExtensionField[
|
|
455
|
+
metadata.r__source_dataExtension_key
|
|
456
|
+
],
|
|
443
457
|
metadata.c__filterDefinition?.ConditionSet
|
|
444
458
|
);
|
|
445
459
|
}
|
|
@@ -530,8 +544,8 @@ class DataFilter extends MetadataType {
|
|
|
530
544
|
metadata.c__filterDefinition?.ConditionSet
|
|
531
545
|
);
|
|
532
546
|
}
|
|
533
|
-
const contactAttributes = this.cache
|
|
534
|
-
const measures = this.cache
|
|
547
|
+
const contactAttributes = this.cache[this.buObject.mid]?.contactAttributes;
|
|
548
|
+
const measures = this.cache[this.buObject.mid]?.measures;
|
|
535
549
|
const conditionsArr = Array.isArray(filter.Condition)
|
|
536
550
|
? filter.Condition
|
|
537
551
|
: [filter.Condition];
|
|
@@ -1067,7 +1067,7 @@ class Event extends MetadataType {
|
|
|
1067
1067
|
if (errors.length > 1) {
|
|
1068
1068
|
errors.unshift(``);
|
|
1069
1069
|
}
|
|
1070
|
-
throw new Error(errors.join('\n · '));
|
|
1070
|
+
throw new Error(errors.join('\n · '));
|
|
1071
1071
|
}
|
|
1072
1072
|
if (warnings?.length) {
|
|
1073
1073
|
// add a line break
|
|
@@ -1220,7 +1220,7 @@ class Event extends MetadataType {
|
|
|
1220
1220
|
if (errors.length > 1) {
|
|
1221
1221
|
errors.unshift(``);
|
|
1222
1222
|
}
|
|
1223
|
-
throw new Error(errors.join('\n · '));
|
|
1223
|
+
throw new Error(errors.join('\n · '));
|
|
1224
1224
|
}
|
|
1225
1225
|
}
|
|
1226
1226
|
|
|
@@ -22,6 +22,7 @@ import { Util } from '../util/util.js';
|
|
|
22
22
|
* @augments MetadataType
|
|
23
23
|
*/
|
|
24
24
|
class FileLocation extends MetadataType {
|
|
25
|
+
static cache = {};
|
|
25
26
|
/**
|
|
26
27
|
* Retrieves Metadata of FileLocation
|
|
27
28
|
* Endpoint /automation/v1/ftplocations/ return all FileLocations
|
|
@@ -32,8 +33,32 @@ class FileLocation extends MetadataType {
|
|
|
32
33
|
* @param {string} [key] customer key of single item to retrieve
|
|
33
34
|
* @returns {Promise.<MetadataTypeMapObj>} Promise
|
|
34
35
|
*/
|
|
35
|
-
static retrieve(retrieveDir, _, __, key) {
|
|
36
|
-
|
|
36
|
+
static async retrieve(retrieveDir, _, __, key) {
|
|
37
|
+
try {
|
|
38
|
+
const dataItems = await super.retrieveREST(
|
|
39
|
+
null,
|
|
40
|
+
'/data/v1/filetransferlocation' + (key ? '/' + encodeURIComponent(key) : 's'),
|
|
41
|
+
null,
|
|
42
|
+
key
|
|
43
|
+
);
|
|
44
|
+
this.cache[this.buObject.mid] ||= {};
|
|
45
|
+
this.cache[this.buObject.mid].dataItems = dataItems.metadata;
|
|
46
|
+
} catch (ex) {
|
|
47
|
+
if (ex.code === 'ERR_BAD_REQUEST') {
|
|
48
|
+
// if retrieve-by-key comes up empty, the data-endpoint returns a code 400
|
|
49
|
+
Util.logger.debug(ex.message);
|
|
50
|
+
} else {
|
|
51
|
+
Util.logger.warn(ex.message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const items = await super.retrieveREST(
|
|
55
|
+
retrieveDir,
|
|
56
|
+
'/automation/v1/ftplocations/',
|
|
57
|
+
null,
|
|
58
|
+
key
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
return items;
|
|
37
62
|
}
|
|
38
63
|
|
|
39
64
|
/**
|
|
@@ -42,9 +67,156 @@ class FileLocation extends MetadataType {
|
|
|
42
67
|
* @returns {Promise.<MetadataTypeMapObj>} Promise
|
|
43
68
|
*/
|
|
44
69
|
static async retrieveForCache() {
|
|
45
|
-
return
|
|
70
|
+
return this.retrieve(null);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates a single item
|
|
75
|
+
*
|
|
76
|
+
* @param {MetadataTypeItem} metadata a single item
|
|
77
|
+
* @returns {Promise.<MetadataTypeItem>} Promise
|
|
78
|
+
*/
|
|
79
|
+
static create(metadata) {
|
|
80
|
+
return this.createREST(metadata, '/data/v1/filetransferlocation');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Updates a single item
|
|
84
|
+
*
|
|
85
|
+
* @param {MetadataTypeItem} metadata a single item
|
|
86
|
+
* @returns {Promise.<MetadataTypeItem>} Promise
|
|
87
|
+
*/
|
|
88
|
+
static update(metadata) {
|
|
89
|
+
return this.updateREST(
|
|
90
|
+
metadata,
|
|
91
|
+
'/data/v1/filetransferlocation/' +
|
|
92
|
+
encodeURIComponent(metadata[this.definition.keyField])
|
|
93
|
+
);
|
|
46
94
|
}
|
|
47
95
|
|
|
96
|
+
/**
|
|
97
|
+
* helper for {@link MetadataType.parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
98
|
+
*
|
|
99
|
+
* @param {MetadataTypeItem} metadata single item
|
|
100
|
+
*/
|
|
101
|
+
static createCustomKeyField(metadata) {
|
|
102
|
+
if (metadata.fileTransferLocation) {
|
|
103
|
+
const fileTransferLocation = metadata.fileTransferLocation;
|
|
104
|
+
for (const key of Object.keys(metadata)) {
|
|
105
|
+
delete metadata[key];
|
|
106
|
+
}
|
|
107
|
+
Object.assign(metadata, fileTransferLocation);
|
|
108
|
+
} else {
|
|
109
|
+
if (!metadata.customerKey && this.cache[this.buObject.mid]?.dataItems) {
|
|
110
|
+
const nameMatch = Object.values(this.cache[this.buObject.mid].dataItems).find(
|
|
111
|
+
(item) => item.name === metadata.name
|
|
112
|
+
);
|
|
113
|
+
if (nameMatch) {
|
|
114
|
+
Object.assign(metadata, nameMatch);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!this.definition.locationTypeIdMappingDeployable[metadata.locationTypeId]) {
|
|
118
|
+
// old file location types are only returned by the automation-endpoint which does not return customerKey field - but also these are not updatable and hence we can improvise here
|
|
119
|
+
|
|
120
|
+
metadata.customerKey ||= metadata.name;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Creates a single metadata entry via REST
|
|
127
|
+
*
|
|
128
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
129
|
+
* @param {string} uri rest endpoint for POST
|
|
130
|
+
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
131
|
+
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
132
|
+
*/
|
|
133
|
+
static async createREST(metadataEntry, uri, handleOutside) {
|
|
134
|
+
this.removeNotCreateableFields(metadataEntry);
|
|
135
|
+
const createPayload = { fileTransferLocation: metadataEntry };
|
|
136
|
+
try {
|
|
137
|
+
// set to empty object in case API returned nothing to be able to update it in helper classes
|
|
138
|
+
let response = (await this.client.rest.post(uri, createPayload)) || {};
|
|
139
|
+
response = await this.postCreateTasks(metadataEntry, response);
|
|
140
|
+
if (!handleOutside) {
|
|
141
|
+
Util.logger.info(
|
|
142
|
+
` - created ${Util.getTypeKeyName(this.definition, metadataEntry)}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return response;
|
|
146
|
+
} catch (ex) {
|
|
147
|
+
const parsedErrors = this.getErrorsREST(ex);
|
|
148
|
+
Util.logger.error(
|
|
149
|
+
` ☇ error creating ${Util.getTypeKeyName(this.definition, metadataEntry)}:`
|
|
150
|
+
);
|
|
151
|
+
if (parsedErrors.length) {
|
|
152
|
+
for (const msg of parsedErrors) {
|
|
153
|
+
Util.logger.error(' • ' + msg);
|
|
154
|
+
}
|
|
155
|
+
} else if (ex?.message) {
|
|
156
|
+
Util.logger.debug(ex.message);
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* helper for {@link MetadataType.createREST}
|
|
164
|
+
*
|
|
165
|
+
* @param {MetadataTypeItem} _ a single metadata Entry
|
|
166
|
+
* @param {object} apiResponse varies depending on the API call
|
|
167
|
+
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
168
|
+
*/
|
|
169
|
+
static async postCreateTasks(_, apiResponse) {
|
|
170
|
+
return apiResponse?.fileTransferLocation || apiResponse;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Updates a single metadata entry via REST
|
|
175
|
+
*
|
|
176
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
177
|
+
* @param {string} uri rest endpoint for PATCH
|
|
178
|
+
* @param {'patch'|'post'|'put'} [httpMethod] defaults to 'patch'; some update requests require PUT instead of PATCH
|
|
179
|
+
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
180
|
+
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
181
|
+
*/
|
|
182
|
+
static async updateREST(metadataEntry, uri, httpMethod = 'patch', handleOutside) {
|
|
183
|
+
this.removeNotUpdateableFields(metadataEntry);
|
|
184
|
+
const updatePayload = { fileTransferLocation: metadataEntry };
|
|
185
|
+
try {
|
|
186
|
+
// set to empty object in case API returned nothing to be able to update it in helper classes
|
|
187
|
+
let response = (await this.client.rest[httpMethod](uri, updatePayload)) || {};
|
|
188
|
+
await this._postChangeKeyTasks(metadataEntry);
|
|
189
|
+
this.getErrorsREST(response);
|
|
190
|
+
response = await this.postUpdateTasks(metadataEntry, response);
|
|
191
|
+
// some times, e.g. automation dont return a key in their update response and hence we need to fall back to name
|
|
192
|
+
if (!handleOutside) {
|
|
193
|
+
Util.logger.info(
|
|
194
|
+
` - updated ${Util.getTypeKeyName(this.definition, metadataEntry)}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return response;
|
|
198
|
+
} catch (ex) {
|
|
199
|
+
const parsedErrors = this.getErrorsREST(ex);
|
|
200
|
+
Util.logger.error(
|
|
201
|
+
` ☇ error updating ${Util.getTypeKeyName(this.definition, metadataEntry)}:`
|
|
202
|
+
);
|
|
203
|
+
for (const msg of parsedErrors) {
|
|
204
|
+
Util.logger.error(' • ' + msg);
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP}
|
|
212
|
+
*
|
|
213
|
+
* @param {MetadataTypeItem} _ a single metadata Entry
|
|
214
|
+
* @param {object} apiResponse varies depending on the API call
|
|
215
|
+
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
216
|
+
*/
|
|
217
|
+
static postUpdateTasks(_, apiResponse) {
|
|
218
|
+
return apiResponse?.fileTransferLocation || apiResponse;
|
|
219
|
+
}
|
|
48
220
|
/**
|
|
49
221
|
* prepares a import definition for deployment
|
|
50
222
|
*
|
|
@@ -54,6 +226,14 @@ class FileLocation extends MetadataType {
|
|
|
54
226
|
static async preDeployTasks(metadata) {
|
|
55
227
|
if (metadata.c__locationType) {
|
|
56
228
|
metadata.locationTypeId = this.definition.locationTypeMapping[metadata.c__locationType];
|
|
229
|
+
if (this.definition.locationTypeMappingDeployable[metadata.c__locationType]) {
|
|
230
|
+
metadata.locationType =
|
|
231
|
+
this.definition.locationTypeMappingDeployable[metadata.c__locationType];
|
|
232
|
+
} else {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`Only FileLocations of types ${Object.keys(this.definition.locationTypeMappingDeployable).join(', ')} can be deployed via mcdev.`
|
|
235
|
+
);
|
|
236
|
+
}
|
|
57
237
|
}
|
|
58
238
|
return metadata;
|
|
59
239
|
}
|
|
@@ -65,7 +245,7 @@ class FileLocation extends MetadataType {
|
|
|
65
245
|
* @returns {MetadataTypeItem} parsed metadata
|
|
66
246
|
*/
|
|
67
247
|
static postRetrieveTasks(metadata) {
|
|
68
|
-
if (metadata.locationTypeId) {
|
|
248
|
+
if (metadata.locationTypeId !== undefined) {
|
|
69
249
|
try {
|
|
70
250
|
metadata.c__locationType = Util.inverseGet(
|
|
71
251
|
this.definition.locationTypeMapping,
|
|
@@ -78,9 +258,29 @@ class FileLocation extends MetadataType {
|
|
|
78
258
|
metadata.locationTypeId
|
|
79
259
|
);
|
|
80
260
|
}
|
|
261
|
+
} else if (metadata.locationType) {
|
|
262
|
+
// assuming create/update of new types
|
|
263
|
+
metadata.c__locationType = Util.inverseGet(
|
|
264
|
+
this.definition.locationTypeMappingDeployable,
|
|
265
|
+
metadata.locationType
|
|
266
|
+
);
|
|
81
267
|
}
|
|
82
268
|
return metadata;
|
|
83
269
|
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Delete a metadata item from the specified business unit
|
|
273
|
+
*
|
|
274
|
+
* @param {string} key Identifier of item
|
|
275
|
+
* @returns {Promise.<boolean>} deletion success flag
|
|
276
|
+
*/
|
|
277
|
+
static async deleteByKey(key) {
|
|
278
|
+
return super.deleteByKeyREST(
|
|
279
|
+
'/data/v1/filetransferlocation/' + encodeURIComponent(key),
|
|
280
|
+
key,
|
|
281
|
+
400
|
|
282
|
+
);
|
|
283
|
+
}
|
|
84
284
|
}
|
|
85
285
|
|
|
86
286
|
// Assign definition to static attributes
|
|
@@ -50,6 +50,11 @@ class Journey extends MetadataType {
|
|
|
50
50
|
|
|
51
51
|
let singleKey = '';
|
|
52
52
|
let mode = 'all';
|
|
53
|
+
const additionalParams = new URLSearchParams();
|
|
54
|
+
if (Util.OPTIONS.onlyPublished) {
|
|
55
|
+
Util.logger.info(' - Retrieving only published versions of journeys');
|
|
56
|
+
additionalParams.set('status', 'Published');
|
|
57
|
+
}
|
|
53
58
|
if (key) {
|
|
54
59
|
if (key.startsWith('%23')) {
|
|
55
60
|
// correct the format
|
|
@@ -74,7 +79,7 @@ class Journey extends MetadataType {
|
|
|
74
79
|
}
|
|
75
80
|
mode = 'id';
|
|
76
81
|
} else if (key.startsWith('name:')) {
|
|
77
|
-
|
|
82
|
+
additionalParams.set('nameOrDescription', key.slice(5));
|
|
78
83
|
mode = 'name';
|
|
79
84
|
} else {
|
|
80
85
|
// assume actual key was provided
|
|
@@ -85,7 +90,10 @@ class Journey extends MetadataType {
|
|
|
85
90
|
|
|
86
91
|
try {
|
|
87
92
|
const uri = `/interaction/v1/interactions/`;
|
|
88
|
-
if (
|
|
93
|
+
if (
|
|
94
|
+
(!Util.OPTIONS.onlyPublished && singleKey && (mode === 'key' || mode === 'id')) ||
|
|
95
|
+
!retrieveDir
|
|
96
|
+
) {
|
|
89
97
|
// full details for retrieve, only base data for caching; reduces caching time from minutes to seconds
|
|
90
98
|
const extras = retrieveDir && singleKey ? extrasDefault : '';
|
|
91
99
|
|
|
@@ -97,13 +105,12 @@ class Journey extends MetadataType {
|
|
|
97
105
|
key
|
|
98
106
|
);
|
|
99
107
|
} else {
|
|
108
|
+
const url =
|
|
109
|
+
uri + (additionalParams.size > 0 ? '?' + additionalParams.toString() : '');
|
|
100
110
|
// retrieve all
|
|
101
111
|
const results = this.definition.restPagination
|
|
102
|
-
? await this.client.rest.getBulk(
|
|
103
|
-
|
|
104
|
-
this.definition.restPageSize || 500
|
|
105
|
-
)
|
|
106
|
-
: await this.client.rest.get(uri + (mode === 'name' ? singleKey : ''));
|
|
112
|
+
? await this.client.rest.getBulk(url, this.definition.restPageSize || 500)
|
|
113
|
+
: await this.client.rest.get(url);
|
|
107
114
|
|
|
108
115
|
if (results.items?.length) {
|
|
109
116
|
// empty results will come back without "items" defined
|
|
@@ -115,51 +122,71 @@ class Journey extends MetadataType {
|
|
|
115
122
|
}
|
|
116
123
|
// full details for retrieve
|
|
117
124
|
const extras = extrasDefault;
|
|
125
|
+
let details = [];
|
|
118
126
|
let parsed;
|
|
119
127
|
if (retrieveDir) {
|
|
120
128
|
const searchName = mode === 'name' ? key.slice(5) : null;
|
|
121
129
|
const foundKey = [];
|
|
130
|
+
let journey;
|
|
122
131
|
// get extra details for saving this
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
if (Util.OPTIONS.onlyPublished && mode === 'key') {
|
|
133
|
+
journey = results.items.find(
|
|
134
|
+
(a) => a[this.definition.keyField] === singleKey.slice(4)
|
|
135
|
+
);
|
|
136
|
+
} else if (Util.OPTIONS.onlyPublished && mode === 'id') {
|
|
137
|
+
journey = results.items.find(
|
|
138
|
+
(a) => a[this.definition.idField] === singleKey
|
|
139
|
+
);
|
|
140
|
+
} else {
|
|
141
|
+
details = results.items
|
|
142
|
+
? await Promise.all(
|
|
143
|
+
results.items.map(async (a) => {
|
|
144
|
+
if (mode === 'name') {
|
|
145
|
+
// when filtering by name, the API in fact does a LIKE search with placeholders left and right of the search term - and also searches the description field.
|
|
146
|
+
if (searchName === a[this.definition.nameField]) {
|
|
147
|
+
foundKey.push(a[this.definition.keyField]);
|
|
148
|
+
} else {
|
|
149
|
+
// skip because the name does not match
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
return await this.client.rest.get(
|
|
155
|
+
`${uri}key:${a[this.definition.keyField]}?extras=${extras}` +
|
|
156
|
+
`&versionNumber=${a.version}`
|
|
157
|
+
);
|
|
158
|
+
} catch (ex) {
|
|
159
|
+
// if we do get here, we should log the error and continue instead of failing to download all automations
|
|
160
|
+
Util.logger.warn(
|
|
161
|
+
` ☇ skipping ${this.definition.type} ${
|
|
162
|
+
a[this.definition.nameField]
|
|
163
|
+
} (${a[this.definition.keyField]}): ${ex.message} (${
|
|
164
|
+
ex.code
|
|
165
|
+
})${
|
|
166
|
+
ex.endpoint
|
|
167
|
+
? Util.getGrayMsg(
|
|
168
|
+
' - ' +
|
|
169
|
+
ex.endpoint.split(
|
|
170
|
+
'rest.marketingcloudapis.com'
|
|
171
|
+
)[1]
|
|
172
|
+
)
|
|
173
|
+
: ''
|
|
174
|
+
}`
|
|
175
|
+
);
|
|
132
176
|
return null;
|
|
133
177
|
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
ex.code
|
|
147
|
-
})${
|
|
148
|
-
ex.endpoint
|
|
149
|
-
? Util.getGrayMsg(
|
|
150
|
-
' - ' +
|
|
151
|
-
ex.endpoint.split(
|
|
152
|
-
'rest.marketingcloudapis.com'
|
|
153
|
-
)[1]
|
|
154
|
-
)
|
|
155
|
-
: ''
|
|
156
|
-
}`
|
|
157
|
-
);
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
})
|
|
161
|
-
)
|
|
162
|
-
: [];
|
|
178
|
+
})
|
|
179
|
+
)
|
|
180
|
+
: [];
|
|
181
|
+
}
|
|
182
|
+
if (Util.OPTIONS.onlyPublished && journey) {
|
|
183
|
+
details.push(
|
|
184
|
+
await this.client.rest.get(
|
|
185
|
+
`${uri}${singleKey}?extras=${extras}` +
|
|
186
|
+
`&versionNumber=${journey.version}`
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
163
190
|
parsed = this.parseResponseBody({ items: details.filter(Boolean) });
|
|
164
191
|
// * retrieveDir is mandatory in this method as it is not used for caching (there is a seperate method for that)
|
|
165
192
|
const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null);
|
|
@@ -83,6 +83,22 @@ class Script extends MetadataType {
|
|
|
83
83
|
return super.updateREST(script, '/automation/v1/scripts/' + script.ssjsActivityId);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP}
|
|
88
|
+
*
|
|
89
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
90
|
+
* @param {object} apiResponse varies depending on the API call
|
|
91
|
+
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
92
|
+
*/
|
|
93
|
+
static async postUpdateTasks(metadataEntry, apiResponse) {
|
|
94
|
+
// script update endpoint returns wrong values for createdDate and modifiedDate - need to re-retrieve to get correct values
|
|
95
|
+
const ssjsActivityId = apiResponse?.ssjsActivityId;
|
|
96
|
+
if (ssjsActivityId) {
|
|
97
|
+
apiResponse = await this.client.rest.get('/automation/v1/scripts/' + ssjsActivityId);
|
|
98
|
+
}
|
|
99
|
+
return apiResponse;
|
|
100
|
+
}
|
|
101
|
+
|
|
86
102
|
/**
|
|
87
103
|
* Creates a single Script
|
|
88
104
|
*
|
|
@@ -23,6 +23,7 @@ export default {
|
|
|
23
23
|
soapType: 'FilterDefinition',
|
|
24
24
|
typeDescription: 'Defines an audience based on specified rules. Used by Filter Activities.',
|
|
25
25
|
typeRetrieveByDefault: true,
|
|
26
|
+
typeCdpByDefault: true,
|
|
26
27
|
typeName: 'Data Filter',
|
|
27
28
|
fields: {
|
|
28
29
|
// the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
|
|
@@ -24,6 +24,7 @@ export default {
|
|
|
24
24
|
typeDescription:
|
|
25
25
|
'Defines an audience based on specified rules. Auto-generated by filtered DEs and filtered Lists.',
|
|
26
26
|
typeRetrieveByDefault: false,
|
|
27
|
+
typeCdpByDefault: false,
|
|
27
28
|
typeName: 'Data Filter (auto-generated)',
|
|
28
29
|
fields: {
|
|
29
30
|
// the GUI seems to ONLY send fields during update that are actually changed. It has yet to be tested if that also works with sending other fiedls as well
|