mcdev 8.2.1 → 8.3.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.
Files changed (123) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  2. package/.github/workflows/code-test.yml +1 -1
  3. package/.github/workflows/coverage-base-update.yml +1 -1
  4. package/.github/workflows/coverage-develop-branch.yml +1 -1
  5. package/.github/workflows/coverage-main-branch.yml +1 -1
  6. package/.github/workflows/coverage.yml +1 -1
  7. package/.github/workflows/npm-publish.yml +9 -5
  8. package/@types/lib/MetadataTypeDefinitions.d.ts +4 -0
  9. package/@types/lib/MetadataTypeDefinitions.d.ts.map +1 -1
  10. package/@types/lib/MetadataTypeInfo.d.ts +4 -0
  11. package/@types/lib/MetadataTypeInfo.d.ts.map +1 -1
  12. package/@types/lib/Retriever.d.ts.map +1 -1
  13. package/@types/lib/index.d.ts +20 -9
  14. package/@types/lib/index.d.ts.map +1 -1
  15. package/@types/lib/metadataTypes/Asset.d.ts +3 -3
  16. package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
  17. package/@types/lib/metadataTypes/Automation.d.ts +7 -7
  18. package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
  19. package/@types/lib/metadataTypes/DataExtension.d.ts +9 -9
  20. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  21. package/@types/lib/metadataTypes/DataExtensionField.d.ts +7 -0
  22. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
  23. package/@types/lib/metadataTypes/DataFilter.d.ts +326 -0
  24. package/@types/lib/metadataTypes/DataFilter.d.ts.map +1 -0
  25. package/@types/lib/metadataTypes/DataFilterHidden.d.ts +164 -0
  26. package/@types/lib/metadataTypes/DataFilterHidden.d.ts.map +1 -0
  27. package/@types/lib/metadataTypes/Filter.d.ts +181 -6
  28. package/@types/lib/metadataTypes/Filter.d.ts.map +1 -1
  29. package/@types/lib/metadataTypes/FilterDefinition.d.ts +318 -0
  30. package/@types/lib/metadataTypes/FilterDefinition.d.ts.map +1 -0
  31. package/@types/lib/metadataTypes/FilterDefinitionHidden.d.ts +156 -0
  32. package/@types/lib/metadataTypes/FilterDefinitionHidden.d.ts.map +1 -0
  33. package/@types/lib/metadataTypes/ImportFile.d.ts.map +1 -1
  34. package/@types/lib/metadataTypes/Journey.d.ts +18 -0
  35. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  36. package/@types/lib/metadataTypes/MobileKeyword.d.ts +2 -2
  37. package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -1
  38. package/@types/lib/metadataTypes/User.d.ts +1 -1
  39. package/@types/lib/metadataTypes/User.d.ts.map +1 -1
  40. package/@types/lib/metadataTypes/definitions/DataFilter.definition.d.ts +230 -0
  41. package/@types/lib/metadataTypes/definitions/DataFilter.definition.d.ts.map +1 -0
  42. package/@types/lib/metadataTypes/definitions/DataFilterHidden.definition.d.ts +230 -0
  43. package/@types/lib/metadataTypes/definitions/DataFilterHidden.definition.d.ts.map +1 -0
  44. package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts +203 -31
  45. package/@types/lib/metadataTypes/definitions/FilterDefinition.definition.d.ts +218 -0
  46. package/@types/lib/metadataTypes/definitions/FilterDefinition.definition.d.ts.map +1 -0
  47. package/@types/lib/metadataTypes/definitions/FilterDefinitionHidden.definition.d.ts +218 -0
  48. package/@types/lib/metadataTypes/definitions/FilterDefinitionHidden.definition.d.ts.map +1 -0
  49. package/@types/lib/metadataTypes/definitions/Journey.definition.d.ts +18 -0
  50. package/@types/lib/util/devops.d.ts +1 -0
  51. package/@types/lib/util/devops.d.ts.map +1 -1
  52. package/@types/lib/util/replaceContentBlockReference.d.ts +3 -3
  53. package/@types/lib/util/replaceContentBlockReference.d.ts.map +1 -1
  54. package/@types/lib/util/util.d.ts.map +1 -1
  55. package/@types/lib/util/validations.d.ts.map +1 -1
  56. package/@types/types/mcdev.d.d.ts +296 -0
  57. package/@types/types/mcdev.d.d.ts.map +1 -1
  58. package/lib/MetadataTypeDefinitions.js +4 -0
  59. package/lib/MetadataTypeInfo.js +4 -0
  60. package/lib/Retriever.js +3 -0
  61. package/lib/index.js +87 -3
  62. package/lib/metadataTypes/DataExtensionField.js +21 -0
  63. package/lib/metadataTypes/DataFilter.js +653 -0
  64. package/lib/metadataTypes/DataFilterHidden.js +18 -0
  65. package/lib/metadataTypes/Filter.js +315 -1
  66. package/lib/metadataTypes/ImportFile.js +11 -1
  67. package/lib/metadataTypes/Journey.js +111 -7
  68. package/lib/metadataTypes/definitions/Automation.definition.js +1 -0
  69. package/lib/metadataTypes/definitions/DataFilter.definition.js +168 -0
  70. package/lib/metadataTypes/definitions/DataFilterHidden.definition.js +169 -0
  71. package/lib/metadataTypes/definitions/Filter.definition.js +136 -22
  72. package/lib/metadataTypes/definitions/Journey.definition.js +20 -1
  73. package/lib/util/devops.js +14 -4
  74. package/lib/util/util.js +7 -2
  75. package/lib/util/validations.js +7 -1
  76. package/package.json +10 -10
  77. package/test/general.test.js +17 -19
  78. package/test/mockRoot/.mcdevrc.json +4 -1
  79. package/test/mockRoot/deploy/testInstance/testBU/dataFilter/testExisting_dataFilter.dataFilter-meta.json +19 -0
  80. package/test/mockRoot/deploy/testInstance/testBU/dataFilter/testNew_dataFilter.dataFilter-meta.json +19 -0
  81. package/test/mockRoot/deploy/testInstance/testBU/filter/testExisting_filter.filter-meta.json +10 -0
  82. package/test/mockRoot/deploy/testInstance/testBU/filter/testNew_filter.filter-meta.json +11 -0
  83. package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_journey_updatecontact.journey-meta.json +108 -0
  84. package/test/resources/9999999/automation/v1/filters/a0f1a1bc-4ea1-44b3-8fe1-ce40ef35c1c0/patch-response.json +18 -0
  85. package/test/resources/9999999/automation/v1/filters/f018f237-f7ef-40b0-afc8-39ea2e5dcca4/delete-response.txt +0 -0
  86. package/test/resources/9999999/automation/v1/filters/f018f237-f7ef-40b0-afc8-39ea2e5dcca4/get-response.json +15 -0
  87. package/test/resources/9999999/automation/v1/filters/f018f237-f7ef-40b0-afc8-39ea2e5dcca4/patch-response.json +15 -0
  88. package/test/resources/9999999/automation/v1/filters/get-response.json +22 -0
  89. package/test/resources/9999999/dataFilter/build-expected.json +19 -0
  90. package/test/resources/9999999/dataFilter/get-expected.json +23 -0
  91. package/test/resources/9999999/dataFilter/patch-expected.json +20 -0
  92. package/test/resources/9999999/dataFilter/post-expected.json +20 -0
  93. package/test/resources/9999999/dataFilter/template-expected.json +19 -0
  94. package/test/resources/9999999/dataFolder/retrieve-ContentTypeINasset,asset-sha,automatio,cloudpage,dataexten,filteract,filterdef,hidden,journey,list,mysubs,publicati,queryacti,salesforc,shared_da,shared_da,shared_sa,ssjsactiv,synchroni,useriniti-response.xml +765 -0
  95. package/test/resources/9999999/dataFolder/retrieve-ContentTypeINfilteractivity,hidden-response.xml +139 -0
  96. package/test/resources/9999999/dataFolder/retrieve-ContentTypeINfilterdefinition,hidden-response.xml +116 -0
  97. package/test/resources/9999999/email/v1/filters/filterdefinition/10ef27dd-4be8-4bf6-970a-8acf8e281e55/delete-response.txt +1 -0
  98. package/test/resources/9999999/email/v1/filters/filterdefinition/10ef27dd-4be8-4bf6-970a-8acf8e281e55/get-response.json +19 -0
  99. package/test/resources/9999999/email/v1/filters/filterdefinition/10ef27dd-4be8-4bf6-970a-8acf8e281e55/patch-response.json +15 -0
  100. package/test/resources/9999999/email/v1/filters/filterdefinition/category/5318/get-response.json +27 -0
  101. package/test/resources/9999999/email/v1/filters/filterdefinition/category/8502/get-response.json +7 -0
  102. package/test/resources/9999999/email/v1/filters/filterdefinition/category/8503/get-response.json +7 -0
  103. package/test/resources/9999999/email/v1/filters/filterdefinition/post-response.json +15 -0
  104. package/test/resources/9999999/filter/build-expected.json +10 -0
  105. package/test/resources/9999999/filter/get-expected.json +11 -0
  106. package/test/resources/9999999/filter/patch-expected.json +11 -0
  107. package/test/resources/9999999/filter/post-expected.json +11 -0
  108. package/test/resources/9999999/filter/template-expected.json +10 -0
  109. package/test/resources/9999999/filterActivity/create-response.xml +46 -0
  110. package/test/resources/9999999/filterActivity/retrieve-CustomerKey=testExisting_filter-response.xml +30 -0
  111. package/test/resources/9999999/filterDefinition/retrieve-CustomerKey=testExisting_dataFilter-response.xml +30 -0
  112. package/test/resources/9999999/interaction/v1/interactions/get-response.json +45 -3
  113. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact/get-response.json +119 -0
  114. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact/put-response.json +106 -0
  115. package/test/resources/9999999/journey/get-updatecontact-expected.json +108 -0
  116. package/test/resources/9999999/journey/put-updatecontact-expected.json +108 -0
  117. package/test/type.automation.test.js +14 -14
  118. package/test/type.dataFilter.test.js +174 -0
  119. package/test/type.filter.test.js +170 -0
  120. package/test/type.journey.test.js +59 -7
  121. package/test/type.query.test.js +2 -2
  122. package/test/utils.js +8 -0
  123. package/types/mcdev.d.js +101 -0
