mcdev 5.0.1 → 5.1.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/.coverage-comment-template.svelte +177 -161
- package/.eslintrc.json +1 -0
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.github/dependabot.yml +8 -0
- package/.github/workflows/coverage-base-update.yml +6 -2
- package/.github/workflows/coverage-develop-branch.yml +7 -6
- package/.github/workflows/coverage-main-branch.yml +7 -6
- package/.github/workflows/coverage.yml +7 -2
- package/.husky/commit-msg +1 -1
- package/.husky/post-checkout +35 -3
- package/.husky/post-merge +21 -0
- package/.husky/pre-commit +1 -0
- package/docs/dist/documentation.md +222 -62
- package/lib/Deployer.js +3 -4
- package/lib/cli.js +36 -8
- package/lib/index.js +175 -3
- package/lib/metadataTypes/Asset.js +4 -2
- package/lib/metadataTypes/Automation.js +413 -195
- package/lib/metadataTypes/DataExtension.js +6 -8
- package/lib/metadataTypes/DataExtensionField.js +5 -5
- package/lib/metadataTypes/Journey.js +11 -10
- package/lib/metadataTypes/MetadataType.js +76 -22
- package/lib/metadataTypes/MobileKeyword.js +165 -20
- package/lib/metadataTypes/MobileMessage.js +20 -28
- package/lib/metadataTypes/Query.js +26 -0
- package/lib/metadataTypes/Role.js +2 -3
- package/lib/metadataTypes/TransactionalSMS.js +5 -5
- package/lib/metadataTypes/User.js +3 -17
- package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +52 -6
- package/lib/metadataTypes/definitions/DataExtension.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +1 -0
- package/lib/metadataTypes/definitions/EmailSend.definition.js +1 -0
- package/lib/metadataTypes/definitions/Event.definition.js +1 -0
- package/lib/metadataTypes/definitions/Filter.definition.js +1 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +1 -0
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +20 -7
- package/lib/metadataTypes/definitions/MobileMessage.definition.js +50 -8
- package/lib/metadataTypes/definitions/Query.definition.js +1 -0
- package/lib/metadataTypes/definitions/Role.definition.js +1 -0
- package/lib/metadataTypes/definitions/TriggeredSend.definition.js +1 -0
- package/lib/metadataTypes/definitions/User.definition.js +2 -0
- package/lib/util/auth.js +4 -1
- package/lib/util/cli.js +1 -1
- package/lib/util/devops.js +13 -11
- package/lib/util/file.js +5 -3
- package/lib/util/util.js +153 -129
- package/package.json +11 -11
- package/test/general.test.js +26 -0
- package/test/mockRoot/.mcdevrc.json +3 -1
- package/test/mockRoot/deploy/testInstance/testBU/automation/testExisting_automation.automation-meta.json +53 -0
- package/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json +46 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.json} +1 -4
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword_blocked.mobileKeyword-meta.json → 4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.json} +1 -4
- package/test/mockRoot/deploy/testInstance/testBU/query/{testExistingQuery.query-meta.json → testExisting_query.query-meta.json} +2 -2
- package/test/mockRoot/deploy/testInstance/testBU/query/{testNewQuery.query-meta.json → testNew_query.query-meta.json} +2 -2
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testExisting_tsms.transactionalSMS-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/transactionalSMS/testNew_tsms.transactionalSMS-meta.json +1 -1
- package/test/resourceFactory.js +64 -21
- package/test/resources/1111111/user/retrieve-expected.md +19 -0
- package/test/resources/9999999/automation/build-expected.json +58 -0
- package/test/resources/9999999/automation/create-expected.json +46 -0
- package/test/resources/9999999/automation/create-testNew_automation-expected.md +28 -0
- package/test/resources/9999999/automation/delete-response.xml +40 -0
- package/test/resources/9999999/automation/retrieve-expected.json +58 -0
- package/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md +30 -0
- package/test/resources/9999999/automation/template-expected.json +58 -0
- package/test/resources/9999999/automation/update-expected.json +46 -0
- package/test/resources/9999999/automation/update-testExisting_automation-expected.md +28 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/patch-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/a8afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/post-response.json +85 -0
- package/test/resources/9999999/automation/v1/dataextracts/56c5370a-f988-4f36-b0ee-0f876573f6d7/get-response.json +38 -0
- package/test/resources/9999999/automation/v1/dataextracts/get-response.json +20 -0
- package/test/resources/9999999/automation/v1/filetransfers/72c328ac-f5b0-4e37-91d3-a775666f15a6/get-response.json +18 -0
- package/test/resources/9999999/automation/v1/filetransfers/get-response.json +15 -0
- package/test/resources/9999999/automation/v1/imports/get-response.json +38 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/actions/start/post-response.txt +1 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/get-response.json +2 -2
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +2 -2
- package/test/resources/9999999/automation/v1/queries/get-response.json +4 -4
- package/test/resources/9999999/automation/v1/queries/post-response.json +2 -2
- package/test/resources/9999999/automation/v1/scripts/get-response.json +17 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.md +18 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=automations-response.xml +48 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=queryactivity-response.xml +48 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +22 -0
- package/test/resources/9999999/emailSendDefinition/retrieve-response.xml +85 -0
- package/test/resources/9999999/journey/build-expected.json +1 -1
- package/test/resources/9999999/journey/get-expected.json +1 -1
- package/test/resources/9999999/journey/put-expected.json +1 -1
- package/test/resources/9999999/journey/template-expected.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/automations/notifications/RkpOcE9qSVh2VUdnYTVJbWFfWW14dzoyNTow/get-response.json +21 -0
- package/test/resources/9999999/legacy/v1/beta/automations/notifications/RkpOcE9qSVh2VUdnYTVJbWFfWW14dzoyNTow/post-response.json +0 -0
- package/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json +30 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json +1 -1
- package/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/post-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/sms/definitions/testExisting_tsms/patch-response.json +1 -1
- package/test/resources/9999999/mobileKeyword/build-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/get-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/post-create-expected.json +1 -4
- package/test/resources/9999999/mobileKeyword/template-expected.json +1 -4
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation-response.xml +30 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testNew_automation-response.xml +30 -0
- package/test/resources/9999999/program/retrieve-Name=testExisting_automation-response.xml +31 -0
- package/test/resources/9999999/program/retrieve-response.xml +32 -0
- package/test/resources/9999999/query/build-expected.json +2 -2
- package/test/resources/9999999/query/build-expected.sql +1 -1
- package/test/resources/9999999/query/get-expected.json +2 -2
- package/test/resources/9999999/query/get-expected.sql +1 -1
- package/test/resources/9999999/query/get2-expected.json +2 -2
- package/test/resources/9999999/query/patch-expected.json +2 -2
- package/test/resources/9999999/query/patch-expected.sql +1 -1
- package/test/resources/9999999/query/post-expected.json +2 -2
- package/test/resources/9999999/query/post-expected.sql +1 -1
- package/test/resources/9999999/query/template-expected.json +2 -2
- package/test/resources/9999999/query/template-expected.sql +1 -1
- package/test/resources/9999999/transactionalSMS/build-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/get-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/patch-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/post-expected.json +1 -1
- package/test/resources/9999999/transactionalSMS/template-expected.json +1 -1
- package/test/type.automation.test.js +259 -0
- package/test/type.dataExtension.test.js +16 -1
- package/test/type.mobileKeyword.test.js +57 -19
- package/test/type.query.test.js +39 -26
- package/test/type.user.test.js +44 -1
- package/test/utils.js +16 -5
- package/.coverage-comment-template.md +0 -20
- /package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword.mobileKeyword-meta.amp → 4912312345678.TESTNEW_KEYWORD.mobileKeyword-meta.amp} +0 -0
- /package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/{testNew_keyword_blocked.mobileKeyword-meta.amp → 4912312345678.TESTNEW_KEYWORD_BLOCKED.mobileKeyword-meta.amp} +0 -0
- /package/test/mockRoot/deploy/testInstance/testBU/query/{testExistingQuery.query-meta.sql → testExisting_query.query-meta.sql} +0 -0
- /package/test/mockRoot/deploy/testInstance/testBU/query/{testNewQuery.query-meta.sql → testNew_query.query-meta.sql} +0 -0
|
@@ -59,8 +59,8 @@ class DataExtension extends MetadataType {
|
|
|
59
59
|
// output error & remove from deploy list
|
|
60
60
|
Util.logger.error(
|
|
61
61
|
` ☇ skipping ${this.definition.type} ${
|
|
62
|
-
metadataMap[this.definition.keyField]
|
|
63
|
-
} / ${metadataMap[this.definition.nameField]}: ${ex.message}`
|
|
62
|
+
metadataMap[metadataKey][this.definition.keyField]
|
|
63
|
+
} / ${metadataMap[metadataKey][this.definition.nameField]}: ${ex.message}`
|
|
64
64
|
);
|
|
65
65
|
delete metadataMap[metadataKey];
|
|
66
66
|
// skip rest of handling for this DE
|
|
@@ -464,9 +464,7 @@ class DataExtension extends MetadataType {
|
|
|
464
464
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
465
465
|
Util.getKeysString(key)
|
|
466
466
|
);
|
|
467
|
-
|
|
468
|
-
await this.document(savedMetadata);
|
|
469
|
-
}
|
|
467
|
+
await this.runDocumentOnRetrieve(key, savedMetadata);
|
|
470
468
|
}
|
|
471
469
|
return { metadata: metadata, type: 'dataExtension' };
|
|
472
470
|
}
|
|
@@ -626,16 +624,16 @@ class DataExtension extends MetadataType {
|
|
|
626
624
|
* @returns {Promise.<TYPE.DataExtensionItem>} Promise of updated single DE
|
|
627
625
|
*/
|
|
628
626
|
static async preDeployTasks(metadata) {
|
|
629
|
-
if (metadata.Name
|
|
627
|
+
if (metadata.Name?.startsWith('_')) {
|
|
630
628
|
throw new Error(`Cannot Upsert Strongly Typed Data Extensions`);
|
|
631
629
|
}
|
|
632
630
|
if (
|
|
633
631
|
this.buObject.eid !== this.buObject.mid &&
|
|
634
|
-
metadata.r__folder_Path
|
|
632
|
+
metadata.r__folder_Path?.startsWith('Shared Items')
|
|
635
633
|
) {
|
|
636
634
|
throw new Error(`Cannot Create/Update a Shared Data Extension from the Child BU`);
|
|
637
635
|
}
|
|
638
|
-
if (metadata.r__folder_Path
|
|
636
|
+
if (metadata.r__folder_Path?.startsWith('Synchronized Data Extensions')) {
|
|
639
637
|
throw new Error(
|
|
640
638
|
`Cannot Create/Update a Synchronized Data Extension. Please use Contact Builder to maintain these`
|
|
641
639
|
);
|
|
@@ -170,11 +170,11 @@ class DataExtensionField extends MetadataType {
|
|
|
170
170
|
|
|
171
171
|
// enable renaming
|
|
172
172
|
if (item.Name_new) {
|
|
173
|
+
Util.logger.info(
|
|
174
|
+
` - Found Name_new='${item.Name_new}' for field ${deKey}.${item.Name} - trying to rename.`
|
|
175
|
+
);
|
|
173
176
|
item.Name = item.Name_new;
|
|
174
177
|
delete item.Name_new;
|
|
175
|
-
Util.logger.warn(
|
|
176
|
-
` - Found 'Name_new' value '${item.Name_new}' for ${deKey}.${item.Name} - trying to rename.`
|
|
177
|
-
);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// check if any changes were found
|
|
@@ -210,8 +210,8 @@ class DataExtensionField extends MetadataType {
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
if (item.Name_new) {
|
|
213
|
-
Util.logger.
|
|
214
|
-
` - Found
|
|
213
|
+
Util.logger.info(
|
|
214
|
+
` - Found Name_new='${item.Name_new}' for field ${deKey}.${item.Name} but could not find a corresponding DE field on the server - adding new field instead of updating.`
|
|
215
215
|
);
|
|
216
216
|
delete item.Name_new;
|
|
217
217
|
}
|
|
@@ -542,12 +542,13 @@ class Journey extends MetadataType {
|
|
|
542
542
|
try {
|
|
543
543
|
// mobileKeyword
|
|
544
544
|
if (activity.configurationArguments?.keywordId) {
|
|
545
|
-
activity.configurationArguments.
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
545
|
+
activity.configurationArguments.r__mobileKeyword_codeKeyword =
|
|
546
|
+
cache.searchForField(
|
|
547
|
+
'mobileKeyword',
|
|
548
|
+
activity.configurationArguments.keywordId,
|
|
549
|
+
'id',
|
|
550
|
+
'c__codeKeyword'
|
|
551
|
+
);
|
|
551
552
|
delete activity.configurationArguments.keywordId;
|
|
552
553
|
}
|
|
553
554
|
} catch (ex) {
|
|
@@ -833,14 +834,14 @@ class Journey extends MetadataType {
|
|
|
833
834
|
delete activity.configurationArguments.c__mobileMessage_id;
|
|
834
835
|
}
|
|
835
836
|
// mobileKeyword
|
|
836
|
-
if (activity.configurationArguments?.
|
|
837
|
+
if (activity.configurationArguments?.r__mobileKeyword_codeKeyword) {
|
|
837
838
|
activity.configurationArguments.keywordId = cache.searchForField(
|
|
838
839
|
'mobileKeyword',
|
|
839
|
-
activity.
|
|
840
|
-
'
|
|
840
|
+
activity.r__mobileKeyword_codeKeyword,
|
|
841
|
+
'c__codeKeyword',
|
|
841
842
|
'id'
|
|
842
843
|
);
|
|
843
|
-
delete activity.configurationArguments.
|
|
844
|
+
delete activity.configurationArguments.r__mobileKeyword_codeKeyword;
|
|
844
845
|
}
|
|
845
846
|
if (activity.configurationArguments?.c__next_mobileKeyword) {
|
|
846
847
|
activity.configurationArguments.nextKeywordId = cache.searchForField(
|
|
@@ -58,15 +58,9 @@ class MetadataType {
|
|
|
58
58
|
if (key === fileNameWithoutEnding || listBadKeys) {
|
|
59
59
|
fileName2FileContent[fileNameWithoutEnding] = fileContent;
|
|
60
60
|
} else {
|
|
61
|
-
Util.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
'getJsonFromFS',
|
|
65
|
-
'Name of the Metadata and the External Identifier must match',
|
|
66
|
-
JSON.stringify({
|
|
67
|
-
Filename: fileNameWithoutEnding,
|
|
68
|
-
ExternalIdentifier: key,
|
|
69
|
-
})
|
|
61
|
+
Util.logger.error(
|
|
62
|
+
` ${this.definition.type} ${key}: Name of the metadata file and the JSON-key (${this.definition.keyField}) must match` +
|
|
63
|
+
Util.getGrayMsg(` - ${dir}/${fileName}`)
|
|
70
64
|
);
|
|
71
65
|
}
|
|
72
66
|
}
|
|
@@ -460,6 +454,18 @@ class MetadataType {
|
|
|
460
454
|
return;
|
|
461
455
|
}
|
|
462
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Abstract execute method that needs to be implemented in child metadata type
|
|
459
|
+
*
|
|
460
|
+
* @returns {void}
|
|
461
|
+
*/
|
|
462
|
+
static execute() {
|
|
463
|
+
Util.logger.error(
|
|
464
|
+
` ☇ skipping ${this.definition.type}: execute is not supported yet for ${this.definition.type}`
|
|
465
|
+
);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
463
469
|
/**
|
|
464
470
|
* test if metadata was actually changed or not to potentially skip it during deployment
|
|
465
471
|
*
|
|
@@ -650,6 +656,7 @@ class MetadataType {
|
|
|
650
656
|
metadataMap[metadataKey][this.definition.keyField]
|
|
651
657
|
);
|
|
652
658
|
// Update if it already exists; Create it if not
|
|
659
|
+
const maxKeyLength = this.definition.maxKeyLength || 36;
|
|
653
660
|
if (
|
|
654
661
|
Util.logger.level === 'debug' &&
|
|
655
662
|
metadataMap[metadataKey][this.definition.idField] &&
|
|
@@ -703,11 +710,11 @@ class MetadataType {
|
|
|
703
710
|
// NOTE: trim twice while getting the newKey value to remove leading spaces before limiting the length
|
|
704
711
|
const newKey = (metadataMap[metadataKey][Util.OPTIONS.changeKeyField] + '')
|
|
705
712
|
.trim()
|
|
706
|
-
.slice(0,
|
|
713
|
+
.slice(0, maxKeyLength)
|
|
707
714
|
.trim();
|
|
708
|
-
if (metadataMap[metadataKey][Util.OPTIONS.changeKeyField] + '' >
|
|
715
|
+
if (metadataMap[metadataKey][Util.OPTIONS.changeKeyField] + '' > maxKeyLength) {
|
|
709
716
|
Util.logger.warn(
|
|
710
|
-
|
|
717
|
+
`${this.definition.type} ${this.definition.keyField} may not exceed ${maxKeyLength} characters. Truncated the value in field ${Util.OPTIONS.changeKeyField} to ${newKey}`
|
|
711
718
|
);
|
|
712
719
|
}
|
|
713
720
|
if (metadataKey == newKey) {
|
|
@@ -727,10 +734,10 @@ class MetadataType {
|
|
|
727
734
|
}
|
|
728
735
|
} else if (Util.OPTIONS.changeKeyValue) {
|
|
729
736
|
// NOTE: trim twice while getting the newKey value to remove leading spaces before limiting the length
|
|
730
|
-
const newKey = Util.OPTIONS.changeKeyValue.trim().slice(0,
|
|
731
|
-
if (Util.OPTIONS.changeKeyValue.trim().length >
|
|
737
|
+
const newKey = Util.OPTIONS.changeKeyValue.trim().slice(0, maxKeyLength).trim();
|
|
738
|
+
if (Util.OPTIONS.changeKeyValue.trim().length > maxKeyLength) {
|
|
732
739
|
Util.logger.warn(
|
|
733
|
-
|
|
740
|
+
`${this.definition.type} ${this.definition.keyField} may not exceed ${maxKeyLength} characters. Truncated your value to ${newKey}`
|
|
734
741
|
);
|
|
735
742
|
}
|
|
736
743
|
if (this.definition.keyField == this.definition.idField) {
|
|
@@ -1016,12 +1023,7 @@ class MetadataType {
|
|
|
1016
1023
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
1017
1024
|
Util.getKeysString(singleRetrieve)
|
|
1018
1025
|
);
|
|
1019
|
-
|
|
1020
|
-
this.buObject &&
|
|
1021
|
-
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
|
|
1022
|
-
) {
|
|
1023
|
-
await this.document(savedMetadata);
|
|
1024
|
-
}
|
|
1026
|
+
await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
|
|
1025
1027
|
}
|
|
1026
1028
|
return { metadata: metadata, type: this.definition.type };
|
|
1027
1029
|
}
|
|
@@ -1063,6 +1065,7 @@ class MetadataType {
|
|
|
1063
1065
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
1064
1066
|
Util.getKeysString(singleRetrieve)
|
|
1065
1067
|
);
|
|
1068
|
+
await this.runDocumentOnRetrieve(singleRetrieve, savedMetadata);
|
|
1066
1069
|
}
|
|
1067
1070
|
|
|
1068
1071
|
return {
|
|
@@ -1070,6 +1073,54 @@ class MetadataType {
|
|
|
1070
1073
|
type: this.definition.type,
|
|
1071
1074
|
};
|
|
1072
1075
|
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Used to execute a query/automation etc.
|
|
1078
|
+
*
|
|
1079
|
+
* @param {string} uri REST endpoint where the POST request should be sent
|
|
1080
|
+
* @param {string} key item key
|
|
1081
|
+
* @returns {Promise.<string>} 'OK' if started execution successfully, otherwise - 'Error'
|
|
1082
|
+
*/
|
|
1083
|
+
static async executeREST(uri, key) {
|
|
1084
|
+
try {
|
|
1085
|
+
const response = await this.client.rest.post(uri, {}); // payload is empty for this request
|
|
1086
|
+
if (response === 'OK') {
|
|
1087
|
+
Util.logger.info(`Executed ${this.definition.type}: ${key}`);
|
|
1088
|
+
} else {
|
|
1089
|
+
throw new Error(response);
|
|
1090
|
+
}
|
|
1091
|
+
return response;
|
|
1092
|
+
} catch (ex) {
|
|
1093
|
+
Util.logger.error(`Failed to execute ${this.definition.type} ${key}: ${ex.message}`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* helper for {@link retrieveREST} and {@link retrieveSOAP}
|
|
1099
|
+
*
|
|
1100
|
+
* @param {string|number} [singleRetrieve] key of single item to filter by
|
|
1101
|
+
* @param {TYPE.MetadataTypeMap} metadataMap saved metadata
|
|
1102
|
+
* @returns {Promise.<void>} -
|
|
1103
|
+
*/
|
|
1104
|
+
static async runDocumentOnRetrieve(singleRetrieve, metadataMap) {
|
|
1105
|
+
if (
|
|
1106
|
+
this.buObject &&
|
|
1107
|
+
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)
|
|
1108
|
+
) {
|
|
1109
|
+
if (!singleRetrieve || (singleRetrieve && !this.definition.documentInOneFile)) {
|
|
1110
|
+
const count = Object.keys(metadataMap).length;
|
|
1111
|
+
Util.logger.debug(
|
|
1112
|
+
` - Running document for ${count} record${count === 1 ? '' : 's'}`
|
|
1113
|
+
);
|
|
1114
|
+
await this.document(metadataMap);
|
|
1115
|
+
} else {
|
|
1116
|
+
Util.logger.info(
|
|
1117
|
+
Util.getGrayMsg(
|
|
1118
|
+
` - Skipped running document because you supplied keys and ${this.definition.type} is documented in a single file for all.`
|
|
1119
|
+
)
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1073
1124
|
|
|
1074
1125
|
/**
|
|
1075
1126
|
* Builds map of metadata entries mapped to their keyfields
|
|
@@ -1528,7 +1579,10 @@ class MetadataType {
|
|
|
1528
1579
|
// so its in retrieve but not in save. Here we put into the clone so that the original
|
|
1529
1580
|
// object used for caching doesnt have the Id removed.
|
|
1530
1581
|
const saveClone = JSON.parse(JSON.stringify(results[originalKey]));
|
|
1531
|
-
if (
|
|
1582
|
+
if (
|
|
1583
|
+
!this.definition.keepId &&
|
|
1584
|
+
this.definition.idField !== this.definition.keyField
|
|
1585
|
+
) {
|
|
1532
1586
|
delete saveClone[this.definition.idField];
|
|
1533
1587
|
}
|
|
1534
1588
|
|
|
@@ -24,14 +24,12 @@ class MobileKeyword extends MetadataType {
|
|
|
24
24
|
*/
|
|
25
25
|
static retrieve(retrieveDir, _, __, key) {
|
|
26
26
|
try {
|
|
27
|
+
let queryParams;
|
|
28
|
+
[key, queryParams] = this.#getRetrieveKeyAndUrl(key);
|
|
29
|
+
|
|
27
30
|
return super.retrieveREST(
|
|
28
31
|
retrieveDir,
|
|
29
|
-
'/legacy/v1/beta/mobile/keyword/' +
|
|
30
|
-
(key
|
|
31
|
-
? key.startsWith('id:')
|
|
32
|
-
? key.slice(3)
|
|
33
|
-
: `?view=simple&$where=keyword%20eq%20%27${key}%27%20`
|
|
34
|
-
: '?view=simple'),
|
|
32
|
+
'/legacy/v1/beta/mobile/keyword/' + queryParams,
|
|
35
33
|
null,
|
|
36
34
|
key
|
|
37
35
|
);
|
|
@@ -47,6 +45,109 @@ class MobileKeyword extends MetadataType {
|
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Builds map of metadata entries mapped to their keyfields
|
|
50
|
+
*
|
|
51
|
+
* @param {object} body json of response body
|
|
52
|
+
* @param {string|number} [singleRetrieve] key of single item to filter by
|
|
53
|
+
* @returns {TYPE.MetadataTypeMap} keyField => metadata map
|
|
54
|
+
*/
|
|
55
|
+
static parseResponseBody(body, singleRetrieve) {
|
|
56
|
+
const bodyIteratorField = this.definition.bodyIteratorField;
|
|
57
|
+
const keyField = this.definition.keyField;
|
|
58
|
+
const metadataStructure = {};
|
|
59
|
+
if (body !== null) {
|
|
60
|
+
if (Array.isArray(body)) {
|
|
61
|
+
// in some cases data is just an array
|
|
62
|
+
for (const item of body) {
|
|
63
|
+
this.#createCustomKeyField(item);
|
|
64
|
+
const key = item[keyField];
|
|
65
|
+
metadataStructure[key] = item;
|
|
66
|
+
}
|
|
67
|
+
} else if (body[bodyIteratorField]) {
|
|
68
|
+
for (const item of body[bodyIteratorField]) {
|
|
69
|
+
this.#createCustomKeyField(item);
|
|
70
|
+
const key = item[keyField];
|
|
71
|
+
metadataStructure[key] = item;
|
|
72
|
+
}
|
|
73
|
+
} else if (singleRetrieve) {
|
|
74
|
+
// some types will return a single item intead of an array if the key is supported by their api
|
|
75
|
+
this.#createCustomKeyField(body);
|
|
76
|
+
// ! currently, the id: prefix is only supported by journey (interaction)
|
|
77
|
+
if (singleRetrieve.startsWith('id:')) {
|
|
78
|
+
singleRetrieve = body[keyField];
|
|
79
|
+
}
|
|
80
|
+
metadataStructure[singleRetrieve] = body;
|
|
81
|
+
return metadataStructure;
|
|
82
|
+
}
|
|
83
|
+
if (
|
|
84
|
+
metadataStructure[singleRetrieve] &&
|
|
85
|
+
(typeof singleRetrieve === 'string' || typeof singleRetrieve === 'number')
|
|
86
|
+
) {
|
|
87
|
+
// in case we really just wanted one entry but couldnt do so in the api call, filter it here
|
|
88
|
+
const single = {};
|
|
89
|
+
single[singleRetrieve] = metadataStructure[singleRetrieve];
|
|
90
|
+
return single;
|
|
91
|
+
} else if (singleRetrieve) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return metadataStructure;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* helper for {@link parseResponseBody} that creates a custom key field for this type based on mobileCode and keyword
|
|
100
|
+
*
|
|
101
|
+
* @private
|
|
102
|
+
* @param {TYPE.MetadataType} metadata single item
|
|
103
|
+
*/
|
|
104
|
+
static #createCustomKeyField(metadata) {
|
|
105
|
+
metadata.c__codeKeyword = metadata.code.code + '.' + metadata.keyword;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* helper for {@link preDeployTasks} and {@link createOrUpdate} to ensure we have code & keyword properly set
|
|
110
|
+
*
|
|
111
|
+
* @private
|
|
112
|
+
* @param {TYPE.MetadataType} metadata single item
|
|
113
|
+
*/
|
|
114
|
+
static #setCodeAndKeyword(metadata) {
|
|
115
|
+
const [code, keyword] = metadata.c__codeKeyword.split('.');
|
|
116
|
+
|
|
117
|
+
// mobileCode
|
|
118
|
+
metadata.code = {
|
|
119
|
+
id: cache.searchForField('mobileCode', code, 'code', 'id'),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// keyword
|
|
123
|
+
metadata.keyword = keyword;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* helper for {@link MetadataType.upsert}
|
|
128
|
+
*
|
|
129
|
+
* @param {TYPE.MetadataTypeMap} metadataMap list of metadata
|
|
130
|
+
* @param {string} metadataKey key of item we are looking at
|
|
131
|
+
* @param {boolean} hasError error flag from previous code
|
|
132
|
+
* @param {TYPE.MetadataTypeItemDiff[]} metadataToUpdate list of items to update
|
|
133
|
+
* @param {TYPE.MetadataTypeItem[]} metadataToCreate list of items to create
|
|
134
|
+
* @returns {'create' | 'update' | 'skip'} action to take
|
|
135
|
+
*/
|
|
136
|
+
static createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate) {
|
|
137
|
+
const createOrUpdateAction = super.createOrUpdate(
|
|
138
|
+
metadataMap,
|
|
139
|
+
metadataKey,
|
|
140
|
+
hasError,
|
|
141
|
+
metadataToUpdate,
|
|
142
|
+
metadataToCreate
|
|
143
|
+
);
|
|
144
|
+
if (createOrUpdateAction === 'update') {
|
|
145
|
+
// in case --changeKeyField or --changeKeyValue was used, let's ensure we set code & keyword here again
|
|
146
|
+
this.#setCodeAndKeyword(metadataMap[metadataKey]);
|
|
147
|
+
}
|
|
148
|
+
return createOrUpdateAction;
|
|
149
|
+
}
|
|
150
|
+
|
|
50
151
|
/**
|
|
51
152
|
* Retrieves event definition metadata for caching
|
|
52
153
|
*
|
|
@@ -60,26 +161,29 @@ class MobileKeyword extends MetadataType {
|
|
|
60
161
|
}
|
|
61
162
|
|
|
62
163
|
/**
|
|
63
|
-
*
|
|
164
|
+
* retrieve an item and create a template from it
|
|
64
165
|
*
|
|
65
166
|
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
66
|
-
* @param {string}
|
|
167
|
+
* @param {string} key name of the metadata file
|
|
67
168
|
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
68
169
|
* @returns {Promise.<TYPE.MetadataTypeItemObj>} Promise of metadata
|
|
69
170
|
*/
|
|
70
|
-
static async retrieveAsTemplate(templateDir,
|
|
171
|
+
static async retrieveAsTemplate(templateDir, key, templateVariables) {
|
|
71
172
|
try {
|
|
173
|
+
let queryParams;
|
|
174
|
+
[key, queryParams] = this.#getRetrieveKeyAndUrl(key);
|
|
175
|
+
|
|
72
176
|
return super.retrieveTemplateREST(
|
|
73
177
|
templateDir,
|
|
74
|
-
`/legacy/v1/beta/mobile/keyword
|
|
178
|
+
`/legacy/v1/beta/mobile/keyword/` + queryParams,
|
|
75
179
|
templateVariables,
|
|
76
|
-
|
|
180
|
+
key
|
|
77
181
|
);
|
|
78
182
|
} catch (ex) {
|
|
79
183
|
// if the mobileMessage does not exist, the API returns the error "Request failed with status code 400 (ERR_BAD_REQUEST)" which would otherwise bring execution to a hold
|
|
80
|
-
if (
|
|
184
|
+
if (key && ex.code === 'ERR_BAD_REQUEST') {
|
|
81
185
|
Util.logger.info(
|
|
82
|
-
`Downloaded: ${this.definition.type} (0)${Util.getKeysString(
|
|
186
|
+
`Downloaded: ${this.definition.type} (0)${Util.getKeysString(key)}`
|
|
83
187
|
);
|
|
84
188
|
} else {
|
|
85
189
|
throw ex;
|
|
@@ -88,13 +192,43 @@ class MobileKeyword extends MetadataType {
|
|
|
88
192
|
}
|
|
89
193
|
|
|
90
194
|
/**
|
|
91
|
-
*
|
|
195
|
+
* helper for {@link retrieve} and {@link retrieveAsTemplate}
|
|
196
|
+
*
|
|
197
|
+
* @private
|
|
198
|
+
* @param {string} key customer key of single item to retrieve / name of the metadata file
|
|
199
|
+
* @returns {Array} key, queryParams
|
|
200
|
+
*/
|
|
201
|
+
static #getRetrieveKeyAndUrl(key) {
|
|
202
|
+
let queryParams;
|
|
203
|
+
if (key) {
|
|
204
|
+
if (key.startsWith('id:')) {
|
|
205
|
+
// overwrite queryParams
|
|
206
|
+
queryParams = key.slice(3);
|
|
207
|
+
} else if (key.includes('.')) {
|
|
208
|
+
// keywords are always uppercased
|
|
209
|
+
key = key.toUpperCase();
|
|
210
|
+
// format: code.keyword
|
|
211
|
+
const [code, keyword] = key.split('.');
|
|
212
|
+
queryParams = `?view=simple&$where=keyword%20eq%20%27${keyword}%27%20and%code%20eq%20%27${code}%27%20`;
|
|
213
|
+
} else {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`key ${key} has unexpected format. Expected 'code.keyword' or 'id:yourId'`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
queryParams = '?view=simple';
|
|
220
|
+
}
|
|
221
|
+
return [key, queryParams];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Creates a single item
|
|
92
226
|
*
|
|
93
|
-
* @param {TYPE.MetadataTypeItem}
|
|
227
|
+
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
94
228
|
* @returns {Promise} Promise
|
|
95
229
|
*/
|
|
96
|
-
static create(
|
|
97
|
-
return super.createREST(
|
|
230
|
+
static create(metadata) {
|
|
231
|
+
return super.createREST(metadata, '/legacy/v1/beta/mobile/keyword/');
|
|
98
232
|
}
|
|
99
233
|
/**
|
|
100
234
|
* Updates a single item
|
|
@@ -114,9 +248,21 @@ class MobileKeyword extends MetadataType {
|
|
|
114
248
|
* manages post retrieve steps
|
|
115
249
|
*
|
|
116
250
|
* @param {TYPE.MetadataTypeItem} metadata a single item
|
|
117
|
-
* @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem} Array with one metadata object and one ssjs string
|
|
251
|
+
* @returns {TYPE.CodeExtractItem | TYPE.MetadataTypeItem | void} Array with one metadata object and one ssjs string; or single metadata object; nothing if filtered
|
|
118
252
|
*/
|
|
119
253
|
static postRetrieveTasks(metadata) {
|
|
254
|
+
try {
|
|
255
|
+
cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
|
|
256
|
+
} catch {
|
|
257
|
+
// in case the the mobileCode cannot be found, do not save this keyword as its no longer accessible in the UI either
|
|
258
|
+
Util.logger.debug(
|
|
259
|
+
` - skipping ${this.definition.type} ${
|
|
260
|
+
metadata[this.definition.keyField]
|
|
261
|
+
}. Could not find parent mobileCode ${metadata.code.code}`
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
120
266
|
if (metadata.responseMessage) {
|
|
121
267
|
// extract message body
|
|
122
268
|
const codeArr = [];
|
|
@@ -295,8 +441,7 @@ class MobileKeyword extends MetadataType {
|
|
|
295
441
|
);
|
|
296
442
|
}
|
|
297
443
|
|
|
298
|
-
|
|
299
|
-
metadata.code.id = cache.searchForField('mobileCode', metadata.code.code, 'code', 'id');
|
|
444
|
+
this.#setCodeAndKeyword(metadata);
|
|
300
445
|
return metadata;
|
|
301
446
|
}
|
|
302
447
|
/**
|
|
@@ -163,21 +163,17 @@ class MobileMessage extends MetadataType {
|
|
|
163
163
|
|
|
164
164
|
// mobileKeyword
|
|
165
165
|
try {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
metadata.subscriptionKeyword.keyword,
|
|
178
|
-
'keyword',
|
|
179
|
-
'keyword'
|
|
180
|
-
);
|
|
166
|
+
for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
|
|
167
|
+
if (metadata[attr]?.id) {
|
|
168
|
+
metadata[attr] = {
|
|
169
|
+
c__codeKeyword: cache.searchForField(
|
|
170
|
+
'mobileKeyword',
|
|
171
|
+
metadata[attr].id,
|
|
172
|
+
'id',
|
|
173
|
+
'c__codeKeyword'
|
|
174
|
+
),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
181
177
|
}
|
|
182
178
|
} catch (ex) {
|
|
183
179
|
Util.logger.warn(
|
|
@@ -265,20 +261,16 @@ class MobileMessage extends MetadataType {
|
|
|
265
261
|
metadata.code = code;
|
|
266
262
|
|
|
267
263
|
// mobileKeyword
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!keyword) {
|
|
278
|
-
throw new Error(`mobileKeyword ${metadata.keyword.keyword} not found in cache`);
|
|
264
|
+
for (const attr of ['keyword', 'subscriptionKeyword', 'nextKeyword']) {
|
|
265
|
+
if (metadata[attr]?.c__codeKeyword) {
|
|
266
|
+
const keywordObj = cache.getByKey('mobileKeyword', metadata[attr].c__codeKeyword);
|
|
267
|
+
if (!keywordObj) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`mobileKeyword ${metadata[attr].c__codeKeyword} not found in cache`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
metadata[attr] = keywordObj;
|
|
279
273
|
}
|
|
280
|
-
|
|
281
|
-
metadata.subscriptionKeyword.keyword = keyword;
|
|
282
274
|
}
|
|
283
275
|
|
|
284
276
|
// campaign
|
|
@@ -50,6 +50,32 @@ class Query extends MetadataType {
|
|
|
50
50
|
key
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* a function to start query execution via API
|
|
55
|
+
*
|
|
56
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
57
|
+
* @returns {Promise.<boolean>} Returns true if all items were executed successfully, otherwise false
|
|
58
|
+
*/
|
|
59
|
+
static async execute(keyArr) {
|
|
60
|
+
const results = [];
|
|
61
|
+
// works only with objectId
|
|
62
|
+
let objectId;
|
|
63
|
+
for (const key of keyArr) {
|
|
64
|
+
if (key) {
|
|
65
|
+
objectId = await this._getObjectIdForSingleRetrieve(key);
|
|
66
|
+
if (!objectId) {
|
|
67
|
+
Util.logger.info(`Skipping ${key} - did not find an item with such key`);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
results.push(
|
|
72
|
+
super.executeREST(`/automation/v1/queries/${objectId}/actions/start/`, key)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const successCounter = (await Promise.all(results)).filter((r) => r === 'OK').length;
|
|
76
|
+
Util.logger.info(`Executed ${successCounter} of ${keyArr.length} items`);
|
|
77
|
+
return successCounter === keyArr.length;
|
|
78
|
+
}
|
|
53
79
|
/**
|
|
54
80
|
* helper to allow us to select single metadata entries via REST
|
|
55
81
|
*
|
|
@@ -102,9 +102,8 @@ class Role extends MetadataType {
|
|
|
102
102
|
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
103
103
|
Util.getKeysString(key)
|
|
104
104
|
);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
105
|
+
|
|
106
|
+
await this.runDocumentOnRetrieve(key, savedMetadata);
|
|
108
107
|
}
|
|
109
108
|
return { metadata: parsed, type: this.definition.type };
|
|
110
109
|
}
|
|
@@ -58,9 +58,9 @@ class TransactionalSMS extends TransactionalMessage {
|
|
|
58
58
|
// we merely want to be able to show an error if it does not exist
|
|
59
59
|
cache.searchForField(
|
|
60
60
|
'mobileKeyword',
|
|
61
|
-
metadata.subscriptions.keyword,
|
|
62
|
-
'
|
|
63
|
-
'
|
|
61
|
+
metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
|
|
62
|
+
'c__codeKeyword',
|
|
63
|
+
'id'
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
return metadata;
|
|
@@ -142,9 +142,9 @@ class TransactionalSMS extends TransactionalMessage {
|
|
|
142
142
|
// we merely want to be able to show a warning if it does not exist
|
|
143
143
|
cache.searchForField(
|
|
144
144
|
'mobileKeyword',
|
|
145
|
-
metadata.subscriptions.keyword,
|
|
145
|
+
metadata.subscriptions.shortCode + '.' + metadata.subscriptions.keyword,
|
|
146
146
|
'keyword',
|
|
147
|
-
'
|
|
147
|
+
'id'
|
|
148
148
|
);
|
|
149
149
|
} catch {
|
|
150
150
|
Util.logger.warn(
|