mcdev 7.8.0 → 7.10.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 +2 -0
- package/@types/lib/index.d.ts +2 -1
- package/@types/lib/index.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Asset.d.ts +8 -0
- package/@types/lib/metadataTypes/Asset.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Automation.d.ts.map +1 -1
- package/@types/lib/metadataTypes/DomainVerification.d.ts +10 -2
- package/@types/lib/metadataTypes/DomainVerification.d.ts.map +1 -1
- package/@types/lib/metadataTypes/MetadataType.d.ts +8 -1
- package/@types/lib/metadataTypes/MetadataType.d.ts.map +1 -1
- package/@types/lib/metadataTypes/MobileKeyword.d.ts +8 -6
- package/@types/lib/metadataTypes/MobileKeyword.d.ts.map +1 -1
- package/@types/lib/metadataTypes/MobileMessage.d.ts +8 -0
- package/@types/lib/metadataTypes/MobileMessage.d.ts.map +1 -1
- package/@types/lib/metadataTypes/Verification.d.ts +12 -5
- package/@types/lib/metadataTypes/Verification.d.ts.map +1 -1
- package/@types/lib/metadataTypes/definitions/Verification.definition.d.ts +10 -0
- package/@types/lib/util/cli.d.ts +0 -9
- package/@types/lib/util/cli.d.ts.map +1 -1
- package/@types/lib/util/init.config.d.ts +2 -2
- package/@types/lib/util/init.config.d.ts.map +1 -1
- package/@types/lib/util/util.d.ts +15 -2
- package/@types/lib/util/util.d.ts.map +1 -1
- package/@types/types/mcdev.d.d.ts +20 -0
- package/@types/types/mcdev.d.d.ts.map +1 -1
- package/boilerplate/files/eslint.config.js +30 -27
- package/boilerplate/forcedUpdates.json +4 -0
- package/lib/index.js +28 -5
- package/lib/metadataTypes/Automation.js +0 -18
- package/lib/metadataTypes/DomainVerification.js +62 -2
- package/lib/metadataTypes/MetadataType.js +18 -4
- package/lib/metadataTypes/MobileKeyword.js +2 -52
- package/lib/metadataTypes/Verification.js +94 -58
- package/lib/metadataTypes/definitions/TriggeredSend.definition.js +3 -3
- package/lib/metadataTypes/definitions/Verification.definition.js +8 -2
- package/lib/util/cli.js +1 -30
- package/lib/util/config.js +3 -3
- package/lib/util/init.config.js +89 -28
- package/lib/util/util.js +84 -2
- package/package.json +1 -1
- package/test/general.test.js +3 -18
- package/test/mockRoot/.mcdevrc.json +2 -4
- package/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/verification/{testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json → testExisting_automation__s1.7.verification-meta.json} +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/verification/{testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json → testNew_automation__s1.7.verification-meta.json} +1 -1
- package/test/resources/9999999/automation/build-expected.json +1 -1
- package/test/resources/9999999/automation/clone-expected.json +1 -1
- package/test/resources/9999999/automation/create-callout-expected.json +1 -1
- package/test/resources/9999999/automation/create-expected.json +1 -1
- package/test/resources/9999999/automation/create-testNew_automation-expected.md +1 -1
- package/test/resources/9999999/automation/retrieve-expected.json +1 -1
- package/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md +1 -1
- package/test/resources/9999999/automation/template-expected.json +1 -1
- package/test/resources/9999999/dataFolder/retrieve-ContentTypeINasset,asset-sha,automatio,dataexten,hidden,journey,list,mysubs,publicati,queryacti,salesforc,shared_da,shared_da,shared_sa,ssjsactiv,synchroni,useriniti-response.xml +291 -0
- package/test/resources/9999999/verification/build-expected.json +1 -1
- package/test/resources/9999999/verification/get-expected.json +1 -1
- package/test/resources/9999999/verification/patch-expected.json +1 -1
- package/test/resources/9999999/verification/post-expected.json +1 -1
- package/test/resources/9999999/verification/template-expected.json +1 -1
- package/test/type.automation.test.js +3 -3
- package/test/type.verification.test.js +7 -13
- package/test/utils.js +3 -1
- package/types/mcdev.d.js +5 -0
|
@@ -16,6 +16,31 @@ export default [
|
|
|
16
16
|
{
|
|
17
17
|
plugins: { jsdoc },
|
|
18
18
|
rules: {
|
|
19
|
+
'unicorn/better-regex': 'off',
|
|
20
|
+
'unicorn/prefer-string-raw': 'off',
|
|
21
|
+
'unicorn/catch-error-name': [
|
|
22
|
+
'error',
|
|
23
|
+
{
|
|
24
|
+
name: 'ex'
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
'unicorn/explicit-length-check': 'off',
|
|
28
|
+
'unicorn/no-null': 'off',
|
|
29
|
+
'unicorn/prefer-module': 'off',
|
|
30
|
+
'unicorn/prevent-abbreviations': 'off',
|
|
31
|
+
'unicorn/filename-case': 'off',
|
|
32
|
+
'unicorn/no-array-callback-reference': 'off',
|
|
33
|
+
'unicorn/no-array-reduce': 'off',
|
|
34
|
+
'unicorn/no-await-expression-member': 'off',
|
|
35
|
+
'unicorn/no-hex-escape': 'off',
|
|
36
|
+
'unicorn/no-nested-ternary': 'off',
|
|
37
|
+
'unicorn/no-static-only-class': 'off',
|
|
38
|
+
'unicorn/no-unused-properties': 'warn',
|
|
39
|
+
'unicorn/numeric-separators-style': 'off',
|
|
40
|
+
'unicorn/prefer-array-some': 'off',
|
|
41
|
+
'unicorn/prefer-set-has': 'off',
|
|
42
|
+
'unicorn/prefer-spread': 'off',
|
|
43
|
+
'unicorn/prefer-string-replace-all': 'error',
|
|
19
44
|
'padded-blocks': 'off',
|
|
20
45
|
'prefer-rest-params': 'off',
|
|
21
46
|
'prefer-spread': 'off',
|
|
@@ -49,7 +74,11 @@ export default [
|
|
|
49
74
|
},
|
|
50
75
|
{
|
|
51
76
|
...sfmcSsjs.configs.recommended,
|
|
52
|
-
files: ['**/*.ssjs']
|
|
77
|
+
files: ['**/*.ssjs'],
|
|
78
|
+
rules: {
|
|
79
|
+
'unicorn/text-encoding-identifier-case': 'off',
|
|
80
|
+
'unicorn/prefer-string-replace-all': 'off'
|
|
81
|
+
}
|
|
53
82
|
},
|
|
54
83
|
{
|
|
55
84
|
files: ['**/*.js'],
|
|
@@ -106,32 +135,6 @@ export default [
|
|
|
106
135
|
|
|
107
136
|
rules: {
|
|
108
137
|
'logical-assignment-operators': ['error', 'always'],
|
|
109
|
-
'unicorn/better-regex': 'off',
|
|
110
|
-
|
|
111
|
-
'unicorn/catch-error-name': [
|
|
112
|
-
'error',
|
|
113
|
-
{
|
|
114
|
-
name: 'ex'
|
|
115
|
-
}
|
|
116
|
-
],
|
|
117
|
-
|
|
118
|
-
'unicorn/explicit-length-check': 'off',
|
|
119
|
-
'unicorn/no-null': 'off',
|
|
120
|
-
'unicorn/prefer-module': 'off',
|
|
121
|
-
'unicorn/prevent-abbreviations': 'off',
|
|
122
|
-
'unicorn/filename-case': 'off',
|
|
123
|
-
'unicorn/no-array-callback-reference': 'off',
|
|
124
|
-
'unicorn/no-array-reduce': 'off',
|
|
125
|
-
'unicorn/no-await-expression-member': 'off',
|
|
126
|
-
'unicorn/no-hex-escape': 'off',
|
|
127
|
-
'unicorn/no-nested-ternary': 'off',
|
|
128
|
-
'unicorn/no-static-only-class': 'off',
|
|
129
|
-
'unicorn/no-unused-properties': 'warn',
|
|
130
|
-
'unicorn/numeric-separators-style': 'off',
|
|
131
|
-
'unicorn/prefer-array-some': 'off',
|
|
132
|
-
'unicorn/prefer-set-has': 'off',
|
|
133
|
-
'unicorn/prefer-spread': 'off',
|
|
134
|
-
'unicorn/prefer-string-replace-all': 'error',
|
|
135
138
|
'arrow-body-style': ['error', 'as-needed'],
|
|
136
139
|
curly: 'error',
|
|
137
140
|
|
package/lib/index.js
CHANGED
|
@@ -1030,7 +1030,7 @@ class Mcdev {
|
|
|
1030
1030
|
);
|
|
1031
1031
|
|
|
1032
1032
|
// try re-retrieve without passing selectedTypes to ensure we find all dependencies
|
|
1033
|
-
await this._reRetrieve(businessUnit, true);
|
|
1033
|
+
await this._reRetrieve(businessUnit, true, null, typeKeyList);
|
|
1034
1034
|
|
|
1035
1035
|
Util.logger.info(
|
|
1036
1036
|
'Searching for selected items and their dependencies in your project folder'
|
|
@@ -1313,11 +1313,17 @@ class Mcdev {
|
|
|
1313
1313
|
* @param {string} businessUnit references credentials from properties.json
|
|
1314
1314
|
* @param {boolean} [alwaysAsk] by default this code only runs if --retrieve is set; this flag allows to always ask
|
|
1315
1315
|
* @param {TypeKeyCombo} [selectedTypes] limit retrieval to given metadata type
|
|
1316
|
+
* @param {TypeKeyCombo} [defaultPlusTheseTypes] if we run build for a non-standard type we need to tell it what to download on top
|
|
1316
1317
|
* @returns {Promise.<void>} -
|
|
1317
1318
|
*/
|
|
1318
|
-
static async _reRetrieve(
|
|
1319
|
+
static async _reRetrieve(
|
|
1320
|
+
businessUnit,
|
|
1321
|
+
alwaysAsk = false,
|
|
1322
|
+
selectedTypes,
|
|
1323
|
+
defaultPlusTheseTypes
|
|
1324
|
+
) {
|
|
1319
1325
|
let runRetrieveNow;
|
|
1320
|
-
if (!Util.OPTIONS.skipInteraction &&
|
|
1326
|
+
if (!Util.OPTIONS.skipInteraction && Util.OPTIONS.retrieve === undefined && alwaysAsk) {
|
|
1321
1327
|
runRetrieveNow = await confirm({
|
|
1322
1328
|
message: `Do you want to re-retrieve ${selectedTypes ? Util.convertTypeKeyToString(selectedTypes) : 'all metadata'} for ${businessUnit} now?`,
|
|
1323
1329
|
default: false,
|
|
@@ -1327,8 +1333,25 @@ class Mcdev {
|
|
|
1327
1333
|
Util.logger.info(
|
|
1328
1334
|
`Re-retrieving ${businessUnit}: ${selectedTypes ? Util.convertTypeKeyToString(selectedTypes) : 'all metadata'}`
|
|
1329
1335
|
);
|
|
1330
|
-
|
|
1331
|
-
|
|
1336
|
+
/** @type {TypeKeyCombo | string[]} */
|
|
1337
|
+
let retrieveTypes = selectedTypes ? structuredClone(selectedTypes) : null;
|
|
1338
|
+
if (defaultPlusTheseTypes) {
|
|
1339
|
+
if (selectedTypes) {
|
|
1340
|
+
// we need to work with a clone here because retrieve() modifies the object passed in as 2nd parameter
|
|
1341
|
+
retrieveTypes = Object.assign(
|
|
1342
|
+
retrieveTypes,
|
|
1343
|
+
structuredClone(defaultPlusTheseTypes)
|
|
1344
|
+
);
|
|
1345
|
+
} else {
|
|
1346
|
+
const properties = await config.getProperties();
|
|
1347
|
+
retrieveTypes = [
|
|
1348
|
+
...new Set(
|
|
1349
|
+
properties.metaDataTypes.retrieve.map((type) => type.split('-')[0])
|
|
1350
|
+
),
|
|
1351
|
+
];
|
|
1352
|
+
retrieveTypes.push(...Object.keys(defaultPlusTheseTypes));
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1332
1355
|
await this.retrieve(businessUnit, retrieveTypes);
|
|
1333
1356
|
}
|
|
1334
1357
|
}
|
|
@@ -987,7 +987,6 @@ class Automation extends MetadataType {
|
|
|
987
987
|
delete metadata.schedule;
|
|
988
988
|
delete metadata.type;
|
|
989
989
|
let i = 0;
|
|
990
|
-
const buName = this.buObject.credential + '/' + this.buObject.businessUnit;
|
|
991
990
|
if (metadata.steps) {
|
|
992
991
|
for (const step of metadata.steps) {
|
|
993
992
|
let displayOrder = 0;
|
|
@@ -997,23 +996,6 @@ class Automation extends MetadataType {
|
|
|
997
996
|
activity.r__key &&
|
|
998
997
|
this.definition.dependencies.includes(activity.r__type)
|
|
999
998
|
) {
|
|
1000
|
-
if (
|
|
1001
|
-
activity.r__type === 'verification' &&
|
|
1002
|
-
this.createdKeyMap?.[buName]?.verification?.[activity.r__key]
|
|
1003
|
-
) {
|
|
1004
|
-
Util.logger.debug(
|
|
1005
|
-
Util.getGrayMsg(
|
|
1006
|
-
` - updated verification activity name from ${
|
|
1007
|
-
activity.r__key
|
|
1008
|
-
} to ${
|
|
1009
|
-
this.createdKeyMap[buName].verification[activity.r__key]
|
|
1010
|
-
}`
|
|
1011
|
-
)
|
|
1012
|
-
);
|
|
1013
|
-
// map structure: cred/bu --> type --> old key --> new key
|
|
1014
|
-
activity.r__key =
|
|
1015
|
-
this.createdKeyMap[buName].verification[activity.r__key];
|
|
1016
|
-
}
|
|
1017
999
|
// automations can have empty placeholder for activities with only their type defined
|
|
1018
1000
|
activity.activityObjectId = cache.searchForField(
|
|
1019
1001
|
activity.r__type,
|
|
@@ -54,7 +54,60 @@ class DomainVerification extends MetadataType {
|
|
|
54
54
|
* @returns {Promise} Promise
|
|
55
55
|
*/
|
|
56
56
|
static create(metadataItem) {
|
|
57
|
-
return
|
|
57
|
+
return metadataItem.domain === 'bulk' &&
|
|
58
|
+
(Array.isArray(metadataItem.addresses) ||
|
|
59
|
+
(metadataItem.deTable && metadataItem.deColumn))
|
|
60
|
+
? this.createRESTBulk(metadataItem, '/messaging/v1/domainverification/') // bulk/insert
|
|
61
|
+
: super.createREST(metadataItem, '/messaging/v1/domainverification/');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Creates a multiple metadata entries via REST
|
|
66
|
+
*
|
|
67
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
68
|
+
* @param {string} uri rest endpoint for POST
|
|
69
|
+
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
70
|
+
*/
|
|
71
|
+
static async createRESTBulk(metadataEntry, uri) {
|
|
72
|
+
if (metadataEntry.deTable || metadataEntry.deColumn) {
|
|
73
|
+
Util.logger.error(
|
|
74
|
+
'It seems the deTable/deColumn approach does not work. Supply emails via addresses-array instead'
|
|
75
|
+
);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const createResults = {};
|
|
79
|
+
for (const address of metadataEntry.addresses) {
|
|
80
|
+
if (cache.getByKey(this.definition.type, address)) {
|
|
81
|
+
Util.logger.warn(` - ${this.definition.type} ${address} already exists`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const item = { domain: address };
|
|
85
|
+
if (await this.createREST(item, uri)) {
|
|
86
|
+
createResults[address] = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (Object.keys(createResults).length) {
|
|
90
|
+
// trigger re-retrieve to get all fields
|
|
91
|
+
await this.postDeployTasks(createResults);
|
|
92
|
+
await this.saveResults(
|
|
93
|
+
createResults,
|
|
94
|
+
[
|
|
95
|
+
this.properties.directories.retrieve,
|
|
96
|
+
this.buObject.credential,
|
|
97
|
+
this.buObject.businessUnit,
|
|
98
|
+
].join('/'),
|
|
99
|
+
null
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
Util.logger.info(
|
|
103
|
+
`${this.definition.type} bulk load: ${Object.keys(createResults).length} of ${metadataEntry.addresses.length} created`
|
|
104
|
+
);
|
|
105
|
+
if (Object.keys(createResults).length) {
|
|
106
|
+
Util.logger.warn('Please ignore the next message about filtering 1 item');
|
|
107
|
+
return `bulk successfully added.`;
|
|
108
|
+
} else {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
58
111
|
}
|
|
59
112
|
|
|
60
113
|
/**
|
|
@@ -121,9 +174,13 @@ class DomainVerification extends MetadataType {
|
|
|
121
174
|
* manages post retrieve steps
|
|
122
175
|
*
|
|
123
176
|
* @param {DomainVerificationItem} metadataItem a single item
|
|
124
|
-
* @returns {DomainVerificationItem} metadata
|
|
177
|
+
* @returns {DomainVerificationItem|void} metadata
|
|
125
178
|
*/
|
|
126
179
|
static postRetrieveTasks(metadataItem) {
|
|
180
|
+
if (typeof metadataItem === 'string' && metadataItem === 'bulk successfully added.') {
|
|
181
|
+
// this is a hack for bulk-creation only
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
127
184
|
if (metadataItem.status !== 'Verified') {
|
|
128
185
|
Util.logger.warn(
|
|
129
186
|
Util.getMsgPrefix(this.definition, metadataItem) +
|
|
@@ -146,6 +203,9 @@ class DomainVerification extends MetadataType {
|
|
|
146
203
|
*/
|
|
147
204
|
static async postDeployTasks(upsertResults) {
|
|
148
205
|
// re-retrieve all upserted items to ensure we have all fields (createdDate and modifiedDate are otherwise not present)
|
|
206
|
+
if (!Object.keys(upsertResults).length) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
149
209
|
Util.logger.debug(
|
|
150
210
|
`Caching all ${this.definition.type} post-deploy to ensure we have all fields`
|
|
151
211
|
);
|
|
@@ -198,9 +198,10 @@ class MetadataType {
|
|
|
198
198
|
*
|
|
199
199
|
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
200
200
|
* @param {object} apiResponse varies depending on the API call
|
|
201
|
+
* @param {MetadataTypeItem} metadataEntryWithAllFields like metadataEntry but before non-creatable fields were stripped
|
|
201
202
|
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
202
203
|
*/
|
|
203
|
-
static postUpdateTasks(metadataEntry, apiResponse) {
|
|
204
|
+
static postUpdateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) {
|
|
204
205
|
return apiResponse;
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -1262,13 +1263,14 @@ class MetadataType {
|
|
|
1262
1263
|
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
1263
1264
|
*/
|
|
1264
1265
|
static async updateREST(metadataEntry, uri, httpMethod = 'patch') {
|
|
1266
|
+
const metadataClone = structuredClone(metadataEntry);
|
|
1265
1267
|
this.removeNotUpdateableFields(metadataEntry);
|
|
1266
1268
|
try {
|
|
1267
1269
|
// set to empty object in case API returned nothing to be able to update it in helper classes
|
|
1268
1270
|
let response = (await this.client.rest[httpMethod](uri, metadataEntry)) || {};
|
|
1269
1271
|
await this._postChangeKeyTasks(metadataEntry);
|
|
1270
1272
|
this.getErrorsREST(response);
|
|
1271
|
-
response = await this.postUpdateTasks(metadataEntry, response);
|
|
1273
|
+
response = await this.postUpdateTasks(metadataEntry, response, metadataClone);
|
|
1272
1274
|
// some times, e.g. automation dont return a key in their update response and hence we need to fall back to name
|
|
1273
1275
|
Util.logger.info(` - updated ${Util.getTypeKeyName(this.definition, metadataEntry)}`);
|
|
1274
1276
|
return response;
|
|
@@ -1327,6 +1329,7 @@ class MetadataType {
|
|
|
1327
1329
|
*/
|
|
1328
1330
|
static async updateSOAP(metadataEntry, handleOutside) {
|
|
1329
1331
|
const soapType = this.definition.soapType || this.definition.type;
|
|
1332
|
+
const metadataClone = structuredClone(metadataEntry);
|
|
1330
1333
|
this.removeNotUpdateableFields(metadataEntry);
|
|
1331
1334
|
try {
|
|
1332
1335
|
let response = await this.client.soap.update(
|
|
@@ -1340,7 +1343,7 @@ class MetadataType {
|
|
|
1340
1343
|
);
|
|
1341
1344
|
}
|
|
1342
1345
|
await this._postChangeKeyTasks(metadataEntry);
|
|
1343
|
-
response = await this.postUpdateTasks(metadataEntry, response);
|
|
1346
|
+
response = await this.postUpdateTasks(metadataEntry, response, metadataClone);
|
|
1344
1347
|
|
|
1345
1348
|
return response;
|
|
1346
1349
|
} catch (ex) {
|
|
@@ -1515,6 +1518,7 @@ class MetadataType {
|
|
|
1515
1518
|
: [];
|
|
1516
1519
|
const results = {};
|
|
1517
1520
|
for (const item of metadataArr) {
|
|
1521
|
+
this.createCustomKeyField(item);
|
|
1518
1522
|
const key = item[this.definition.keyField];
|
|
1519
1523
|
results[key] = item;
|
|
1520
1524
|
}
|
|
@@ -1617,6 +1621,13 @@ class MetadataType {
|
|
|
1617
1621
|
}
|
|
1618
1622
|
}
|
|
1619
1623
|
|
|
1624
|
+
/**
|
|
1625
|
+
* helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
1626
|
+
*
|
|
1627
|
+
* @param {MetadataTypeItem} metadata single item
|
|
1628
|
+
*/
|
|
1629
|
+
static createCustomKeyField(metadata) {}
|
|
1630
|
+
|
|
1620
1631
|
/**
|
|
1621
1632
|
* Builds map of metadata entries mapped to their keyfields
|
|
1622
1633
|
*
|
|
@@ -1632,18 +1643,21 @@ class MetadataType {
|
|
|
1632
1643
|
if (Array.isArray(body)) {
|
|
1633
1644
|
// in some cases data is just an array
|
|
1634
1645
|
for (const item of body) {
|
|
1646
|
+
this.createCustomKeyField(item);
|
|
1635
1647
|
const key = item[keyField];
|
|
1636
1648
|
metadataStructure[key] = item;
|
|
1637
1649
|
}
|
|
1638
1650
|
} else if (body[bodyIteratorField]) {
|
|
1639
1651
|
for (const item of body[bodyIteratorField]) {
|
|
1652
|
+
this.createCustomKeyField(item);
|
|
1640
1653
|
const key = item[keyField];
|
|
1641
1654
|
metadataStructure[key] = item;
|
|
1642
1655
|
}
|
|
1643
1656
|
} else if (singleRetrieve) {
|
|
1644
1657
|
// some types will return a single item intead of an array if the key is supported by their api
|
|
1658
|
+
this.createCustomKeyField(body);
|
|
1645
1659
|
// ! currently, the id: prefix is only supported by journey (interaction)
|
|
1646
|
-
if (
|
|
1660
|
+
if (singleRetrieve.startsWith('id:')) {
|
|
1647
1661
|
singleRetrieve = body[keyField];
|
|
1648
1662
|
}
|
|
1649
1663
|
metadataStructure[singleRetrieve] = body;
|
|
@@ -60,61 +60,11 @@ class MobileKeyword extends MetadataType {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* @param {object} body json of response body
|
|
66
|
-
* @param {string} [singleRetrieve] key of single item to filter by
|
|
67
|
-
* @returns {MetadataTypeMap} keyField => metadata map
|
|
68
|
-
*/
|
|
69
|
-
static parseResponseBody(body, singleRetrieve) {
|
|
70
|
-
const bodyIteratorField = this.definition.bodyIteratorField;
|
|
71
|
-
const keyField = this.definition.keyField;
|
|
72
|
-
const metadataStructure = {};
|
|
73
|
-
if (body !== null) {
|
|
74
|
-
if (Array.isArray(body)) {
|
|
75
|
-
// in some cases data is just an array
|
|
76
|
-
for (const item of body) {
|
|
77
|
-
this.#createCustomKeyField(item);
|
|
78
|
-
const key = item[keyField];
|
|
79
|
-
metadataStructure[key] = item;
|
|
80
|
-
}
|
|
81
|
-
} else if (body[bodyIteratorField]) {
|
|
82
|
-
for (const item of body[bodyIteratorField]) {
|
|
83
|
-
this.#createCustomKeyField(item);
|
|
84
|
-
const key = item[keyField];
|
|
85
|
-
metadataStructure[key] = item;
|
|
86
|
-
}
|
|
87
|
-
} else if (singleRetrieve) {
|
|
88
|
-
// some types will return a single item intead of an array if the key is supported by their api
|
|
89
|
-
this.#createCustomKeyField(body);
|
|
90
|
-
// ! currently, the id: prefix is only supported by journey (interaction)
|
|
91
|
-
if (singleRetrieve.startsWith('id:')) {
|
|
92
|
-
singleRetrieve = body[keyField];
|
|
93
|
-
}
|
|
94
|
-
metadataStructure[singleRetrieve] = body;
|
|
95
|
-
return metadataStructure;
|
|
96
|
-
}
|
|
97
|
-
if (
|
|
98
|
-
metadataStructure[singleRetrieve] &&
|
|
99
|
-
(typeof singleRetrieve === 'string' || typeof singleRetrieve === 'number')
|
|
100
|
-
) {
|
|
101
|
-
// in case we really just wanted one entry but couldnt do so in the api call, filter it here
|
|
102
|
-
const single = {};
|
|
103
|
-
single[singleRetrieve] = metadataStructure[singleRetrieve];
|
|
104
|
-
return single;
|
|
105
|
-
} else if (singleRetrieve) {
|
|
106
|
-
return {};
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return metadataStructure;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* helper for {@link MobileKeyword.parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
63
|
+
* helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
114
64
|
*
|
|
115
65
|
* @param {MetadataTypeItem} metadata single item
|
|
116
66
|
*/
|
|
117
|
-
static
|
|
67
|
+
static createCustomKeyField(metadata) {
|
|
118
68
|
metadata.c__codeKeyword = metadata.code.code + '.' + metadata.keyword;
|
|
119
69
|
}
|
|
120
70
|
|
|
@@ -29,6 +29,7 @@ import cache from '../util/cache.js';
|
|
|
29
29
|
* @augments MetadataType
|
|
30
30
|
*/
|
|
31
31
|
class Verification extends MetadataType {
|
|
32
|
+
static verificationIdKeyMap;
|
|
32
33
|
/**
|
|
33
34
|
* Retrieves Metadata of Data Verification Activity.
|
|
34
35
|
*
|
|
@@ -39,51 +40,63 @@ class Verification extends MetadataType {
|
|
|
39
40
|
* @returns {Promise.<MetadataTypeMapObj>} Promise of metadata
|
|
40
41
|
*/
|
|
41
42
|
static async retrieve(retrieveDir, _, __, key) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
const paramArr = [];
|
|
44
|
+
let automationKey;
|
|
45
|
+
if (key) {
|
|
46
|
+
const regex = /^(.*?)__s\d{1,3}\.\d{1,3}$/;
|
|
47
|
+
const match = key.match(regex);
|
|
48
|
+
if (match) {
|
|
49
|
+
// automation key found
|
|
50
|
+
automationKey = match[1];
|
|
51
|
+
} else {
|
|
52
|
+
// invalid key, unset it
|
|
53
|
+
Util.logger.error(`Invalid key: ${key}`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
47
56
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
57
|
+
const results = {};
|
|
58
|
+
// there is no API endpoint to retrieve all dataVerification items, so we need to retrieve all automations and iterate over their activities
|
|
59
|
+
Util.logger.info(` - Caching dependent Metadata: automation`);
|
|
60
|
+
Automation.client = this.client;
|
|
61
|
+
Automation.buObject = this.buObject;
|
|
62
|
+
Automation.properties = this.properties;
|
|
63
|
+
Automation._skipNotificationRetrieve = true;
|
|
64
|
+
delete Automation._cachedMetadataMap;
|
|
65
|
+
const automationsMapObj = automationKey
|
|
66
|
+
? await Automation.retrieve(undefined, undefined, undefined, automationKey)
|
|
67
|
+
: await Automation.retrieve();
|
|
68
|
+
delete Automation._skipNotificationRetrieve;
|
|
69
|
+
if (automationsMapObj?.metadata && Object.keys(automationsMapObj?.metadata).length) {
|
|
70
|
+
if (!key) {
|
|
71
|
+
// if we are not retrieving a single item, cache the automations for later use during retrieval of automations
|
|
72
|
+
Automation._cachedMetadataMap = automationsMapObj?.metadata;
|
|
73
|
+
}
|
|
74
|
+
// automations found, lets iterate over their activities to find the dataVerification items
|
|
75
|
+
this.verificationIdKeyMap = {};
|
|
76
|
+
for (const automation of Object.values(automationsMapObj.metadata)) {
|
|
77
|
+
if (automation.steps) {
|
|
78
|
+
for (const step of automation.steps) {
|
|
79
|
+
// ideally one would use activity.displayOrder here but that doesnt always start at 1 nor is it always sequential. To avoid cross-BU issues, we use a custom order
|
|
80
|
+
let order = 1;
|
|
81
|
+
for (const activity of step.activities) {
|
|
82
|
+
if (
|
|
83
|
+
activity.objectTypeId === 1000 &&
|
|
84
|
+
activity.activityObjectId &&
|
|
85
|
+
activity.activityObjectId !== '00000000-0000-0000-0000-000000000000'
|
|
86
|
+
) {
|
|
87
|
+
// log the verification id
|
|
88
|
+
this.verificationIdKeyMap[activity.activityObjectId] =
|
|
89
|
+
`${automation.key}__s${step.step}.${order}`;
|
|
77
90
|
}
|
|
91
|
+
order++;
|
|
78
92
|
}
|
|
79
93
|
}
|
|
80
94
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
}
|
|
96
|
+
if (Object.keys(this.verificationIdKeyMap).length) {
|
|
97
|
+
paramArr.push(...Object.keys(this.verificationIdKeyMap));
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
|
-
const results = {};
|
|
87
100
|
if (paramArr.length) {
|
|
88
101
|
const response = await this.retrieveRESTcollection(
|
|
89
102
|
paramArr.map((id) => ({ id, uri: '/automation/v1/dataverifications/' + id })),
|
|
@@ -131,7 +144,7 @@ class Verification extends MetadataType {
|
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
/**
|
|
134
|
-
* Retrieves Metadata of
|
|
147
|
+
* Retrieves Metadata of item for caching
|
|
135
148
|
*
|
|
136
149
|
* @returns {Promise.<MetadataTypeMapObj>} Promise of metadata
|
|
137
150
|
*/
|
|
@@ -140,9 +153,9 @@ class Verification extends MetadataType {
|
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
/**
|
|
143
|
-
* Creates a single
|
|
156
|
+
* Creates a single item
|
|
144
157
|
*
|
|
145
|
-
* @param {VerificationItem} metadata a single
|
|
158
|
+
* @param {VerificationItem} metadata a single item
|
|
146
159
|
* @returns {Promise} Promise
|
|
147
160
|
*/
|
|
148
161
|
static create(metadata) {
|
|
@@ -161,31 +174,41 @@ class Verification extends MetadataType {
|
|
|
161
174
|
if (!apiResponse?.[this.definition.idField]) {
|
|
162
175
|
return;
|
|
163
176
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
// update apiResponse to ensure the new metadata is saved correctly on disk
|
|
178
|
+
apiResponse[this.definition.keyField] =
|
|
179
|
+
metadataEntryWithAllFields?.[this.definition.keyField];
|
|
180
|
+
|
|
181
|
+
// update info on metadataEntry to allow for proper logs
|
|
182
|
+
metadataEntry[this.definition.keyField] =
|
|
183
|
+
metadataEntryWithAllFields?.[this.definition.keyField];
|
|
171
184
|
metadataEntry[this.definition.idField] = apiResponse?.[this.definition.idField];
|
|
172
185
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
return apiResponse;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP}
|
|
190
|
+
*
|
|
191
|
+
* @param {MetadataTypeItem} metadataEntry a single metadata Entry
|
|
192
|
+
* @param {object} apiResponse varies depending on the API call
|
|
193
|
+
* @param {MetadataTypeItem} metadataEntryWithAllFields like metadataEntry but before non-creatable fields were stripped
|
|
194
|
+
* @returns {Promise.<object>} apiResponse, potentially modified
|
|
195
|
+
*/
|
|
196
|
+
static postUpdateTasks(metadataEntry, apiResponse, metadataEntryWithAllFields) {
|
|
197
|
+
// update apiResponse to ensure the new metadata is saved correctly on disk
|
|
198
|
+
apiResponse[this.definition.keyField] =
|
|
199
|
+
metadataEntryWithAllFields?.[this.definition.keyField];
|
|
181
200
|
|
|
201
|
+
// update info on metadataEntry to allow for proper logs
|
|
202
|
+
metadataEntry[this.definition.keyField] =
|
|
203
|
+
metadataEntryWithAllFields?.[this.definition.keyField];
|
|
204
|
+
metadataEntry[this.definition.idField] = apiResponse?.[this.definition.idField];
|
|
182
205
|
return apiResponse;
|
|
183
206
|
}
|
|
184
207
|
|
|
185
208
|
/**
|
|
186
|
-
* Updates a single
|
|
209
|
+
* Updates a single item
|
|
187
210
|
*
|
|
188
|
-
* @param {VerificationItem} metadata a single
|
|
211
|
+
* @param {VerificationItem} metadata a single item
|
|
189
212
|
* @returns {Promise} Promise
|
|
190
213
|
*/
|
|
191
214
|
static update(metadata) {
|
|
@@ -209,9 +232,22 @@ class Verification extends MetadataType {
|
|
|
209
232
|
'ObjectID'
|
|
210
233
|
);
|
|
211
234
|
delete metadata.r__dataExtension_key;
|
|
235
|
+
|
|
212
236
|
return metadata;
|
|
213
237
|
}
|
|
214
238
|
|
|
239
|
+
/**
|
|
240
|
+
* helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
241
|
+
*
|
|
242
|
+
* @param {MetadataTypeItem} metadata single item
|
|
243
|
+
*/
|
|
244
|
+
static createCustomKeyField(metadata) {
|
|
245
|
+
if (this.verificationIdKeyMap[metadata[this.definition.idField]]) {
|
|
246
|
+
metadata[this.definition.keyField] =
|
|
247
|
+
this.verificationIdKeyMap[metadata[this.definition.idField]];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
215
251
|
/**
|
|
216
252
|
* parses retrieved Metadata before saving
|
|
217
253
|
*
|
|
@@ -39,9 +39,9 @@ export default {
|
|
|
39
39
|
maxKeyLength: 36, // confirmed max length
|
|
40
40
|
type: 'triggeredSend',
|
|
41
41
|
soapType: 'triggeredSendDefinition',
|
|
42
|
-
typeDescription: '
|
|
43
|
-
typeRetrieveByDefault:
|
|
44
|
-
typeCdpByDefault:
|
|
42
|
+
typeDescription: 'Used by Journey Builder to send triggered emails',
|
|
43
|
+
typeRetrieveByDefault: false,
|
|
44
|
+
typeCdpByDefault: false,
|
|
45
45
|
typeName: 'Triggered Send',
|
|
46
46
|
priorityMapping: {
|
|
47
47
|
High: 3,
|