@@ -1,6 +1,16 @@
1
1
  'use strict';
2
2
 
3
3
  import MetadataType from './MetadataType.js';
4
+ import { Util } from '../util/util.js';
5
+ import cache from '../util/cache.js';
6
+ import File from '../util/file.js';
7
+
8
+ const dataTypes = {
9
+ 1: 'List',
10
+ 2: 'DataExtension',
11
+ 3: 'Group Wizard',
12
+ 4: 'Behavioral Data',
13
+ };
4
14
 
5
15
  /**
6
16
  * @typedef {import('../../types/mcdev.d.js').BuObject} BuObject
@@ -13,6 +23,7 @@ import MetadataType from './MetadataType.js';
13
23
  * @typedef {import('../../types/mcdev.d.js').MetadataTypeMapObj} MetadataTypeMapObj
14
24
  * @typedef {import('../../types/mcdev.d.js').SoapRequestParams} SoapRequestParams
15
25
  * @typedef {import('../../types/mcdev.d.js').TemplateMap} TemplateMap
26
+ * @typedef {import('../../types/mcdev.d.js').FilterItem} FilterItem
16
27
  */
17
28
 
18
29
  /**
@@ -34,7 +45,310 @@ class Filter extends MetadataType {
34
45
  * @returns {Promise.<MetadataTypeMapObj>} Promise
35
46
  */
