mcdev 9.0.2 → 9.0.3

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 (47) hide show
  1. package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
  2. package/.github/copilot-instructions.md +85 -0
  3. package/.github/pr-labeler.yml +3 -3
  4. package/.github/workflows/coverage-base-update.yml +2 -2
  5. package/.github/workflows/coverage.yml +1 -1
  6. package/.github/workflows/npm-publish.yml +51 -2
  7. package/.github/workflows/sync-milestone.yml +55 -0
  8. package/.husky/post-checkout +1 -1
  9. package/@types/lib/Retriever.d.ts.map +1 -1
  10. package/@types/lib/metadataTypes/DataExtension.d.ts +2 -0
  11. package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
  12. package/@types/lib/metadataTypes/DataExtensionField.d.ts +25 -0
  13. package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
  14. package/@types/lib/metadataTypes/Filter.d.ts +1 -1
  15. package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
  16. package/@types/lib/metadataTypes/MetadataType.d.ts +10 -0
  17. package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
  18. package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts +1 -1
  19. package/@types/lib/util/cli.d.ts.map +1 -1
  20. package/lib/Retriever.js +2 -1
  21. package/lib/index.js +2 -2
  22. package/lib/metadataTypes/DataExtension.js +59 -0
  23. package/lib/metadataTypes/DataExtensionField.js +114 -0
  24. package/lib/metadataTypes/Journey.js +82 -39
  25. package/lib/metadataTypes/MetadataType.js +24 -8
  26. package/lib/metadataTypes/definitions/Filter.definition.js +2 -3
  27. package/lib/util/cli.js +3 -0
  28. package/package.json +10 -10
  29. package/test/general.test.js +11 -11
  30. package/test/mockRoot/.mcdevrc.json +1 -1
  31. package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_journey_updatecontact_sharedDE.journey-meta.json +108 -0
  32. package/test/resources/1111111/dataExtension/template_sharedDE-expected.json +53 -0
  33. package/test/resources/9999999/automation/v1/queries/get-response.json +17 -0
  34. package/test/resources/9999999/interaction/v1/interactions/get-response.json +40 -2
  35. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact/get-response.json +4 -4
  36. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact_sharedDE/get-response.json +119 -0
  37. package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact_sharedDE/put-response.json +110 -0
  38. package/test/resources/9999999/journey/get-updatecontact-sharedDE-expected.json +108 -0
  39. package/test/resources/9999999/journey/put-updatecontact-sharedDE-expected.json +108 -0
  40. package/test/resources/9999999/query/get_sharedDE-expected.json +11 -0
  41. package/test/resources/9999999/query/get_sharedDE-expected.sql +7 -0
  42. package/test/resources/9999999/query/template_sharedDE-expected.json +8 -0
  43. package/test/resources/9999999/query/template_sharedDE-expected.sql +7 -0
  44. package/test/type.attributeSet.test.js +41 -0
  45. package/test/type.automation.test.js +1 -1
  46. package/test/type.journey.test.js +108 -6
  47. package/test/type.query.test.js +105 -8
@@ -4,6 +4,7 @@ import MetadataType from './MetadataType.js';
4
4
  import { Util } from '../util/util.js';
5
5
  import DataExtension from './DataExtension.js';
6
6
  import { confirm } from '@inquirer/prompts';
7
+ import auth from '../util/auth.js';
7
8
 
