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.
- package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
- package/.github/copilot-instructions.md +85 -0
- package/.github/pr-labeler.yml +3 -3
- package/.github/workflows/coverage-base-update.yml +2 -2
- package/.github/workflows/coverage.yml +1 -1
- package/.github/workflows/npm-publish.yml +51 -2
- package/.github/workflows/sync-milestone.yml +55 -0
- package/.husky/post-checkout +1 -1
- package/@types/lib/Retriever.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataExtension.d.ts +2 -0
- package/@types/lib/metadataTypes/DataExtension.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DataExtensionField.d.ts +25 -0
- package/@types/lib/metadataTypes/DataExtensionField.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Filter.d.ts +1 -1
- package/@types/lib/metadataTypes/Journey.d.ts.map +1 -1
- package/@types/lib/metadataTypes/MetadataType.d.ts +10 -0
- package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
- package/@types/lib/metadataTypes/definitions/Filter.definition.d.ts +1 -1
- package/@types/lib/util/cli.d.ts.map +1 -1
- package/lib/Retriever.js +2 -1
- package/lib/index.js +2 -2
- package/lib/metadataTypes/DataExtension.js +59 -0
- package/lib/metadataTypes/DataExtensionField.js +114 -0
- package/lib/metadataTypes/Journey.js +82 -39
- package/lib/metadataTypes/MetadataType.js +24 -8
- package/lib/metadataTypes/definitions/Filter.definition.js +2 -3
- package/lib/util/cli.js +3 -0
- package/package.json +10 -10
- package/test/general.test.js +11 -11
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_journey_updatecontact_sharedDE.journey-meta.json +108 -0
- package/test/resources/1111111/dataExtension/template_sharedDE-expected.json +53 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +17 -0
- package/test/resources/9999999/interaction/v1/interactions/get-response.json +40 -2
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact/get-response.json +4 -4
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact_sharedDE/get-response.json +119 -0
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_journey_updatecontact_sharedDE/put-response.json +110 -0
- package/test/resources/9999999/journey/get-updatecontact-sharedDE-expected.json +108 -0
- package/test/resources/9999999/journey/put-updatecontact-sharedDE-expected.json +108 -0
- package/test/resources/9999999/query/get_sharedDE-expected.json +11 -0
- package/test/resources/9999999/query/get_sharedDE-expected.sql +7 -0
- package/test/resources/9999999/query/template_sharedDE-expected.json +8 -0
- package/test/resources/9999999/query/template_sharedDE-expected.sql +7 -0
- package/test/type.attributeSet.test.js +41 -0
- package/test/type.automation.test.js +1 -1
- package/test/type.journey.test.js +108 -6
- 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
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
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
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
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
|
-
|
|
1756
|
-
'
|
|
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
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
82
|
+
"prettier-plugin-sql": "0.20.0",
|
|
83
83
|
"semver": "7.7.4",
|
|
84
|
-
"sfmc-sdk": "3.0.
|
|
85
|
-
"simple-git": "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.
|
|
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
|
|
104
|
+
"eslint": "10.1.0",
|
|
105
105
|
"eslint-config-ssjs": "2.0.0",
|
|
106
|
-
"eslint-plugin-jsdoc": "62.
|
|
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.
|
|
110
|
+
"fast-xml-parser": "5.5.9",
|
|
111
111
|
"globals": "17.4.0",
|
|
112
112
|
"husky": "9.1.7",
|
|
113
|
-
"lint-staged": "16.
|
|
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",
|
package/test/general.test.js
CHANGED
|
@@ -60,7 +60,7 @@ describe('GENERAL', () => {
|
|
|
60
60
|
);
|
|
61
61
|
assert.equal(
|
|
62
62
|
testUtils.getAPIHistoryLength(),
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
2006
|
+
const expectedApiCallsRetrieve = 111;
|
|
2007
2007
|
assert.equal(
|
|
2008
2008
|
testUtils.getAPIHistoryLength(),
|
|
2009
2009
|
expectedApiCallsRetrieve,
|
|
@@ -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
|
+
}
|