36
47
  static async retrieve(retrieveDir, _, __, key) {
37
- return super.retrieveREST(retrieveDir, '/automation/v1/filters/', null, key);
48
+ const objectId = key ? await this._getObjectIdForSingleRetrieve(key) : '';
49
+ return super.retrieveREST(retrieveDir, '/automation/v1/filters/' + objectId, null, key);
50
+ }
51
+ /**
52
+ * parses retrieved Metadata before saving
53
+ *
54
+ * @param {FilterItem} metadata a single record
55
+ * @returns {FilterItem} parsed metadata definition
56
+ */
57
+ static postRetrieveTasks(metadata) {
58
+ // folder
59
+ super.setFolderPath(metadata);
60
+
61
+ try {
62
+ // filterDefinition
63
+ metadata.r__dataFilter_key = cache.searchForField(
64
+ 'dataFilter',
65
+ metadata.filterDefinitionId,
66
+ 'id',
67
+ 'key'
68
+ );
69
+ delete metadata.filterDefinitionId;
70
+ } catch {
71
+ try {
72
+ // filterDefinitionHidden - auto-generated for filtered dataExtensions
73
+ metadata.r__dataFilter_key = cache.searchForField(
74
+ 'dataFilterHidden',
75
+ metadata.filterDefinitionId,
76
+ 'id',
77
+ 'key'
78
+ );
79
+ delete metadata.filterDefinitionId;
80
+ } catch {
81
+ // ignore
82
+ }
83
+ }
84
+
85
+ // rest API does not return description property when it's empty but we always want this as part of our json
86
+ metadata.description ||= '';
87
+
88
+ this._postRetrieve_dataTypeMapping('source', metadata);
89
+ this._postRetrieve_dataTypeMapping('destination', metadata);
90
+ return metadata;
91
+ }
92
+
93
+ /**
94
+ * helper for postRetrieveTasks to map data types
95
+ *
96
+ * @param {'source'|'destination'} target we are processing source and destinations
97
+ * @param {FilterItem} metadata single record
98
+ */
99
+ static _postRetrieve_dataTypeMapping(target, metadata) {
100
+ try {
101
+ switch (metadata[`${target}TypeId`]) {
102
+ // case 1: {
103
+ // // List
104
+ // // TODO
105
+ // break;
106
+ case 2: {
107
+ // dataExtension
108
+ metadata[`r__${target}_dataExtension_key`] = cache.searchForField(
109
+ 'dataExtension',
110
+ metadata[`${target}ObjectId`],
111
+ 'ObjectID',
112
+ 'CustomerKey'
113
+ );
114
+ delete metadata[`${target}ObjectId`];
115
+ delete metadata[`${target}TypeId`];
116
+ break;
117
+ }
118
+ // case 3: {
119
+ // // GroupWizard
120
+ // // TODO
121
+ // break;
122
+ // }
123
+ // case 4: {
124
+ // // BehavioralData
125
+ // // TODO
126
+ // break;
127
+ // }
128
+ default: {
129
+ Util.logger.warn(
130
+ ` - Filter '${metadata.name}' (${
131
+ metadata.customerKey
132
+ }): Unsupported ${target} type '${dataTypes[metadata[`${target}TypeId`]]}'`
133
+ );
134
+ }
135
+ }
136
+ } catch (ex) {
137
+ Util.logger.warn(
138
+ ` - filter '${metadata.name}' (${metadata.customerKey}): ${target} not found (${ex.message})`
139
+ );
140
+ }
141
+ }
142
+ /**
143
+ * helper for preDeployTasks to map data types
144
+ *
145
+ * @param {'source'|'destination'} target we are processing source and destinations
146
+ * @param {FilterItem} metadata single record
147
+ * @param {FilterItem} cachedVersion cached version of the metadata
148
+ */
149
+ static _preDeploy_dataTypeMapping(target, metadata, cachedVersion) {
150
+ // TODO List / TypeId==1
151
+
152
+ // dataExtension
153
+ if (metadata[`r__${target}_dataExtension_key`]) {
154
+ metadata[`${target}TypeId`] = 2;
155
+
156
+ metadata[`${target}ObjectId`] = cache.searchForField(
157
+ 'dataExtension',
158
+ metadata[`r__${target}_dataExtension_key`],
159
+ 'CustomerKey',
160
+ 'ObjectID'
161
+ );
162
+ if (
163
+ cachedVersion &&
164
+ cachedVersion[`${target}ObjectId`] !== metadata[`${target}ObjectId`]
165
+ ) {
166
+ throw new Error(
167
+ `Updating r__${target}_dataExtension_key is not allowed. You need to delete and re-create the filter to change this.`
168
+ );
169
+ // metadata[`${target}ObjectId`] = cachedVersion[`${target}ObjectId`];
170
+ }
171
+ if (target === 'source') {
172
+ // this seems to be duplicated in update calls from the GUI
173
+ metadata.filterDefinitionSourceTypeId = metadata[`${target}TypeId`];
174
+ metadata.sourceId = null;
175
+ } else if (target === 'destination') {
176
+ const deItem = cache.getByKey(
177
+ 'dataExtension',
178
+ metadata[`r__${target}_dataExtension_key`]
179
+ );
180
+ metadata.resultDEDescription = deItem?.Description || '';
181
+ metadata.resultDEName = deItem?.Name || '';
182
+ metadata.resultDEKey = deItem?.CustomerKey || '';
183
+
184
+ metadata.resultGroupFolderId = null;
185
+ metadata.resultGroupName = null;
186
+ }
187
+ delete metadata[`r__${target}_dataExtension_key`];
188
+ }
189
+
190
+ // TODO GroupWizard / TypeId==3
191
+
192
+ // TODO BehavioralData / TypeId==4
193
+ }
194
+
195
+ /**
196
+ * Creates a single item
197
+ * this uses soap API because the rest api does not allow hotlinking to an existing target DE
198
+ *
199
+ * @param {FilterItem} item a single item
200
+ * @returns {Promise} Promise
201
+ */
202
+ static async create(item) {
203
+ // create: POST automation/v1/filters/
204
+ // {"filterDefinitionId":"4b9adc79-8f56-488c-9910-ab7ad7afc13a","name":"jb_talent_flat_2_gui","description":"","categoryId":"5319","sourceId":null,"resultGroupFolderId":null,"resultGroupName":null,"filterDefinitionSourceTypeId":2,"folderLocationText":"Filter","customerKey":"jb_talent_flat_2_gui","resultDEName":"jb_talent_flat_2_gui","resultDEKey":"jb_talent_flat_2_gui","resultDEDescription":""}
205
+ // return super.createREST(item, '/automation/v1/filters/');
206
+
207
+ let response = null;
208
+ try {
209
+ response = await super.createSOAP(this.preCreateSOAPItem(item), true);
210
+
211
+ Util.logger.info(` - created ${Util.getTypeKeyName(this.definition, item)}`);
212
+ } catch (ex) {
213
+ this._handleSOAPErrors(ex, 'creating', item, false);
214
+ }
215
+ return this.postCreateTasks(item, response);
216
+ }
217
+
218
+ /**
219
+ * helper that converts the rest item into a soap item
220
+ *
221
+ * @param {FilterItem} item a single item
222
+ * @returns {object} SOAP formatted filter item
223
+ */
224
+ static preCreateSOAPItem(item) {
225
+ return {
226
+ FilterDefinitionID: item.filterDefinitionId,
227
+ Name: item.name,
228
+ Description: item.description || '',
229
+ // CategoryID: item.categoryId,
230
+ CustomerKey: item.customerKey,
231
+ DestinationTypeID: item.destinationTypeId,
232
+ DestinationObjectID: item.destinationObjectId,
233
+ SourceTypeID: item.sourceTypeId,
234
+ SourceObjectID: item.sourceObjectId,
235
+ };
236
+ }
237
+
238
+ /**
239
+ * helper that runs update on all create calls to ensure all fields are set
240
+ *
241
+ * @param {FilterItem} restItem original rest item
242
+ * @param {object} response SOAP response
243
+ * @returns {Promise.<FilterItem>} created item
244
+ */
245
+ static async postCreateTasks(restItem, response) {
246
+ Util.logger.debug(
247
+ ` - running post create tasks for ${Util.getTypeKeyName(this.definition, restItem)} to set the folder ID`
248
+ );
249
+ if (!response) {
250
+ return response;
251
+ }
252
+ const soapItem = response.Results?.[0].Object;
253
+ restItem.filterActivityId = soapItem.FilterActivityID;
254
+ return super.updateREST(
255
+ restItem,
256
+ '/automation/v1/filters/' + restItem[this.definition.idField],
257
+ undefined,
258
+ true
259
+ );
260
+ }
261
+
262
+ /**
263
+ * Updates a single item
264
+ *
265
+ * @param {MetadataTypeItem} item a single item
266
+ * @returns {Promise} Promise
267
+ */
268
+ static update(item) {
269
+ return super.updateREST(item, '/automation/v1/filters/' + item[this.definition.idField]);
270
+ }
271
+
272
+ /**
273
+ * prepares a record for deployment
274
+ *
275
+ * @param {FilterItem} metadata a single record
276
+ * @returns {Promise.<FilterItem>} Promise of updated single record
277
+ */
278
+ static async preDeployTasks(metadata) {
279
+ // folder
280
+ super.setFolderId(metadata);
281
+
282
+ // disable updates to r__dataFilter_key, r__source_dataExtension_key, r__destination_dataExtension_key
283
+ // technically, the API allows changing the reference to the dataFilter but the GUI does not.
284
+ // the API and GUI prevent changes to the source & destination data extensions.
285
+ // to avoid confusion and accidental changes, we will reset the values from cache before deployment
286
+ const normalizedKey = File.reverseFilterIllegalFilenames(
287
+ metadata[this.definition.keyField]
288
+ );
289
+ const cachedVersion = cache.getByKey(this.definition.type, normalizedKey);
290
+
291
+ // filterDefinition
292
+ if (metadata.r__dataFilter_key) {
293
+ metadata.filterDefinitionId = cache.searchForField(
294
+ 'dataFilter',
295
+ metadata.r__dataFilter_key,
296
+ 'key',
297
+ 'id'
298
+ );
299
+ if (cachedVersion && cachedVersion.filterDefinitionId !== metadata.filterDefinitionId) {
300
+ throw new Error(
301
+ `Updating r__dataFilter_key is not allowed. You need to delete and re-create the filter to change this.`
302
+ );
303
+ }
304
+ delete metadata.r__dataFilter_key;
305
+ }
306
+ if (!metadata.description) {
307
+ metadata.description = '';
308
+ }
309
+
310
+ metadata.resultGroupFolderId = null;
311
+ metadata.sourceId = null;
312
+
313
+ this._preDeploy_dataTypeMapping('source', metadata, cachedVersion);
314
+ this._preDeploy_dataTypeMapping('destination', metadata, cachedVersion);
315
+
316
+ return metadata;
317
+ }
318
+
319
+ /**
320
+ * helper to allow us to select single metadata entries via REST
321
+ *
322
+ * @private
323
+ * @param {string} key customer key
324
+ * @returns {Promise.<string>} objectId or empty string
325
+ */
326
+ static async _getObjectIdForSingleRetrieve(key) {
327
+ const name = key.startsWith('name:') ? key.slice(5) : null;
328
+ const response = await this.client.soap.retrieve(this.definition.soapType, ['ObjectID'], {
329
+ filter: {
330
+ leftOperand: name ? 'Name' : 'CustomerKey',
331
+ operator: 'equals',
332
+ rightOperand: name || key,
333
+ },
334
+ });
335
+ return response?.Results?.length ? response.Results[0].ObjectID : '';
336
+ }
337
+
338
+ /**
339
+ * Delete a metadata item from the specified business unit
340
+ *
341
+ * @param {string} key Identifier of data extension
342
+ * @returns {Promise.<boolean>} deletion success flag
343
+ */
344
+ static async deleteByKey(key) {
345
+ // delete only works with the query's object id
346
+ const objectId = key ? await this._getObjectIdForSingleRetrieve(key) : null;
347
+ if (!objectId) {
348
+ await this.deleteNotFound(key);
349
+ return false;
350
+ }
351
+ return super.deleteByKeyREST('/automation/v1/filters/' + objectId, key);
38
352
  }