8
9
  /**
9
10
  * @typedef {import('../../types/mcdev.d.js').BuObject} BuObject
@@ -75,6 +76,95 @@ class DataExtensionField extends MetadataType {
75
76
  }, {});
76
77
  }
77
78
 
79
+ /**
80
+ * determines if a dataExtension is shared (from parent BU) by checking its folder's ContentType in the parent BU folder cache
81
+ *
82
+ * @param {string} customerKey external key of single DE
83
+ * @returns {boolean} true if the DE is shared (stored on parent BU)
84
+ */
85
+ static isSharedDe(customerKey) {
86
+ if (this.buObject.eid === this.buObject.mid) {
87
+ // already on parent BU, no shared DEs possible
88
+ return false;
89
+ }
90
+ const categoryId = cache.getCache().dataExtension?.[customerKey]?.CategoryID;
91
+ if (!categoryId) {
92
+ return false;
93
+ }
94
+ try {
95
+ const contentType = cache.searchForField(
96
+ 'folder',
97
+ categoryId,
98
+ 'ID',
99
+ 'ContentType',
100
+ this.buObject.eid
101
+ );
102
+ return MetadataTypeDefinitions.folder.folderTypesFromParent.includes(contentType);
103
+ } catch {
104
+ // folder not found in parent BU cache → local DE
105
+ return false;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * helps retrieving fields for a single DE from parent BU.
111
+ * Use when the DE is shared (stored on parent BU) and its fields are not in the current BU's cache.
112
+ *
113
+ * @param {string} customerKey external key of single DE
114
+ * @returns {Promise.<object>} Promise of items
115
+ */
116
+ static async retrieveFieldsForSingleSharedDe(customerKey) {
117
+ if (this.buObject.eid === this.buObject.mid) {
118
+ // already on parent BU, shared retrieval is not needed - all DEs are local here
119
+ Util.logger.debug(
120
+ `DataExtensionField.retrieveFieldsForSingleSharedDe: already on parent BU, skipping for ${customerKey}`
121
+ );
122
+ return {};
123
+ }
124
+ if (!this.properties.credentials[this.buObject.credential]) {
125
+ Util.logger.error(
126
+ `DataExtensionField.retrieveFieldsForSingleSharedDe: credential '${this.buObject.credential}' not found in properties`
127
+ );
128
+ return {};
129
+ }
130
+ const buObjectBak = this.buObject;
131
+ const clientBak = this.client;
132
+ /** @type {BuObject} */
133
+ const buObjectParentBu = {
134
+ eid: this.properties.credentials[this.buObject.credential].eid,
135
+ mid: this.properties.credentials[this.buObject.credential].eid,
136
+ businessUnit: Util.parentBuName,
137
+ credential: this.buObject.credential,
138
+ };
139
+ try {
140
+ this.buObject = buObjectParentBu;
141
+ this.client = auth.getSDK(buObjectParentBu);
142
+ } catch (ex) {
143
+ Util.logger.error(ex.message);
144
+ this.buObject = buObjectBak;
145
+ this.client = clientBak;
146
+ return {};
147
+ }
148
+ const fields = await this.retrieveFieldsForSingleDe(customerKey);
149
+ // revert client to current default
150
+ this.buObject = buObjectBak;
151
+ this.client = clientBak;
152
+ return fields;
153
+ }
154
+
155
+ /**
156
+ * Routes field retrieval to the correct method depending on whether the DE is local or shared.
157
+ * Use this instead of duplicating the isSharedDe() check at each call site.
158
+ *
159
+ * @param {string} customerKey external key of single DE
160
+ * @returns {Promise.<object>} Promise of items
161
+ */
162
+ static async retrieveFieldsForSingleDeAuto(customerKey) {
163
+ return this.isSharedDe(customerKey)
164
+ ? this.retrieveFieldsForSingleSharedDe(customerKey)
165
+ : this.retrieveFieldsForSingleDe(customerKey);
166
+ }
167
+
78
168
  /**
79
169
  * Retrieves all records for caching
80
170
  *
@@ -187,6 +277,10 @@ class DataExtensionField extends MetadataType {
187
277
  // make sure we stringify the name in case it looked numeric and then lowercase it for easy comparison as the server is comparing field names case-insensitive
188
278
  existingFieldByName[(fieldsObj[key].Name + '').toLowerCase()] = fieldsObj[key];
189
279
  }
280
+ // capture field names from deploy payload before the loop modifies them (for renamed fields, the original name is used)
281
+ const deployColumnNames = new Set(
282
+ deployColumns.map((item) => (item.Name + '').toLowerCase())
283
+ );
190
284
  for (let i = deployColumns.length - 1; i >= 0; i--) {
191
285
  const item = deployColumns[i];
192
286
  // make sure we stringify the name in case it looked numeric and then lowercase it for easy comparison as the server is comparing field names case-insensitive
@@ -304,6 +398,26 @@ class DataExtensionField extends MetadataType {
304
398
  )
305
399
  );
306
400
 
401
+ // warn about fields that exist on the server but are not in the deploy payload
402
+ // these fields are NOT auto-deleted to prevent unintended data loss
403
+ const removedFieldNames = Object.values(existingFieldByName)
404
+ .filter((field) => !deployColumnNames.has((field.Name + '').toLowerCase()))
405
+ .map((field) => field.Name)
406
+ .toSorted();
407
+ if (removedFieldNames.length > 0) {
408
+ Util.logger.warn(
409
+ ` - dataExtension ${deKey}: found ${removedFieldNames.length} field${removedFieldNames.length > 1 ? 's' : ''} on the server but not in the deploy payload. Fields cannot be auto-deleted due to potential data loss. To delete them, please run:`
410
+ );
411
+ Util.logger.info(
412
+ Util.getGrayMsg(
413
+ `mcdev delete ${this.buObject.credential}/${this.buObject.businessUnit} -m ` +
414
+ removedFieldNames
415
+ .map((name) => `dataExtensionField:"${deKey}.${name}"`)
416
+ .join(' ')
417
+ )
418
+ );
419
+ }
420
+
307
421
  return existingFieldByName;
308
422
  }
309
423
 
@@ -1286,25 +1286,53 @@ class Journey extends MetadataType {
1286
1286
  metadata[this.definition.keyField]
1287
1287
  }) activity-key=${activity.key}: ${ex.message}`
1288
1288
  );
1289
+ continue;
1289
1290
  }
1290
1291
  // Don't replace the field Id with the name if DE CustomerKey was not found
1291
1292
  try {
1293
+ DataExtensionField.buObject = this.buObject;
1294
+ DataExtensionField.client = this.client;
1295
+ DataExtensionField.properties = this.properties;
1296
+ let alreadyFetchedDe = false;
1292
1297
  if (!cache.getCache().dataExtensionField) {
1298
+ // no dataExtensionField cache yet: retrieve fields for this specific DE
1293
1299
  tempCachedFields = true;
1294
- DataExtensionField.buObject = this.buObject;
1295
- DataExtensionField.client = this.client;
1296
- DataExtensionField.properties = this.properties;
1297
- const fields = await DataExtensionField.retrieveFieldsForSingleDe(
1298
- contactField.r__dataExtension_key
1299
- );
1300
+ alreadyFetchedDe = true;
1301
+ const fields =
1302
+ await DataExtensionField.retrieveFieldsForSingleDeAuto(
1303
+ contactField.r__dataExtension_key
1304
+ );
1300
1305
  cache.setMetadata('dataExtensionField', fields);
1301
1306
  }
1302
- contactField.r__dataExtensionField_name = cache.searchForField(
1303
- 'dataExtensionField',
1304
- contactField.field,
1305
- 'ObjectID',
1306
- 'Name'
1307
- );
1307
+ let fieldName;
1308
+ try {
1309
+ fieldName = cache.searchForField(
1310
+ 'dataExtensionField',
1311
+ contactField.field,
1312
+ 'ObjectID',
1313
+ 'Name'
1314
+ );
1315
+ } catch (ex) {
1316
+ if (alreadyFetchedDe) {
1317
+ throw ex;
1318
+ }
1319
+ // field not in existing cache - retrieve fields for this specific DE
1320
+ const fields =
1321
+ await DataExtensionField.retrieveFieldsForSingleDeAuto(
1322
+ contactField.r__dataExtension_key
1323
+ );
1324
+ if (Object.keys(fields).length > 0) {
1325
+ cache.mergeMetadata('dataExtensionField', fields);
1326
+ }
1327
+ // throws if still not found, handled by outer catch
1328
+ fieldName = cache.searchForField(
1329
+ 'dataExtensionField',
1330
+ contactField.field,
1331
+ 'ObjectID',
1332
+ 'Name'
1333
+ );
1334
+ }
1335
+ contactField.r__dataExtensionField_name = fieldName;
1308
1336
  delete contactField.field;
1309
1337
  } catch (ex) {
1310
1338
  Util.logger.warn(
@@ -1751,46 +1779,61 @@ class Journey extends MetadataType {
1751
1779
  activity?.arguments?.activityData?.updateContactFields ?? [];
1752
1780
  let tempCachedFields = false;
1753
1781
  for (const contactField of contactFields) {
1782
+ if (!contactField.r__dataExtension_key) {
1783
+ continue;
1784
+ }
1785
+ contactField.dataExtensionId = cache.searchForField(
1786
+ 'dataExtension',
1787
+ contactField.r__dataExtension_key,
1788
+ 'CustomerKey',
1789
+ 'ObjectID'
1790
+ );
1791
+ if (!contactField.r__dataExtensionField_name) {
1792
+ continue;
1793
+ }
1794
+ DataExtensionField.buObject = this.buObject;
1795
+ DataExtensionField.client = this.client;
1796
+ DataExtensionField.properties = this.properties;
1797
+ let alreadyFetchedDe = false;
1798
+ if (!cache.getCache().dataExtensionField) {
1799
+ // no dataExtensionField cache yet: retrieve fields for this specific DE
1800
+ tempCachedFields = true;
1801
+ alreadyFetchedDe = true;
1802
+ const fields = await DataExtensionField.retrieveFieldsForSingleDeAuto(
1803
+ contactField.r__dataExtension_key
1804
+ );
1805
+ cache.setMetadata('dataExtensionField', fields);
1806
+ }
1807
+ let fieldObjectId;
1754
1808
  try {
1755
- contactField.dataExtensionId = cache.searchForField(
1756
- 'dataExtension',
1757
- contactField.r__dataExtension_key,
1809
+ fieldObjectId = cache.searchForField(
1810
+ 'dataExtensionField',
1811
+ `[${contactField.r__dataExtension_key}].[${contactField.r__dataExtensionField_name}]`,
1758
1812
  'CustomerKey',
1759
1813
  'ObjectID'
1760
1814
  );
1761
1815
  } catch (ex) {
1762
- Util.logger.warn(
1763
- ` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
1764
- metadata[this.definition.keyField]
1765
- }) activity-key=${activity.key}: ${ex.message}`
1816
+ // field not in existing cache - retrieve fields for this specific DE
1817
+ if (alreadyFetchedDe) {
1818
+ throw ex;
1819
+ }
1820
+ const fields = await DataExtensionField.retrieveFieldsForSingleDeAuto(
1821
+ contactField.r__dataExtension_key
1766
1822
  );
1767
- }
1768
- try {
1769
- if (!cache.getCache().dataExtensionField) {
1770
- tempCachedFields = true;
1771
- DataExtensionField.buObject = this.buObject;
1772
- DataExtensionField.client = this.client;
1773
- DataExtensionField.properties = this.properties;
1774
- const fields = await DataExtensionField.retrieveFieldsForSingleDe(
1775
- contactField.r__dataExtension_key
1776
- );
1777
- cache.setMetadata('dataExtensionField', fields);
1823
+ if (Object.keys(fields).length > 0) {
1824
+ cache.mergeMetadata('dataExtensionField', fields);
1778
1825
  }
1779
- contactField.field = cache.searchForField(
1826
+ // throws if still not found, handled by outer catch
1827
+ fieldObjectId = cache.searchForField(
1780
1828
  'dataExtensionField',
1781
1829
  `[${contactField.r__dataExtension_key}].[${contactField.r__dataExtensionField_name}]`,
1782
1830
  'CustomerKey',
1783
1831
  'ObjectID'
1784
1832
  );
1785
- delete contactField.r__dataExtensionField_name;
1786
- delete contactField.r__dataExtension_key;
1787
- } catch (ex) {
1788
- Util.logger.warn(
1789
- ` - ${this.definition.type} '${metadata[this.definition.nameField]}'(${
1790
- metadata[this.definition.keyField]
1791
- }) activity - key=${activity.key}: ${ex.message} `
1792
- );
1793
1833
  }
1834
+ contactField.field = fieldObjectId;
1835
+ delete contactField.r__dataExtensionField_name;
1836
+ delete contactField.r__dataExtension_key;
1794
1837
  }
1795
1838
  if (tempCachedFields) {
1796
1839
  // reset dataExtensionField caching to trigger re-caching
@@ -2785,14 +2785,17 @@ class MetadataType {
2785
2785
  (key) => !multiTypeKeyList[this.definition.type].includes(key)
2786
2786
  );
2787
2787
  if (notFound && notFound.length) {
2788
- Util.logger.warn(
2789
- Util.getGrayMsg(
2790
- ` ☇ skipping ${this.definition.type} dependenc${notFound.length === 1 ? 'y' : 'ies'} ${notFound.join(', ')}: Not found on your in your project folder.`
2791
- )
2792
- );
2793
- // make sure we don't search for it twice
2794
- notFoundList[this.definition.type] ||= [];
2795
- notFoundList[this.definition.type].push(...notFound);
2788
+ const remainingNotFound = await this.handleNotFoundDependencies(notFound);
2789
+ if (remainingNotFound.length) {
2790
+ Util.logger.warn(
2791
+ Util.getGrayMsg(
2792
+ ` ☇ skipping ${this.definition.type} dependenc${remainingNotFound.length === 1 ? 'y' : 'ies'} ${remainingNotFound.join(', ')}: Not found on your in your project folder.`
2793
+ )
2794
+ );
2795
+ // make sure we don't search for it twice
2796
+ notFoundList[this.definition.type] ||= [];
2797
+ notFoundList[this.definition.type].push(...remainingNotFound);
2798
+ }
2796
2799
  }
2797
2800
  return multiTypeKeyList;
2798
2801
  }
@@ -2805,6 +2808,19 @@ class MetadataType {
2805
2808
  */
2806
2809
  static getDependentFilesExtra(metadataItem, dependentTypeKeyCombo) {}
2807
2810
 
2811
+ /**
2812
+ * Hook called when dependency keys were not found in the primary retrieve folder.
2813
+ * Override in subtypes to show type-specific warnings (e.g. shared/synchronized dataExtensions).
2814
+ * Keys returned by this method still receive the generic "not found" warning.
2815
+ * Used by {@link MetadataType.getDependentFiles}.
2816
+ *
2817
+ * @param {string[]} notFound keys that could not be found in the retrieve folder
2818
+ * @returns {Promise.<string[]>} keys that should still trigger the default "not found" warning
2819
+ */
2820
+ static async handleNotFoundDependencies(notFound) {
2821
+ return notFound;
2822
+ }
2823
+
2808
2824
  /**
2809
2825
  * helper for {@link MetadataType.getDependentFiles}
2810
2826
  *
@@ -9,7 +9,7 @@ export default {
9
9
  'folder-hidden',
10
10
  ],
11
11
  dependencyGraph: {
12
- filterDefinition: ['r__dataFilter_key'],
12
+ dataFilter: ['r__dataFilter_key'],
13
13
  dataExtension: ['r__source_dataExtension_key', 'r__destination_dataExtension_key'],
14
14
  },
15
15
  hasExtended: false,
@@ -30,8 +30,7 @@ export default {
30
30
  maxKeyLength: 36, // confirmed max length
31
31
  type: 'filter',
32
32
  soapType: 'FilterActivity',
33
- typeDescription:
34
- 'Used in automations to filter lists and DEs. Depends on type "FilterDefinitions".',
33
+ typeDescription: 'Used in automations to filter lists and DEs. Depends on type "DataFilter".',
35
34
  typeRetrieveByDefault: true,
36
35
  typeCdpByDefault: true,
37
36
  typeName: 'Automation: Filter Activity',
package/lib/util/cli.js CHANGED
@@ -87,6 +87,9 @@ const Cli = {
87
87
  return true;
88
88
  } else if (Util.isFalse(Util.skipInteraction?.fixKeysReretrieve)) {
89
89
  return false;
90
+ } else if (Util.skipInteraction) {
91
+ // generic --yes / --skipInteraction: use the confirm default (true = do re-retrieve)
92
+ return true;
90
93
  } else {
91
94
  const fixKeysReretrieve = await confirm({
92
95
  message: `Do you want to re-retrieve dependent types (${dependentTypes.join(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcdev",
3
- "version": "9.0.2",
3
+ "version": "9.0.3",
4
4
  "description": "Accenture Salesforce Marketing Cloud DevTools",
5
5
  "author": "Accenture: joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas",
6
6
  "license": "MIT",
@@ -74,15 +74,15 @@
74
74
  "console.table": "0.10.0",
75
75
  "deep-equal": "2.2.3",
76
76
  "fs-extra": "11.3.4",
77
- "inquirer": "13.3.0",
77
+ "inquirer": "13.3.2",
78
78
  "json-to-table": "4.2.1",
79
79
  "mustache": "4.2.0",
80
80
  "p-limit": "7.3.0",
81
81
  "prettier": "3.8.1",
82
- "prettier-plugin-sql": "0.19.2",
82
+ "prettier-plugin-sql": "0.20.0",
83
83
  "semver": "7.7.4",
84
- "sfmc-sdk": "3.0.1",
85
- "simple-git": "3.32.3",
84
+ "sfmc-sdk": "3.0.3",
85
+ "simple-git": "3.33.0",
86
86
  "toposort": "2.0.2",
87
87
  "update-notifier": "7.3.1",
88
88
  "winston": "3.19.0",
@@ -94,23 +94,23 @@
94
94
  "@types/fs-extra": "11.0.4",
95
95
  "@types/inquirer": "9.0.9",
96
96
  "@types/mocha": "10.0.8",
97
- "@types/node": "25.3.5",
97
+ "@types/node": "25.5.0",
98
98
  "@types/yargs": "17.0.35",
99
99
  "assert": "2.1.0",
100
100
  "axios-mock-adapter": "2.0.0",
101
101
  "c8": "11.0.0",
102
102
  "chai": "6.2.2",
103
103
  "chai-files": "1.4.0",
104
- "eslint": "10.0.3",
104
+ "eslint": "10.1.0",
105
105
  "eslint-config-ssjs": "2.0.0",
106
- "eslint-plugin-jsdoc": "62.7.1",
106
+ "eslint-plugin-jsdoc": "62.8.0",
107
107
  "eslint-plugin-mocha": "11.2.0",
108
108
  "eslint-plugin-prettier": "5.5.5",
109
109
  "eslint-plugin-unicorn": "63.0.0",
110
- "fast-xml-parser": "5.4.2",
110
+ "fast-xml-parser": "5.5.9",
111
111
  "globals": "17.4.0",
112
112
  "husky": "9.1.7",
113
- "lint-staged": "16.3.2",
113
+ "lint-staged": "16.4.0",
114
114
  "mocha": "11.7.5",
115
115
  "mock-fs": "5.3.0",
116
116
  "npm-run-all": "4.1.5",
@@ -60,7 +60,7 @@ describe('GENERAL', () => {
60
60
  );
61
61
  assert.equal(
62
62
  testUtils.getAPIHistoryLength(),
63
- 46,
63
+ 48,
64
64
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
65
65
  );
66
66
  return;
@@ -108,7 +108,7 @@ describe('GENERAL', () => {
108
108
  );
109
109
  assert.equal(
110
110
  testUtils.getAPIHistoryLength(),
111
- 46,
111
+ 48,
112
112
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
113
113
  );
114
114
  return;
@@ -157,7 +157,7 @@ describe('GENERAL', () => {
157
157
 
158
158
  assert.equal(
159
159
  testUtils.getAPIHistoryLength(),
160
- 46,
160
+ 48,
161
161
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
162
162
  );
163
163
  return;
@@ -224,7 +224,7 @@ describe('GENERAL', () => {
224
224
 
225
225
  assert.equal(
226
226
  testUtils.getAPIHistoryLength(),
227
- 82,
227
+ 84,
228
228
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
229
229
  );
230
230
  return;
@@ -284,7 +284,7 @@ describe('GENERAL', () => {
284
284
 
285
285
  assert.equal(
286
286
  testUtils.getAPIHistoryLength(),
287
- 82,
287
+ 84,
288
288
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
289
289
  );
290
290
  return;
@@ -344,7 +344,7 @@ describe('GENERAL', () => {
344
344
 
345
345
  assert.equal(
346
346
  testUtils.getAPIHistoryLength(),
347
- 82,
347
+ 84,
348
348
  'Unexpected number of requests made. Run testUtils.logAPIHistoryDebug() to see the requests'
349
349
  );
350
350
  return;
@@ -985,7 +985,7 @@ describe('GENERAL', () => {
985
985
  // download first before we test buildTemplate
986
986
  await handler.retrieve('testInstance/testBU');
987
987
 
988
- const expectedApiCallsRetrieve = 109;
988
+ const expectedApiCallsRetrieve = 111;
989
989
  assert.equal(
990
990
  testUtils.getAPIHistoryLength(),
991
991
  expectedApiCallsRetrieve,
@@ -1227,7 +1227,7 @@ describe('GENERAL', () => {
1227
1227
  await testUtils.getActualDeployFile('testTemplated_query', 'query', 'sql')
1228
1228
  ).to.equal(await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'));
1229
1229
 
1230
- const expectedApiCallsRetrieve = 113;
1230
+ const expectedApiCallsRetrieve = 115;
1231
1231
  assert.equal(
1232
1232
  testUtils.getAPIHistoryLength(),
1233
1233
  expectedApiCallsRetrieve,
@@ -1601,7 +1601,7 @@ describe('GENERAL', () => {
1601
1601
  // download everything before we test buildTemplate
1602
1602
  await handler.retrieve('testInstance/testBU');
1603
1603
 
1604
- const expectedApiCallsRetrieve = 109;
1604
+ const expectedApiCallsRetrieve = 111;
1605
1605
  assert.equal(
1606
1606
  testUtils.getAPIHistoryLength(),
1607
1607
  expectedApiCallsRetrieve,
@@ -1812,7 +1812,7 @@ describe('GENERAL', () => {
1812
1812
  await testUtils.getActualDeployFile('testTemplated_query', 'query', 'sql')
1813
1813
  ).to.equal(await testUtils.getExpectedFile('9999999', 'query', 'build', 'sql'));
1814
1814
 
1815
- const expectedApiCallsRetrieve = 113;
1815
+ const expectedApiCallsRetrieve = 115;
1816
1816
  assert.equal(
1817
1817
  testUtils.getAPIHistoryLength(),
1818
1818
  expectedApiCallsRetrieve,
@@ -2003,7 +2003,7 @@ describe('GENERAL', () => {
2003
2003
  // download first before we test buildTemplate
2004
2004
  await handler.retrieve('testInstance/testBU');
2005
2005
 
2006
- const expectedApiCallsRetrieve = 109;
2006
+ const expectedApiCallsRetrieve = 111;
2007
2007
  assert.equal(
2008
2008
  testUtils.getAPIHistoryLength(),
2009
2009
  expectedApiCallsRetrieve,
@@ -178,5 +178,5 @@
178
178
  "verification"
179
179
  ]
180
180
  },
181
- "version": "9.0.2"
181
+ "version": "9.0.3"
182
182
  }
@@ -0,0 +1,108 @@
1
+ {
2
+ "key": "testExisting_journey_updatecontact_sharedDE",
3
+ "name": "testExisting_journey_updatecontact_sharedDE",
4
+ "lastPublishedDate": "0001-01-01T00:00:00",
5
+ "description": "",
6
+ "version": 1,
7
+ "workflowApiVersion": 1,
8
+ "createdDate": "2024-05-04T05:54:48.027",
9
+ "modifiedDate": "2024-05-04T17:25:57.9",
10
+ "activities": [
11
+ {
12
+ "key": "UPDATECONTACTDATAV2-1",
13
+ "name": "Update Contact - TEST",
14
+ "description": "",
15
+ "type": "UPDATECONTACTDATA",
16
+ "outcomes": [
17
+ {
18
+ "key": "9f21d42b-4679-4561-911a-4bccf84672ba",
19
+ "arguments": {},
20
+ "metaData": {
21
+ "invalid": false
22
+ }
23
+ }
24
+ ],
25
+ "arguments": {
26
+ "activityData": {
27
+ "updateContactFields": [
28
+ {
29
+ "r__dataExtension_key": "testExisting_dataExtensionShared",
30
+ "r__dataExtensionField_name": "LastName",
31
+ "value": "TEST"
32
+ }
33
+ ]
34
+ }
35
+ },
36
+ "configurationArguments": {},
37
+ "metaData": {
38
+ "isConfigured": true
39
+ },
40
+ "schema": {
41
+ "arguments": {
42
+ "activityData": {
43
+ "dataType": "Text",
44
+ "isNullable": false,
45
+ "direction": "In",
46
+ "readOnly": false,
47
+ "access": "Hidden"
48
+ },
49
+ "contactKey": {
50
+ "dataType": "Text",
51
+ "isNullable": false,
52
+ "direction": "In",
53
+ "readOnly": false,
54
+ "access": "Hidden"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ],
60
+ "triggers": [
61
+ {
62
+ "key": "TRIGGER",
63
+ "name": "TRIGGER",
64
+ "description": "",
65
+ "type": "EmailAudience",
66
+ "outcomes": [],
67
+ "arguments": {},
68
+ "configurationArguments": {},
69
+ "metaData": {
70
+ "r__event_key": "DEAudience-2e3c73b6-48cc-2ec0-5522-48636e1a236e",
71
+ "chainType": "None",
72
+ "configurationRequired": false,
73
+ "iconUrl": "/images/icon-data-extension.svg",
74
+ "title": "Data Extension",
75
+ "entrySourceGroupConfigUrl": "jb:///data/entry/audience/entrysourcegroupconfig.json",
76
+ "sourceInteractionId": "00000000-0000-0000-0000-000000000000"
77
+ }
78
+ }
79
+ ],
80
+ "goals": [],
81
+ "exits": [],
82
+ "notifiers": [],
83
+ "entryMode": "NotSet",
84
+ "definitionType": "Multistep",
85
+ "channel": "",
86
+ "defaults": {
87
+ "email": [
88
+ "{{Event.DEAudience-2e3c73b6-48cc-2ec0-5522-48636e1a236e.\"Email\"}}"
89
+ ],
90
+ "properties": {
91
+ "analyticsTracking": {
92
+ "enabled": true,
93
+ "analyticsType": "google",
94
+ "urlDomainsToTrack": []
95
+ }
96
+ }
97
+ },
98
+ "metaData": {
99
+ "dataSource": "ContactsModel",
100
+ "highThroughputSending": {
101
+ "email": false
102
+ }
103
+ },
104
+ "executionMode": "Production",
105
+ "status": "Draft",
106
+ "scheduledStatus": "Draft",
107
+ "r__folder_Path": "my journeys"
108
+ }