39
353
  }
40
354
 
@@ -57,12 +57,22 @@ class ImportFile extends MetadataType {
57
57
  );
58
58
  this.smsImports = {};
59
59
  if (smsImportResults.totalResults > 0 && smsImportResults.entry.length > 0) {
60
- Util.logger.info(`Caching dependent Metadata: dataExtension (source for SMS imports)`);
60
+ if (smsImportResults.entry.filter((item) => !!item.sourceObjectId).length > 0) {
61
+ // imports from data extensions require additional information (item.sourceFile != '_CustomObject' and item.sourceObjectId is not set)
62
+ Util.logger.info(
63
+ Util.getGrayMsg(
64
+ ` - Caching dependent Metadata: dataExtension (source for SMS imports)`
65
+ )
66
+ );
67
+ }
61
68
  const sourceObject = {};
62
69
  for (const item of smsImportResults.entry) {
63
70
  // this api does not show the key but the name is assumed to be unique
64
71
  this.smsImports[item.name] = item;
65
72
 
73
+ if (!item.sourceObjectId) {
74
+ continue;
75
+ }
66
76
  try {
67
77
  if (!sourceObject[item.sourceObjectId]) {
68
78
  sourceObject[item.sourceObjectId] = await this.client.rest.get(
@@ -2,6 +2,7 @@
2
2
 
3
3
  import MetadataType from './MetadataType.js';
4
4
  import TransactionalEmail from './TransactionalEmail.js';
5
+ import DataExtensionField from './DataExtensionField.js';
5
6
  import TriggeredSend from './TriggeredSend.js';
6
7
  import Event from './Event.js';
7
8
  import { Util } from '../util/util.js';
@@ -512,7 +513,7 @@ class Journey extends MetadataType {
512
513
  }
513
514
  // ~~~ ACTIVITIES ~~~~
514
515
 
515
- this._postRetrieveTasks_activities(metadata);
516
+ await this._postRetrieveTasks_activities(metadata);
516
517
 
517
518
  // TODO: journey template id? / metaData.templateId
518
519
  break;
@@ -581,7 +582,7 @@ class Journey extends MetadataType {
581
582
  delete activity.metaData.highThroughput.definitionKey;
582
583
  }
583
584
 
584
- this._postRetrieveTasks_activities(metadata);
585
+ await this._postRetrieveTasks_activities(metadata);
585
586
 
586
587
  if (activity.metaData?.highThroughput?.dataExtensionId) {
587
588
  try {
@@ -639,7 +640,7 @@ class Journey extends MetadataType {
639
640
  * @private
640
641
  * @param {MetadataTypeItem} metadata a single item
641
642
  */
642
- static _postRetrieveTasks_activities(metadata) {
643
+ static async _postRetrieveTasks_activities(metadata) {
643
644
  if (!metadata.activities) {
644
645
  return;
645
646
  }
@@ -1239,6 +1240,58 @@ class Journey extends MetadataType {
1239
1240
  }
1240
1241
  break;
1241
1242
  }
1243
+ case 'UPDATECONTACTDATA': {
1244
+ const contactFields =
1245
+ activity?.arguments?.activityData?.updateContactFields ?? [];
1246
+ let tempCachedFields = false;
1247
+ for (const contactField of contactFields) {
1248
+ try {
1249
+ contactField.r__dataExtension_key = cache.searchForField(
1250
+ 'dataExtension',
1251
+ contactField.dataExtensionId,
1252
+ 'ObjectID',
1253
+ 'CustomerKey'
1254
+ );
1255
+ delete contactField.dataExtensionId;
1256
+ } catch (ex) {
1257
+ Util.logger.warn(
1258
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
1259
+ metadata[this.definition.keyField]
1260
+ }) activity-key=${activity.key}: ${ex.message}`
1261
+ );
1262
+ }
1263
+ // Don't replace the field Id with the name if DE CustomerKey was not found
1264
+ try {
1265
+ if (!cache.getCache().dataExtensionField) {
1266
+ tempCachedFields = true;
1267
+ DataExtensionField.buObject = this.buObject;
1268
+ DataExtensionField.client = this.client;
1269
+ DataExtensionField.properties = this.properties;
1270
+ const fields = await DataExtensionField.retrieveFieldsForSingleDe(
1271
+ contactField.r__dataExtension_key
1272
+ );
1273
+ cache.setMetadata('dataExtensionField', fields);
1274
+ }
1275
+ contactField.r__dataExtensionField_name = cache.searchForField(
1276
+ 'dataExtensionField',
1277
+ contactField.field,
1278
+ 'ObjectID',
1279
+ 'Name'
1280
+ );
1281
+ delete contactField.field;
1282
+ } catch (ex) {
1283
+ Util.logger.warn(
1284
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
1285
+ metadata[this.definition.keyField]
1286
+ }) activity-key=${activity.key}: ${ex.message}`
1287
+ );
1288
+ }
1289
+ }
1290
+ if (tempCachedFields) {
1291
+ // reset dataExtensionField caching to trigger re-caching
1292
+ cache.clearCache(this.buObject.mid, 'dataExtensionField');
1293
+ }
1294
+ }
1242
1295
  }
1243
1296
  }
1244
1297
 
@@ -1310,7 +1363,7 @@ class Journey extends MetadataType {
1310
1363
 
1311
1364
  // ~~~ ACTIVITIES ~~~~
1312
1365
 
1313
- this._preDeployTasks_activities(metadata);
1366
+ await this._preDeployTasks_activities(metadata);
1314
1367
 
1315
1368
  break;
1316
1369
  }
@@ -1338,7 +1391,7 @@ class Journey extends MetadataType {
1338
1391
  cachedVersion.status = 'Paused';
1339
1392
  } else {
1340
1393
  throw new Error(
1341
- `Cannot update transactional-send journey in Published status. Run depoy with --publish to auto-pause & republish the journey.`
1394
+ `Cannot update transactional-send journey in Published status. Run deploy with --publish to auto-pause & republish the journey.`
1342
1395
  );
1343
1396
  }
1344
1397
  }
@@ -1404,7 +1457,7 @@ class Journey extends MetadataType {
1404
1457
  delete activity.metaData.highThroughput.r__dataExtension_key;
1405
1458
  }
1406
1459
 
1407
- this._preDeployTasks_activities(metadata);
1460
+ await this._preDeployTasks_activities(metadata);
1408
1461
 
1409
1462
  break;
1410
1463
  }
@@ -1433,7 +1486,7 @@ class Journey extends MetadataType {
1433
1486
  * @private
1434
1487
  * @param {MetadataTypeItem} metadata a single item
1435
1488
  */
1436
- static _preDeployTasks_activities(metadata) {
1489
+ static async _preDeployTasks_activities(metadata) {
1437
1490
  for (const activity of metadata.activities) {
1438
1491
  switch (activity.type) {
1439
1492
  case 'EMAILV2': {
@@ -1666,6 +1719,57 @@ class Journey extends MetadataType {
1666
1719
 
1667
1720
  break;
1668
1721
  }
1722
+ case 'UPDATECONTACTDATA': {
1723
+ const contactFields =
1724
+ activity?.arguments?.activityData?.updateContactFields ?? [];
1725
+ let tempCachedFields = false;
1726
+ for (const contactField of contactFields) {
1727
+ try {
1728
+ contactField.dataExtensionId = cache.searchForField(
1729
+ 'dataExtension',
1730
+ contactField.r__dataExtension_key,
1731
+ 'CustomerKey',
1732
+ 'ObjectID'
1733
+ );
1734
+ } catch (ex) {
1735
+ Util.logger.warn(
1736
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
1737
+ metadata[this.definition.keyField]
1738
+ }) activity-key=${activity.key}: ${ex.message}`
1739
+ );
1740
+ }
1741
+ try {
1742
+ if (!cache.getCache().dataExtensionField) {
1743
+ tempCachedFields = true;
1744
+ DataExtensionField.buObject = this.buObject;
1745
+ DataExtensionField.client = this.client;
1746
+ DataExtensionField.properties = this.properties;
1747
+ const fields = await DataExtensionField.retrieveFieldsForSingleDe(
1748
+ contactField.r__dataExtension_key
1749
+ );
1750
+ cache.setMetadata('dataExtensionField', fields);
1751
+ }
1752
+ contactField.field = cache.searchForField(
1753
+ 'dataExtensionField',
1754
+ `[${contactField.r__dataExtension_key}].[${contactField.r__dataExtensionField_name}]`,
1755
+ 'CustomerKey',
1756
+ 'ObjectID'
1757
+ );
1758
+ delete contactField.r__dataExtensionField_name;
1759
+ delete contactField.r__dataExtension_key;
1760
+ } catch (ex) {
1761
+ Util.logger.warn(
1762
+ ` - ${this.definition.type} '${metadata[this.definition.nameField]}'(${
1763
+ metadata[this.definition.keyField]
1764
+ }) activity - key=${activity.key}: ${ex.message} `
1765
+ );
1766
+ }
1767
+ }
1768
+ if (tempCachedFields) {
1769
+ // reset dataExtensionField caching to trigger re-caching
1770
+ cache.clearCache(this.buObject.mid, 'dataExtensionField');
1771
+ }
1772
+ }
1669
1773
  }
1670
1774
  }
1671
1775
  }
@@ -28,6 +28,7 @@ export default {
28
28
  'emailSend',
29
29
  'fileTransfer',
30
30
  'fileLocation',
31
+ 'filter',
31
32
  'folder-automations',
32
33
  'importFile',
33
34
  'query',