mcdev 5.1.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +4 -4
- package/.fork/custom-commands.json +12 -0
- package/.github/ISSUE_TEMPLATE/bug.yml +2 -0
- package/.github/PULL_REQUEST_TEMPLATE/pr_template_release.md +19 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +2 -2
- package/.github/workflows/coverage-develop-branch.yml +0 -2
- package/.github/workflows/coverage-main-branch.yml +0 -2
- package/.github/workflows/coverage.yml +0 -2
- package/.husky/post-checkout +1 -0
- package/.husky/post-merge +1 -0
- package/.vscode/extensions.json +4 -0
- package/docs/dist/documentation.md +1034 -296
- package/lib/Deployer.js +25 -25
- package/lib/MetadataTypeDefinitions.js +2 -1
- package/lib/MetadataTypeInfo.js +2 -1
- package/lib/Retriever.js +1 -1
- package/lib/cli.js +165 -10
- package/lib/index.js +398 -95
- package/lib/metadataTypes/Asset.js +10 -11
- package/lib/metadataTypes/AttributeGroup.js +76 -2
- package/lib/metadataTypes/AttributeSet.js +367 -0
- package/lib/metadataTypes/Automation.js +483 -137
- package/lib/metadataTypes/DataExtension.js +465 -68
- package/lib/metadataTypes/DataExtensionField.js +31 -14
- package/lib/metadataTypes/Event.js +2 -3
- package/lib/metadataTypes/Folder.js +1 -1
- package/lib/metadataTypes/Journey.js +13 -7
- package/lib/metadataTypes/MetadataType.js +212 -54
- package/lib/metadataTypes/MobileKeyword.js +9 -9
- package/lib/metadataTypes/MobileMessage.js +5 -5
- package/lib/metadataTypes/Query.js +26 -10
- package/lib/metadataTypes/Script.js +3 -3
- package/lib/metadataTypes/TransactionalEmail.js +94 -17
- package/lib/metadataTypes/TransactionalMessage.js +3 -2
- package/lib/metadataTypes/TransactionalSMS.js +5 -5
- package/lib/metadataTypes/TriggeredSend.js +25 -50
- package/lib/metadataTypes/User.js +7 -4
- package/lib/metadataTypes/Verification.js +230 -0
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +119 -108
- package/lib/metadataTypes/definitions/{SetDefinition.definition.js → AttributeSet.definition.js} +123 -43
- package/lib/metadataTypes/definitions/Automation.definition.js +23 -15
- package/lib/metadataTypes/definitions/ImportFile.definition.js +36 -6
- package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +19 -1
- package/lib/metadataTypes/definitions/TriggeredSend.definition.js +1 -0
- package/lib/metadataTypes/definitions/Verification.definition.js +88 -0
- package/lib/util/cache.js +9 -4
- package/lib/util/cli.js +40 -0
- package/lib/util/file.js +2 -2
- package/lib/util/init.js +84 -0
- package/lib/util/util.js +121 -13
- package/package.json +13 -13
- package/test/mockRoot/.mcdevrc.json +1 -1
- package/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testExisting_dataExtensionShared.dataExtension-meta.json +59 -0
- package/test/mockRoot/deploy/testInstance/_ParentBU_/dataExtension/testNew_dataExtensionShared.dataExtension-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/testBU/automation/testExisting_automation.automation-meta.json +1 -2
- package/test/mockRoot/deploy/testInstance/testBU/automation/testNew_automation.automation-meta.json +9 -6
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/testExisting_dataExtension.dataExtension-meta.json +1 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtract/testExisting_dataExtract.dataExtract-meta.json +35 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtract/testNew_dataExtract.dataExtract-meta.json +35 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileTransfer/testExisting_fileTransfer.fileTransfer-meta.json +17 -0
- package/test/mockRoot/deploy/testInstance/testBU/fileTransfer/testNew_fileTransfer.fileTransfer-meta.json +17 -0
- package/test/mockRoot/deploy/testInstance/testBU/importFile/testExisting_importFile.importFile-meta.json +29 -0
- package/test/mockRoot/deploy/testInstance/testBU/importFile/testNew_importFile.importFile-meta.json +29 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExisting_query_fixKeys.query-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExisting_query_fixKeys.query-meta.sql +6 -0
- package/test/mockRoot/deploy/testInstance/testBU/script/testExisting_script.script-meta.json +6 -0
- package/test/mockRoot/deploy/testInstance/testBU/script/testExisting_script.script-meta.ssjs +1 -0
- package/test/mockRoot/deploy/testInstance/testBU/script/testNew_script.script-meta.json +6 -0
- package/test/mockRoot/deploy/testInstance/testBU/script/testNew_script.script-meta.ssjs +1 -0
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +3 -4
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +1 -6
- package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testExisting_triggeredSend.triggeredSend-meta.json +29 -0
- package/test/mockRoot/deploy/testInstance/testBU/triggeredSend/testNew_triggeredSend.triggeredSend-meta.json +29 -0
- package/test/mockRoot/deploy/testInstance/testBU/verification/testExisting_39f6a488-20eb-4ba0-b0b9.verification-meta.json +11 -0
- package/test/mockRoot/deploy/testInstance/testBU/verification/testNew_39f6a488-20eb-4ba0-b0b9.verification-meta.json +11 -0
- package/test/resourceFactory.js +77 -12
- package/test/resources/1111111/accountUser/retrieve-ActiveFlag=falseANDCustomerKey=testExisting_userANDEmaillike@-response.xml +27 -0
- package/test/resources/1111111/accountUser/retrieve-ActiveFlag=falseANDEmaillike@-response.xml +156 -0
- package/test/resources/1111111/accountUser/retrieve-ActiveFlag=trueANDEmailisNullORNamelikeapp userANDMustChangePassword=false-response.xml +87 -0
- package/test/resources/1111111/accountUser/retrieve-ActiveFlag=trueANDEmaillike@-response.xml +156 -0
- package/test/resources/1111111/accountUser/retrieve-CustomerKey=testExisting_userANDActiveFlag=trueANDEmailisNullORNamelikeapp userANDMustChangePassword=false-response.xml +27 -0
- package/test/resources/1111111/accountUserAccount/retrieve-AccountUser.AccountUserID=700301950-response.xml +60 -0
- package/test/resources/1111111/data/v1/customobjectdata/key/testExisting_dataExtensionShared/rowset/get-response.json +13 -0
- package/test/resources/1111111/dataExtension/create-expected.json +23 -0
- package/test/resources/1111111/dataExtension/create-response.xml +59 -0
- package/test/resources/1111111/dataExtension/retrieve-expected.json +55 -0
- package/test/resources/1111111/dataExtension/retrieve-expected.md +18 -0
- package/test/resources/1111111/dataExtension/retrieve-response.xml +27 -1
- package/test/resources/1111111/dataExtension/update-expected.json +55 -0
- package/test/resources/1111111/dataExtension/update-response.xml +57 -0
- package/test/resources/1111111/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtensionShared].[TriggerUpdate_randomNumber_]-response.xml +45 -0
- package/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml +98 -0
- package/test/resources/1111111/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionSharedORDataExtension.CustomerKey=testExisting_dataExtensionShared-response.xml +98 -0
- package/test/resources/1111111/dataExtensionField/retrieve-response.xml +98 -0
- package/test/resources/1111111/dataExtensionTemplate/retrieve-response.xml +303 -0
- package/test/resources/1111111/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml +387 -0
- package/test/resources/1111111/dataFolder/retrieve-response.xml +353 -9
- package/test/resources/1111111/user/retrieve-expected.md +4 -2
- package/test/resources/9999999/attributeGroup/retrieve-expected.json +25 -0
- package/test/resources/9999999/attributeSet/retrieve-expected.json +143 -0
- package/test/resources/9999999/automation/build-expected.json +5 -2
- package/test/resources/9999999/automation/create-expected.json +11 -8
- package/test/resources/9999999/automation/create-testNew_automation-expected.md +5 -4
- package/test/resources/9999999/automation/patch_fixKeys-pause-expected.json +44 -0
- package/test/resources/9999999/automation/patch_fixKeys-schedule-expected.json +44 -0
- package/test/resources/9999999/automation/perform-08afb0e2-b00a-4c88-ad2e-1f7f8788c560-response.xml +42 -0
- package/test/resources/9999999/automation/perform-08afb0e2-b00a-4c88-fixKey_pause-response.xml +42 -0
- package/test/resources/9999999/automation/perform-08afb0e2-b00a-4c88-fixKey_schedule-response.xml +42 -0
- package/test/resources/9999999/automation/perform-a8afb0e2-b00a-4c88-ad2e-1f7f8788c560-response.xml +42 -0
- package/test/resources/9999999/automation/retrieve-expected.json +5 -2
- package/test/resources/9999999/automation/retrieve-testExisting_automation-expected.md +3 -2
- package/test/resources/9999999/automation/schedule-08afb0e2-b00a-4c88-ad2e-1f7f8788c560-response.xml +52 -0
- package/test/resources/9999999/automation/schedule-08afb0e2-b00a-4c88-ad2e-pause-response.xml +38 -0
- package/test/resources/9999999/automation/schedule-08afb0e2-b00a-4c88-fixKey_pause-response.xml +52 -0
- package/test/resources/9999999/automation/schedule-08afb0e2-b00a-4c88-fixKey_schedule-response.xml +52 -0
- package/test/resources/9999999/automation/schedule-a8afb0e2-b00a-4c88-ad2e-1f7f8788c560-response.xml +52 -0
- package/test/resources/9999999/automation/template-expected.json +5 -2
- package/test/resources/9999999/automation/update-expected.json +1 -2
- package/test/resources/9999999/automation/update-testExisting_automation-expected.md +2 -2
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +7 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-pause/get-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-ad2e-pause/patch-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-fixKey_pause/get-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-fixKey_pause/patch-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-fixKey_schedule/get-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/08afb0e2-b00a-4c88-fixKey_schedule/patch-response.json +85 -0
- package/test/resources/9999999/automation/v1/automations/a8afb0e2-b00a-4c88-ad2e-1f7f8788c560/get-response.json +1 -1
- package/test/resources/9999999/automation/v1/automations/post-response.json +27 -19
- package/test/resources/9999999/automation/v1/dataextracts/56c5370a-f988-4f36-b0ee-0f876573f6d7/patch-response.json +38 -0
- package/test/resources/9999999/automation/v1/dataextracts/post-response.json +38 -0
- package/test/resources/9999999/automation/v1/dataextracttypes/get-response.json +50 -0
- package/test/resources/9999999/automation/v1/dataverifications/post-response.json +12 -0
- package/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/delete-response.json +0 -0
- package/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/get-response.json +12 -0
- package/test/resources/9999999/automation/v1/dataverifications/testExisting_39f6a488-20eb-4ba0-b0b9/patch-response.json +12 -0
- package/test/resources/9999999/automation/v1/filetransfers/72c328ac-f5b0-4e37-91d3-a775666f15a6/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/filetransfers/post-response.json +18 -0
- package/test/resources/9999999/automation/v1/ftplocations/get-response.json +18 -0
- package/test/resources/9999999/automation/v1/imports/9d16f42c-2260-ed11-b849-48df37d1de8b/patch-response.json +31 -0
- package/test/resources/9999999/automation/v1/imports/get-response.json +1 -1
- package/test/resources/9999999/automation/v1/imports/post-response.json +30 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dae/actions/start/post-response.txt +1 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat_fixKeys/get-response.json +17 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat_fixKeys/patch-response.json +18 -0
- package/test/resources/9999999/automation/v1/queries/get-response.json +18 -1
- package/test/resources/9999999/automation/v1/scripts/39f6a488-20eb-4ba0-b0b9-023725b574e4/patch-response.json +10 -0
- package/test/resources/9999999/automation/v1/scripts/get-response.json +12 -2
- package/test/resources/9999999/automation/v1/scripts/post-response.json +10 -0
- package/test/resources/9999999/dataExtension/build-expected.json +16 -0
- package/test/resources/9999999/dataExtension/delete-response.xml +42 -0
- package/test/resources/9999999/dataExtension/retrieve-Name=testExisting_dataExtension-response.xml +52 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.json +16 -0
- package/test/resources/9999999/dataExtension/retrieve-expected.md +3 -1
- package/test/resources/9999999/dataExtension/template-expected.json +16 -0
- package/test/resources/9999999/dataExtension/update-expected.json +17 -1
- package/test/resources/9999999/dataExtensionField/retrieve-CustomerKey=[testExisting_dataExtension].[LastName]-response.xml +44 -0
- package/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testExisting_dataExtension-response.xml +133 -0
- package/test/resources/9999999/dataExtensionField/retrieve-DataExtension.CustomerKey=testNew_dataExtensionORDataExtension.CustomerKey=testExisting_dataExtension-response.xml +99 -0
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +36 -1
- package/test/resources/9999999/dataExtract/build-expected.json +35 -0
- package/test/resources/9999999/dataExtract/get-expected.json +39 -0
- package/test/resources/9999999/dataExtract/patch-expected.json +37 -0
- package/test/resources/9999999/dataExtract/post-expected.json +37 -0
- package/test/resources/9999999/dataExtract/template-expected.json +35 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=contextual_suppression_listORContentType=publicationORContentType=suppression_listORContentType=mysubsORContentType=list-response.xml +136 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=ssjsactivity-response.xml +48 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=synchronizeddataextensionORContentType=shared_salesforcedataextensionORContentType=shared_dataextensionORContentType=shared_dataORContentType=salesforcedataextensionORContentType=dataextensionORContentType=hidden-response.xml +117 -0
- package/test/resources/9999999/dataFolder/retrieve-ContentType=triggered_send_journeybuilderORContentType=triggered_sendORContentType=hidden-response.xml +276 -0
- package/test/resources/9999999/dataFolder/retrieve-response.xml +23 -0
- package/test/resources/9999999/email/retrieve-response.xml +203 -0
- package/test/resources/9999999/fileTransfer/build-expected.json +15 -0
- package/test/resources/9999999/fileTransfer/get-expected.json +17 -0
- package/test/resources/9999999/fileTransfer/patch-expected.json +17 -0
- package/test/resources/9999999/fileTransfer/post-expected.json +17 -0
- package/test/resources/9999999/fileTransfer/template-expected.json +15 -0
- package/test/resources/9999999/hub/v1/contacts/schema/attributeGroups/get-response.json +628 -0
- package/test/resources/9999999/hub/v1/contacts/schema/setDefinitions/get-response.json +20194 -0
- package/test/resources/9999999/importFile/build-expected.json +27 -0
- package/test/resources/9999999/importFile/get-expected.json +29 -0
- package/test/resources/9999999/importFile/patch-expected.json +29 -0
- package/test/resources/9999999/importFile/post-expected.json +29 -0
- package/test/resources/9999999/importFile/template-expected.json +27 -0
- package/test/resources/9999999/interaction/v1/interactions/233d4413-922c-4568-85a5-e5cc77efc3be/delete-response.json +1 -0
- package/test/resources/9999999/legacy/v1/beta/bulk/automations/automation/definition/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/email/definitions/post-response.json +1 -1
- package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/delete-response.json +6 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation_fixKey_pause-response.xml +32 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation_fixKey_schedule-response.xml +32 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation_fixedKey_paused-response.xml +32 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation_fixedKey_scheduled-response.xml +32 -0
- package/test/resources/9999999/program/retrieve-CustomerKey=testExisting_automation_pause-response.xml +30 -0
- package/test/resources/9999999/program/retrieve-response.xml +21 -3
- package/test/resources/9999999/query/patch_fixKeys-expected.json +11 -0
- package/test/resources/9999999/query/patch_fixKeys-expected.sql +6 -0
- package/test/resources/9999999/queryDefinition/retrieve-CustomerKey=testExisting_query_fixKeysANDStatus=Active-response.xml +30 -0
- package/test/resources/9999999/queryDefinition/retrieve-CustomerKey=testExisting_query_fixedKeysANDStatus=Active-response.xml +30 -0
- package/test/resources/9999999/queryDefinition/retrieve-CustomerKey=testNew_queryANDStatus=Active-response.xml +30 -0
- package/test/resources/9999999/script/build-expected.json +6 -0
- package/test/resources/9999999/script/build-expected.ssjs +1 -0
- package/test/resources/9999999/script/get-expected.json +8 -0
- package/test/resources/9999999/script/get-expected.ssjs +1 -0
- package/test/resources/9999999/script/get_noScriptTag-expected.html +1 -0
- package/test/resources/9999999/script/get_noScriptTag-expected.json +8 -0
- package/test/resources/9999999/script/patch-expected.json +8 -0
- package/test/resources/9999999/script/patch-expected.ssjs +1 -0
- package/test/resources/9999999/script/post-expected.json +8 -0
- package/test/resources/9999999/script/post-expected.ssjs +1 -0
- package/test/resources/9999999/script/template-expected.json +6 -0
- package/test/resources/9999999/script/template-expected.ssjs +1 -0
- package/test/resources/9999999/transactionalEmail/build-expected.json +3 -7
- package/test/resources/9999999/transactionalEmail/get-expected.json +3 -7
- package/test/resources/9999999/transactionalEmail/patch-expected.json +3 -7
- package/test/resources/9999999/transactionalEmail/post-expected.json +3 -7
- package/test/resources/9999999/transactionalEmail/template-expected.json +3 -7
- package/test/resources/9999999/triggeredSend/build-expected.json +29 -0
- package/test/resources/9999999/triggeredSend/get-expected.json +29 -0
- package/test/resources/9999999/triggeredSend/patch-expected.json +29 -0
- package/test/resources/9999999/triggeredSend/post-expected.json +29 -0
- package/test/resources/9999999/triggeredSend/template-expected.json +29 -0
- package/test/resources/9999999/triggeredSendDefinition/create-response.xml +75 -0
- package/test/resources/9999999/triggeredSendDefinition/delete-response.xml +36 -0
- package/test/resources/9999999/triggeredSendDefinition/{retrieve-response.xml → retrieve-TriggeredSendStatusINNew,Active,Inactive,Moved,Canceled-response.xml} +4 -4
- package/test/resources/9999999/triggeredSendDefinition/update-response.xml +74 -0
- package/test/resources/9999999/verification/build-expected.json +11 -0
- package/test/resources/9999999/verification/get-expected.json +11 -0
- package/test/resources/9999999/verification/patch-expected.json +11 -0
- package/test/resources/9999999/verification/post-expected.json +11 -0
- package/test/resources/9999999/verification/template-expected.json +11 -0
- package/test/type.attributeGroup.test.js +55 -0
- package/test/type.attributeSet.test.js +55 -0
- package/test/type.automation.test.js +650 -17
- package/test/type.dataExtension.test.js +205 -46
- package/test/type.dataExtract.test.js +194 -0
- package/test/type.fileTransfer.test.js +192 -0
- package/test/type.importFile.test.js +193 -0
- package/test/type.journey.test.js +38 -11
- package/test/type.mobileKeyword.test.js +6 -5
- package/test/type.mobileMessage.test.js +6 -4
- package/test/type.query.test.js +470 -17
- package/test/type.script.test.js +372 -0
- package/test/type.transactionalEmail.test.js +12 -11
- package/test/type.transactionalPush.test.js +2 -4
- package/test/type.transactionalSMS.test.js +2 -4
- package/test/type.triggeredSend.test.js +154 -0
- package/test/type.user.test.js +22 -10
- package/test/type.verification.test.js +173 -0
- package/test/utils.js +11 -2
- package/types/mcdev.d.js +14 -0
- package/lib/metadataTypes/SetDefinition.js +0 -37
- /package/test/resources/1111111/accountUser/{retrieve-response.xml → retrieve-ActiveFlag=trueANDCustomerKey=testExisting_userANDEmaillike@-response.xml} +0 -0
- /package/test/resources/1111111/accountUserAccount/{retrieve-response.xml → retrieve-AccountUser.AccountUserIDIN700301950,700301951,7471228-response.xml} +0 -0
- /package/test/resources/1111111/businessUnit/{retrieve-response.xml → retrieve-ID=1111111-response.xml} +0 -0
- /package/test/resources/1111111/list/{retrieve-response.xml → retrieve-CustomerKey=All SubscribersORListName=All Subscribers-response.xml} +0 -0
- /package/test/resources/1111111/role/{retrieve-response.xml → retrieve-IsPrivate=false-response.xml} +0 -0
- /package/test/resources/9999999/emailSendDefinition/{retrieve-response.xml → retrieve-IsPlatformObject=falseANDDescriptionnotEqualsSFSendDefinition-response.xml} +0 -0
- /package/test/resources/9999999/queryDefinition/{retrieve-response.xml → retrieve-CustomerKey=testExisting_queryANDStatus=Active-response.xml} +0 -0
|
@@ -25,64 +25,53 @@ class Automation extends MetadataType {
|
|
|
25
25
|
* @returns {Promise.<TYPE.AutomationMapObj>} Promise of metadata
|
|
26
26
|
*/
|
|
27
27
|
static async retrieve(retrieveDir, _, __, key) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
28
|
+
let metadataMap;
|
|
29
|
+
if (key && this._cachedMetadataMap?.[key]) {
|
|
30
|
+
metadataMap = {};
|
|
31
|
+
metadataMap[key] = this._cachedMetadataMap[key];
|
|
32
|
+
delete this._cachedMetadataMap;
|
|
33
|
+
} else if (!key && this._cachedMetadataMap) {
|
|
34
|
+
metadataMap = this._cachedMetadataMap;
|
|
35
|
+
delete this._cachedMetadataMap;
|
|
36
|
+
} else {
|
|
37
|
+
/** @type {TYPE.SoapRequestParams} */
|
|
38
|
+
let requestParams = null;
|
|
39
|
+
if (key) {
|
|
40
|
+
requestParams = {
|
|
41
|
+
filter: {
|
|
42
|
+
leftOperand: 'CustomerKey',
|
|
43
|
+
operator: 'equals',
|
|
44
|
+
rightOperand: key,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const results = await this.client.soap.retrieveBulk(
|
|
49
|
+
'Program',
|
|
50
|
+
['ObjectID'],
|
|
51
|
+
requestParams
|
|
48
52
|
);
|
|
53
|
+
// the API seems to handle 50 concurrent requests nicely
|
|
54
|
+
const response = results?.Results?.length
|
|
55
|
+
? await this.retrieveRESTcollection(
|
|
56
|
+
results?.Results.map((item) => ({
|
|
57
|
+
id: item.ObjectID,
|
|
58
|
+
uri: '/automation/v1/automations/' + item.ObjectID,
|
|
59
|
+
})),
|
|
60
|
+
50,
|
|
61
|
+
!key
|
|
62
|
+
)
|
|
63
|
+
: null;
|
|
64
|
+
metadataMap = response?.metadata || {};
|
|
49
65
|
}
|
|
50
|
-
const details = results.Results
|
|
51
|
-
? await Promise.all(
|
|
52
|
-
results.Results.map(async (a) => {
|
|
53
|
-
try {
|
|
54
|
-
return await this.client.rest.get(
|
|
55
|
-
'/automation/v1/automations/' + a.ObjectID
|
|
56
|
-
);
|
|
57
|
-
} catch (ex) {
|
|
58
|
-
try {
|
|
59
|
-
if (ex.message == 'socket hang up') {
|
|
60
|
-
// one more retry; it's a rare case but retrying again should solve the issue gracefully
|
|
61
|
-
return await this.client.rest.get(
|
|
62
|
-
'/automation/v1/automations/' + a.ObjectID
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
// no extra action needed, handled below
|
|
67
|
-
}
|
|
68
|
-
// if we do get here, we should log the error and continue instead of failing to download all automations
|
|
69
|
-
Util.logger.error(
|
|
70
|
-
` ☇ skipping Automation ${a.ObjectID}: ${ex.message} ${ex.code}`
|
|
71
|
-
);
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
)
|
|
76
|
-
: [];
|
|
77
|
-
let metadataMap = this.parseResponseBody({ items: details });
|
|
78
66
|
|
|
79
|
-
if (Object.keys(metadataMap).length) {
|
|
67
|
+
if (!this._skipNotificationRetrieve && Object.keys(metadataMap).length) {
|
|
80
68
|
// attach notification information to each automation that has any
|
|
81
69
|
await this.#getAutomationNotificationsREST(metadataMap);
|
|
82
70
|
}
|
|
83
71
|
|
|
84
72
|
// * retrieveDir can be empty when we use it in the context of postDeployTasks
|
|
85
73
|
if (retrieveDir) {
|
|
74
|
+
this.retrieveDir = retrieveDir;
|
|
86
75
|
metadataMap = await this.saveResults(metadataMap, retrieveDir, null, null);
|
|
87
76
|
Util.logger.info(
|
|
88
77
|
`Downloaded: ${this.definition.type} (${Object.keys(metadataMap).length})` +
|
|
@@ -94,6 +83,27 @@ class Automation extends MetadataType {
|
|
|
94
83
|
return { metadata: metadataMap, type: this.definition.type };
|
|
95
84
|
}
|
|
96
85
|
|
|
86
|
+
/**
|
|
87
|
+
* helper for {@link this.retrieveRESTcollection}
|
|
88
|
+
*
|
|
89
|
+
* @param {Error} ex exception
|
|
90
|
+
* @param {string} id id or key of item
|
|
91
|
+
* @returns {null} -
|
|
92
|
+
*/
|
|
93
|
+
static async handleRESTErrors(ex, id) {
|
|
94
|
+
try {
|
|
95
|
+
if (ex.message == 'socket hang up') {
|
|
96
|
+
// one more retry; it's a rare case but retrying again should solve the issue gracefully
|
|
97
|
+
return await this.client.rest.get('/automation/v1/automations/' + id);
|
|
98
|
+
}
|
|
99
|
+
} catch {
|
|
100
|
+
// no extra action needed, handled below
|
|
101
|
+
}
|
|
102
|
+
// if we do get here, we should log the error and continue instead of failing to download all automations
|
|
103
|
+
Util.logger.error(` ☇ skipping Automation ${id}: ${ex.message} ${ex.code}`);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
/**
|
|
98
108
|
* helper for {@link Automation.retrieve} to get Automation Notifications
|
|
99
109
|
*
|
|
@@ -144,11 +154,19 @@ class Automation extends MetadataType {
|
|
|
144
154
|
}));
|
|
145
155
|
found++;
|
|
146
156
|
} else {
|
|
147
|
-
|
|
157
|
+
if (
|
|
158
|
+
!notificationsResult ||
|
|
159
|
+
typeof notificationsResult !== 'object' ||
|
|
160
|
+
Object.keys(notificationsResult).length !== 1 ||
|
|
161
|
+
!notificationsResult?.programId
|
|
162
|
+
) {
|
|
163
|
+
throw new TypeError(JSON.stringify(notificationsResult));
|
|
164
|
+
}
|
|
165
|
+
// * if there are no automation notifications, the API returns a single object with the programId
|
|
148
166
|
}
|
|
149
167
|
} catch (ex) {
|
|
150
168
|
Util.logger.debug(
|
|
151
|
-
` ☇
|
|
169
|
+
` ☇ issue retrieving Notifications for automation ${automationLegacy.key}: ${ex.message} ${ex.code}`
|
|
152
170
|
);
|
|
153
171
|
skipped++;
|
|
154
172
|
}
|
|
@@ -215,12 +233,18 @@ class Automation extends MetadataType {
|
|
|
215
233
|
* @returns {Promise.<TYPE.AutomationMapObj>} Promise of metadata
|
|
216
234
|
*/
|
|
217
235
|
static async retrieveForCache() {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
236
|
+
let results = {};
|
|
237
|
+
if (this._cachedMetadataMap) {
|
|
238
|
+
results.Results = Object.values(this._cachedMetadataMap);
|
|
239
|
+
delete this._cachedMetadataMap;
|
|
240
|
+
} else {
|
|
241
|
+
// get automations for cache
|
|
242
|
+
results = await this.client.soap.retrieveBulk('Program', [
|
|
243
|
+
'ObjectID',
|
|
244
|
+
'CustomerKey',
|
|
245
|
+
'Name',
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
224
248
|
const resultsConverted = {};
|
|
225
249
|
if (Array.isArray(results?.Results)) {
|
|
226
250
|
// get encodedAutomationID to retrieve notification information
|
|
@@ -237,15 +261,16 @@ class Automation extends MetadataType {
|
|
|
237
261
|
|
|
238
262
|
// merge encodedAutomationID into results
|
|
239
263
|
for (const m of results.Results) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
264
|
+
const key = m.CustomerKey || m.key;
|
|
265
|
+
resultsConverted[key] = {
|
|
266
|
+
id: m.ObjectID || m.id,
|
|
267
|
+
key: key,
|
|
268
|
+
name: m.Name || m.name,
|
|
269
|
+
programId: automationsLegacy.metadata[key]?.id,
|
|
270
|
+
status: automationsLegacy.metadata[key]?.status,
|
|
245
271
|
};
|
|
246
272
|
}
|
|
247
273
|
}
|
|
248
|
-
|
|
249
274
|
return { metadata: resultsConverted, type: this.definition.type };
|
|
250
275
|
}
|
|
251
276
|
|
|
@@ -318,6 +343,29 @@ class Automation extends MetadataType {
|
|
|
318
343
|
throw new Error(JSON.stringify(results));
|
|
319
344
|
}
|
|
320
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* helper for {@link Automation.postRetrieveTasks} and {@link Automation.execute}
|
|
348
|
+
*
|
|
349
|
+
* @param {TYPE.AutomationItem} metadata a single automation
|
|
350
|
+
* @returns {boolean} true if the automation schedule is valid
|
|
351
|
+
*/
|
|
352
|
+
static #isValidSchedule(metadata) {
|
|
353
|
+
if (metadata.type === 'scheduled' && metadata.schedule?.startDate) {
|
|
354
|
+
try {
|
|
355
|
+
if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) {
|
|
356
|
+
// if we found the id in our list, remove the redundant data
|
|
357
|
+
delete metadata.schedule.timezoneId;
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
Util.logger.debug(
|
|
361
|
+
`- Schedule name '${metadata.schedule.timezoneName}' not found in definition.timeZoneMapping`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
return true;
|
|
365
|
+
} else {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
321
369
|
/**
|
|
322
370
|
* manages post retrieve steps
|
|
323
371
|
*
|
|
@@ -332,34 +380,24 @@ class Automation extends MetadataType {
|
|
|
332
380
|
if (metadata.type === 'scheduled' && metadata.schedule?.startDate) {
|
|
333
381
|
// Starting Source == 'Schedule'
|
|
334
382
|
|
|
335
|
-
|
|
336
|
-
if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) {
|
|
337
|
-
// if we found the id in our list, remove the redundant data
|
|
338
|
-
delete metadata.schedule.timezoneId;
|
|
339
|
-
}
|
|
340
|
-
} catch {
|
|
341
|
-
Util.logger.debug(
|
|
342
|
-
`- Schedule name '${metadata.schedule.timezoneName}' not found in definition.timeZoneMapping`
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
// type 'Running' is temporary status only, overwrite with Scheduled for storage.
|
|
347
|
-
if (metadata.type === 'scheduled' && metadata.status === 'Running') {
|
|
348
|
-
metadata.status = 'Scheduled';
|
|
349
|
-
}
|
|
350
|
-
} catch {
|
|
351
|
-
Util.logger.error(
|
|
352
|
-
`- ${this.definition.type} ${metadata.name} does not have a valid schedule setting.`
|
|
353
|
-
);
|
|
383
|
+
if (!this.#isValidSchedule(metadata)) {
|
|
354
384
|
return;
|
|
355
385
|
}
|
|
386
|
+
// type 'Running' is temporary status only, overwrite with Scheduled for storage.
|
|
387
|
+
if (metadata.type === 'scheduled' && metadata.status === 'Running') {
|
|
388
|
+
metadata.status = 'Scheduled';
|
|
389
|
+
}
|
|
356
390
|
} else if (metadata.type === 'triggered' && metadata.fileTrigger) {
|
|
357
391
|
// Starting Source == 'File Drop'
|
|
358
392
|
// Do nothing for now
|
|
359
393
|
}
|
|
360
394
|
if (metadata.steps) {
|
|
395
|
+
let i = 0;
|
|
396
|
+
|
|
361
397
|
for (const step of metadata.steps) {
|
|
362
|
-
|
|
398
|
+
i++;
|
|
399
|
+
|
|
400
|
+
const stepNumber = step.stepNumber || step.step || i;
|
|
363
401
|
delete step.stepNumber;
|
|
364
402
|
delete step.step;
|
|
365
403
|
|
|
@@ -387,18 +425,28 @@ class Automation extends MetadataType {
|
|
|
387
425
|
activity.activityObjectId == null
|
|
388
426
|
) {
|
|
389
427
|
Util.logger.debug(
|
|
390
|
-
` -
|
|
391
|
-
|
|
392
|
-
|
|
428
|
+
` - skipping ${
|
|
429
|
+
metadata[this.definition.keyField]
|
|
430
|
+
} activity ${stepNumber}.${
|
|
431
|
+
activity.displayOrder
|
|
432
|
+
} due to missing activityObjectId: ${JSON.stringify(activity)}`
|
|
393
433
|
);
|
|
394
434
|
// empty if block
|
|
435
|
+
continue;
|
|
395
436
|
} else if (!this.definition.dependencies.includes(activity.r__type)) {
|
|
396
437
|
Util.logger.debug(
|
|
397
|
-
` -
|
|
438
|
+
` - skipping ${
|
|
439
|
+
metadata[this.definition.keyField]
|
|
440
|
+
} activity ${stepNumber}.${
|
|
441
|
+
activity.displayOrder
|
|
442
|
+
} because the type ${
|
|
443
|
+
activity.r__type
|
|
444
|
+
} is not set up as a dependency for ${this.definition.type}`
|
|
398
445
|
);
|
|
446
|
+
continue;
|
|
399
447
|
}
|
|
400
448
|
// / if managed by cache we can update references to support deployment
|
|
401
|
-
|
|
449
|
+
if (
|
|
402
450
|
Definitions[activity.r__type]?.['idField'] &&
|
|
403
451
|
cache.getCache(this.buObject.mid)[activity.r__type]
|
|
404
452
|
) {
|
|
@@ -445,6 +493,197 @@ class Automation extends MetadataType {
|
|
|
445
493
|
return null;
|
|
446
494
|
}
|
|
447
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* a function to start query execution via API
|
|
498
|
+
*
|
|
499
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
500
|
+
* @returns {Promise.<string[]>} Returns list of keys that were executed
|
|
501
|
+
*/
|
|
502
|
+
static async execute(keyArr) {
|
|
503
|
+
const metadataMap = {};
|
|
504
|
+
for (const key of keyArr) {
|
|
505
|
+
if (Util.OPTIONS.schedule) {
|
|
506
|
+
// schedule
|
|
507
|
+
const results = await this.retrieve(undefined, undefined, undefined, key);
|
|
508
|
+
if (Object.keys(results.metadata).length) {
|
|
509
|
+
for (const resultKey of Object.keys(results.metadata)) {
|
|
510
|
+
if (this.#isValidSchedule(results.metadata[resultKey])) {
|
|
511
|
+
metadataMap[resultKey] = results.metadata[resultKey];
|
|
512
|
+
} else {
|
|
513
|
+
Util.logger.error(
|
|
514
|
+
` - skipping ${this.definition.type} ${results.metadata[resultKey].name}: no valid schedule settings found.`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
// runOnce
|
|
521
|
+
const objectId = await this.#getObjectIdForSingleRetrieve(key);
|
|
522
|
+
metadataMap[key] = {};
|
|
523
|
+
metadataMap[key][this.definition.idField] = objectId;
|
|
524
|
+
metadataMap[key][this.definition.keyField] = key;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (!Object.keys(metadataMap).length) {
|
|
528
|
+
Util.logger.error(`No ${this.definition.type} to execute`);
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
Util.logger.info(
|
|
532
|
+
`Starting automations ${
|
|
533
|
+
Util.OPTIONS.schedule
|
|
534
|
+
? 'according to schedule'
|
|
535
|
+
: 'to run once (use --schedule or --execute=schedule to schedule instead)'
|
|
536
|
+
}: ${Object.keys(metadataMap).length}`
|
|
537
|
+
);
|
|
538
|
+
const promiseResults = [];
|
|
539
|
+
for (const key of Object.keys(metadataMap)) {
|
|
540
|
+
if (Util.OPTIONS.schedule && metadataMap[key].status === 'Scheduled') {
|
|
541
|
+
// schedule
|
|
542
|
+
Util.logger.info(
|
|
543
|
+
` - skipping ${this.definition.type} ${metadataMap[key].name}: already scheduled.`
|
|
544
|
+
);
|
|
545
|
+
} else {
|
|
546
|
+
// schedule + runOnce
|
|
547
|
+
promiseResults.push(this.#executeItem(metadataMap, key));
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const results = await Promise.all(promiseResults);
|
|
551
|
+
const executedKeyArr = results
|
|
552
|
+
.filter(Boolean)
|
|
553
|
+
.filter((r) => r.response.OverallStatus === 'OK')
|
|
554
|
+
.map((r) => r.key);
|
|
555
|
+
Util.logger.info(`Executed ${executedKeyArr.length} of ${keyArr.length} items`);
|
|
556
|
+
return executedKeyArr;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* helper for {@link Automation.execute}
|
|
560
|
+
*
|
|
561
|
+
* @param {TYPE.AutomationMap} metadataMap map of metadata
|
|
562
|
+
* @param {string} key key of the metadata
|
|
563
|
+
* @returns {Promise.<{key:string, response:object}>} metadata key and API response
|
|
564
|
+
*/
|
|
565
|
+
static async #executeItem(metadataMap, key) {
|
|
566
|
+
if (Util.OPTIONS.schedule) {
|
|
567
|
+
this.#preDeploySchedule(metadataMap[key]);
|
|
568
|
+
metadataMap[key].status = 'Scheduled';
|
|
569
|
+
return this.#scheduleAutomation(metadataMap, metadataMap, key);
|
|
570
|
+
} else {
|
|
571
|
+
return this.#runOnce(metadataMap[key]);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* helper for {@link Automation.execute}
|
|
577
|
+
*
|
|
578
|
+
* @param {TYPE.AutomationItem} metadataEntry metadata object
|
|
579
|
+
* @returns {Promise.<{key:string, response:object}>} metadata key and API response
|
|
580
|
+
*/
|
|
581
|
+
static async #runOnce(metadataEntry) {
|
|
582
|
+
return super.executeSOAP(metadataEntry);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Standardizes a check for multiple messages but adds query specific filters to error texts
|
|
587
|
+
*
|
|
588
|
+
* @param {object} ex response payload from REST API
|
|
589
|
+
* @returns {string[] | void} formatted Error Message
|
|
590
|
+
*/
|
|
591
|
+
static getErrorsREST(ex) {
|
|
592
|
+
const errors = super.getErrorsREST(ex);
|
|
593
|
+
if (errors?.length > 0) {
|
|
594
|
+
return errors.map((msg) =>
|
|
595
|
+
msg
|
|
596
|
+
.split('403 Forbidden')
|
|
597
|
+
.join('403 Forbidden: Please check if the automation is currently running.')
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
return errors;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* a function to start query execution via API
|
|
605
|
+
*
|
|
606
|
+
* @param {string[]} keyArr customerkey of the metadata
|
|
607
|
+
* @returns {Promise.<string[]>} Returns list of keys that were paused
|
|
608
|
+
*/
|
|
609
|
+
static async pause(keyArr) {
|
|
610
|
+
const metadataMap = {};
|
|
611
|
+
for (const key of keyArr) {
|
|
612
|
+
if (key) {
|
|
613
|
+
const results = await this.retrieve(undefined, undefined, undefined, key);
|
|
614
|
+
if (Object.keys(results.metadata).length) {
|
|
615
|
+
for (const key of Object.keys(results.metadata)) {
|
|
616
|
+
if (this.#isValidSchedule(results.metadata[key])) {
|
|
617
|
+
metadataMap[key] = results.metadata[key];
|
|
618
|
+
} else {
|
|
619
|
+
Util.logger.error(
|
|
620
|
+
` - skipping ${this.definition.type} ${results.metadata[key].name}: no valid schedule settings found.`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
Util.logger.info(`Pausing automations: ${Object.keys(metadataMap).length}`);
|
|
629
|
+
const promiseResults = [];
|
|
630
|
+
for (const key of Object.keys(metadataMap)) {
|
|
631
|
+
if (metadataMap[key].status === 'Scheduled') {
|
|
632
|
+
promiseResults.push(this.#pauseItem(metadataMap[key]));
|
|
633
|
+
} else if (metadataMap[key].status === 'PausedSchedule') {
|
|
634
|
+
Util.logger.info(
|
|
635
|
+
` - skipping ${this.definition.type} ${metadataMap[key].name}: already paused.`
|
|
636
|
+
);
|
|
637
|
+
} else {
|
|
638
|
+
Util.logger.error(
|
|
639
|
+
` - skipping ${this.definition.type} ${
|
|
640
|
+
metadataMap[key].name
|
|
641
|
+
}: currently ${metadataMap[
|
|
642
|
+
key
|
|
643
|
+
].status.toLowerCase()}. Please try again in a few minutes.`
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
const pausedKeyArr = (await Promise.all(promiseResults))
|
|
648
|
+
.filter(Boolean)
|
|
649
|
+
.filter((r) => r.response.OverallStatus === 'OK')
|
|
650
|
+
.map((r) => r.key);
|
|
651
|
+
|
|
652
|
+
Util.logger.info(`Paused ${pausedKeyArr.length} of ${keyArr.length} items`);
|
|
653
|
+
return pausedKeyArr;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* helper for {@link Automation.pause}
|
|
658
|
+
*
|
|
659
|
+
* @param {TYPE.AutomationItem} metadata automation metadata
|
|
660
|
+
* @returns {Promise.<{key:string, response:object}>} metadata key and API response
|
|
661
|
+
*/
|
|
662
|
+
static async #pauseItem(metadata) {
|
|
663
|
+
const schedule = {};
|
|
664
|
+
try {
|
|
665
|
+
const response = await this.client.soap.schedule(
|
|
666
|
+
'Automation',
|
|
667
|
+
schedule,
|
|
668
|
+
{
|
|
669
|
+
Interaction: {
|
|
670
|
+
ObjectID: metadata[this.definition.idField],
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
'pause',
|
|
674
|
+
{}
|
|
675
|
+
);
|
|
676
|
+
Util.logger.info(
|
|
677
|
+
` - paused ${this.definition.type}: ${metadata[this.definition.keyField]} / ${
|
|
678
|
+
metadata[this.definition.nameField]
|
|
679
|
+
}`
|
|
680
|
+
);
|
|
681
|
+
return { key: metadata[this.definition.keyField], response };
|
|
682
|
+
} catch (ex) {
|
|
683
|
+
this._handleSOAPErrors(ex, 'pausing', metadata, false);
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
448
687
|
|
|
449
688
|
/**
|
|
450
689
|
* Deploys automation - the saved file is the original one due to large differences required for deployment
|
|
@@ -452,11 +691,10 @@ class Automation extends MetadataType {
|
|
|
452
691
|
* @param {TYPE.AutomationMap} metadata metadata mapped by their keyField
|
|
453
692
|
* @param {string} targetBU name/shorthand of target businessUnit for mapping
|
|
454
693
|
* @param {string} retrieveDir directory where metadata after deploy should be saved
|
|
455
|
-
* @param {boolean} [isRefresh] optional flag - so far not used by automation
|
|
456
694
|
* @returns {Promise.<TYPE.AutomationMap>} Promise
|
|
457
695
|
*/
|
|
458
|
-
static async deploy(metadata, targetBU, retrieveDir
|
|
459
|
-
const upsertResults = await this.upsert(metadata, targetBU
|
|
696
|
+
static async deploy(metadata, targetBU, retrieveDir) {
|
|
697
|
+
const upsertResults = await this.upsert(metadata, targetBU);
|
|
460
698
|
const savedMetadata = await this.saveResults(upsertResults, retrieveDir, null);
|
|
461
699
|
if (
|
|
462
700
|
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type) &&
|
|
@@ -488,11 +726,47 @@ class Automation extends MetadataType {
|
|
|
488
726
|
* @returns {Promise} Promise
|
|
489
727
|
*/
|
|
490
728
|
static update(metadata, metadataBefore) {
|
|
491
|
-
|
|
729
|
+
if (metadataBefore.status === 'Running') {
|
|
730
|
+
Util.logger.error(
|
|
731
|
+
` ☇ error updating ${this.definition.type} ${
|
|
732
|
+
metadata[this.definition.keyField] || metadata[this.definition.nameField]
|
|
733
|
+
} / ${
|
|
734
|
+
metadata[this.definition.nameField]
|
|
735
|
+
}: You cannot update an automation that's currently running. Please wait a bit and retry.`
|
|
736
|
+
);
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
492
739
|
const uri = '/automation/v1/automations/' + metadata.id;
|
|
493
740
|
return super.updateREST(metadata, uri);
|
|
494
741
|
}
|
|
495
742
|
|
|
743
|
+
/**
|
|
744
|
+
* helper for {@link Automation.preDeployTasks} and {@link Automation.execute}
|
|
745
|
+
*
|
|
746
|
+
* @param {TYPE.AutomationItem} metadata metadata mapped by their keyField
|
|
747
|
+
*/
|
|
748
|
+
static #preDeploySchedule(metadata) {
|
|
749
|
+
delete metadata.schedule.rangeTypeId;
|
|
750
|
+
delete metadata.schedule.pattern;
|
|
751
|
+
delete metadata.schedule.scheduledTime;
|
|
752
|
+
delete metadata.schedule.scheduledStatus;
|
|
753
|
+
if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) {
|
|
754
|
+
metadata.schedule.timezoneId =
|
|
755
|
+
this.definition.timeZoneMapping[metadata.schedule.timezoneName];
|
|
756
|
+
} else {
|
|
757
|
+
Util.logger.error(
|
|
758
|
+
`Could not find timezone ${metadata.schedule.timezoneName} in definition.timeZoneMapping`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// the upsert API needs this to be named scheduleTypeId; the retrieve API returns it as typeId
|
|
763
|
+
metadata.schedule.scheduleTypeId = metadata.schedule.typeId;
|
|
764
|
+
delete metadata.schedule.typeId;
|
|
765
|
+
|
|
766
|
+
// prep startSource
|
|
767
|
+
metadata.startSource = { schedule: metadata.schedule, typeId: 1 };
|
|
768
|
+
}
|
|
769
|
+
|
|
496
770
|
/**
|
|
497
771
|
* Gets executed before deploying metadata
|
|
498
772
|
*
|
|
@@ -516,25 +790,12 @@ class Automation extends MetadataType {
|
|
|
516
790
|
if (metadata.type === 'scheduled' && metadata?.schedule?.startDate) {
|
|
517
791
|
// Starting Source == 'Schedule'
|
|
518
792
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
delete metadata.schedule.scheduledStatus;
|
|
523
|
-
if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) {
|
|
524
|
-
metadata.schedule.timezoneId =
|
|
525
|
-
this.definition.timeZoneMapping[metadata.schedule.timezoneName];
|
|
526
|
-
} else {
|
|
527
|
-
Util.logger.error(
|
|
528
|
-
`Could not find timezone ${metadata.schedule.timezoneName} in definition.timeZoneMapping`
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
delete metadata.schedule.timezoneName;
|
|
532
|
-
// the upsert API needs this to be named scheduleTypeId; the retrieve API returns it as typeId
|
|
533
|
-
metadata.schedule.scheduleTypeId = metadata.schedule.typeId;
|
|
534
|
-
delete metadata.schedule.typeId;
|
|
793
|
+
this.#preDeploySchedule(metadata);
|
|
794
|
+
// * run _buildSchedule here but only to check if things look ok - do not use the returned schedule object for deploy
|
|
795
|
+
this._buildSchedule(metadata.schedule);
|
|
535
796
|
|
|
536
|
-
|
|
537
|
-
metadata.startSource
|
|
797
|
+
delete metadata.schedule.timezoneName;
|
|
798
|
+
delete metadata.startSource.schedule.timezoneName;
|
|
538
799
|
} else if (metadata.type === 'triggered' && metadata.fileTrigger) {
|
|
539
800
|
// Starting Source == 'File Drop'
|
|
540
801
|
|
|
@@ -553,6 +814,7 @@ class Automation extends MetadataType {
|
|
|
553
814
|
delete metadata.schedule;
|
|
554
815
|
delete metadata.type;
|
|
555
816
|
let i = 0;
|
|
817
|
+
const buName = this.buObject.credential + '/' + this.buObject.businessUnit;
|
|
556
818
|
if (metadata.steps) {
|
|
557
819
|
for (const step of metadata.steps) {
|
|
558
820
|
let displayOrder = 0;
|
|
@@ -562,6 +824,23 @@ class Automation extends MetadataType {
|
|
|
562
824
|
activity.name &&
|
|
563
825
|
this.definition.dependencies.includes(activity.r__type)
|
|
564
826
|
) {
|
|
827
|
+
if (
|
|
828
|
+
activity.r__type === 'verification' &&
|
|
829
|
+
this.createdKeyMap?.[buName]?.verification?.[activity.name]
|
|
830
|
+
) {
|
|
831
|
+
Util.logger.info(
|
|
832
|
+
Util.getGrayMsg(
|
|
833
|
+
` - updated verification activity name from ${
|
|
834
|
+
activity.name
|
|
835
|
+
} to ${
|
|
836
|
+
this.createdKeyMap[buName].verification[activity.name]
|
|
837
|
+
}`
|
|
838
|
+
)
|
|
839
|
+
);
|
|
840
|
+
// map structure: cred/bu --> type --> old key --> new key
|
|
841
|
+
activity.name =
|
|
842
|
+
this.createdKeyMap[buName].verification[activity.name];
|
|
843
|
+
}
|
|
565
844
|
// automations can have empty placeholder for activities with only their type defined
|
|
566
845
|
activity.activityObjectId = cache.searchForField(
|
|
567
846
|
activity.r__type,
|
|
@@ -632,6 +911,16 @@ class Automation extends MetadataType {
|
|
|
632
911
|
}
|
|
633
912
|
return deployable;
|
|
634
913
|
}
|
|
914
|
+
/**
|
|
915
|
+
* helper for {@link MetadataType.updateREST} and {@link MetadataType.updateSOAP} that removes old files after the key was changed
|
|
916
|
+
*
|
|
917
|
+
* @private
|
|
918
|
+
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
919
|
+
* @returns {void}
|
|
920
|
+
*/
|
|
921
|
+
static async _postChangeKeyTasks(metadataEntry) {
|
|
922
|
+
super._postChangeKeyTasks(metadataEntry, true);
|
|
923
|
+
}
|
|
635
924
|
|
|
636
925
|
/**
|
|
637
926
|
* Gets executed after deployment of metadata type
|
|
@@ -642,21 +931,53 @@ class Automation extends MetadataType {
|
|
|
642
931
|
*/
|
|
643
932
|
static async postDeployTasks(metadataMap, originalMetadataMap) {
|
|
644
933
|
for (const key in metadataMap) {
|
|
934
|
+
const item = metadataMap[key];
|
|
935
|
+
|
|
936
|
+
const oldKey = Util.changedKeysMap?.[this.definition.type]?.[key] || key;
|
|
937
|
+
delete Util.changedKeysMap?.[this.definition.type]?.[key];
|
|
938
|
+
|
|
939
|
+
if (!item.type) {
|
|
940
|
+
// create response does not return the type attribute
|
|
941
|
+
|
|
942
|
+
const scheduleHelper = item.schedule || item.startSource.schedule;
|
|
943
|
+
|
|
944
|
+
// el.type
|
|
945
|
+
item.type = scheduleHelper
|
|
946
|
+
? 'scheduled'
|
|
947
|
+
: item.fileTrigger
|
|
948
|
+
? 'triggered'
|
|
949
|
+
: undefined;
|
|
950
|
+
|
|
951
|
+
// el.schedule.timezoneName
|
|
952
|
+
if (item.type === 'scheduled') {
|
|
953
|
+
// not existing for triggered automations
|
|
954
|
+
scheduleHelper.timezoneName ||= Util.inverseGet(
|
|
955
|
+
this.definition.timeZoneMapping,
|
|
956
|
+
scheduleHelper.timezoneId
|
|
957
|
+
);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// el.status
|
|
961
|
+
item.status ||= Util.inverseGet(this.definition.statusMapping, item.statusId);
|
|
962
|
+
}
|
|
645
963
|
// need to put schedule on here if status is scheduled
|
|
646
|
-
await Automation.#scheduleAutomation(metadataMap, originalMetadataMap, key);
|
|
964
|
+
await Automation.#scheduleAutomation(metadataMap, originalMetadataMap, key, oldKey);
|
|
647
965
|
|
|
648
966
|
// need to update notifications separately if there are any
|
|
649
967
|
await Automation.#updateNotificationInfoREST(metadataMap, key);
|
|
650
968
|
|
|
651
969
|
// rewrite upsert to retrieve fields
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
for (const step of metadata.steps) {
|
|
970
|
+
if (item.steps) {
|
|
971
|
+
for (const step of item.steps) {
|
|
655
972
|
step.name = step.annotation;
|
|
656
973
|
delete step.annotation;
|
|
657
974
|
}
|
|
658
975
|
}
|
|
659
976
|
}
|
|
977
|
+
if (Util.OPTIONS.execute || Util.OPTIONS.schedule) {
|
|
978
|
+
Util.logger.info(`Executing: ${this.definition.type}`);
|
|
979
|
+
await this.execute(Object.keys(metadataMap));
|
|
980
|
+
}
|
|
660
981
|
}
|
|
661
982
|
/**
|
|
662
983
|
* helper for {@link Automation.postDeployTasks}
|
|
@@ -706,22 +1027,26 @@ class Automation extends MetadataType {
|
|
|
706
1027
|
}
|
|
707
1028
|
|
|
708
1029
|
/**
|
|
709
|
-
* helper for {@link postDeployTasks}
|
|
1030
|
+
* helper for {@link Automation.postDeployTasks}
|
|
710
1031
|
*
|
|
711
1032
|
* @param {TYPE.AutomationMap} metadataMap metadata mapped by their keyField
|
|
712
1033
|
* @param {TYPE.AutomationMap} originalMetadataMap metadata to be updated (contains additioanl fields)
|
|
713
1034
|
* @param {string} key current customer key
|
|
1035
|
+
* @param {string} [oldKey] old customer key before fixKey / changeKeyValue / changeKeyField
|
|
1036
|
+
* @returns {Promise.<{key:string, response:object}>} metadata key and API response
|
|
714
1037
|
*/
|
|
715
|
-
static async #scheduleAutomation(metadataMap, originalMetadataMap, key) {
|
|
716
|
-
|
|
1038
|
+
static async #scheduleAutomation(metadataMap, originalMetadataMap, key, oldKey) {
|
|
1039
|
+
let response = null;
|
|
1040
|
+
oldKey ||= key;
|
|
1041
|
+
if (originalMetadataMap[oldKey]?.type === 'scheduled') {
|
|
717
1042
|
// Starting Source == 'Schedule': Try starting the automation
|
|
718
|
-
if (originalMetadataMap[
|
|
1043
|
+
if (originalMetadataMap[oldKey].status === 'Scheduled') {
|
|
719
1044
|
let schedule = null;
|
|
720
1045
|
try {
|
|
721
|
-
schedule = this._buildSchedule(originalMetadataMap[
|
|
1046
|
+
schedule = this._buildSchedule(originalMetadataMap[oldKey].schedule);
|
|
722
1047
|
} catch (ex) {
|
|
723
1048
|
Util.logger.error(
|
|
724
|
-
`- Could not create schedule for automation '${originalMetadataMap[
|
|
1049
|
+
`- Could not create schedule for automation '${originalMetadataMap[oldKey].name}' to start it: ${ex.message}`
|
|
725
1050
|
);
|
|
726
1051
|
}
|
|
727
1052
|
if (schedule !== null) {
|
|
@@ -734,12 +1059,12 @@ class Automation extends MetadataType {
|
|
|
734
1059
|
const schedule_timezoneString = schedule._timezoneString;
|
|
735
1060
|
delete schedule._timezoneString;
|
|
736
1061
|
// start the automation
|
|
737
|
-
await this.client.soap.schedule(
|
|
1062
|
+
response = await this.client.soap.schedule(
|
|
738
1063
|
'Automation',
|
|
739
1064
|
schedule,
|
|
740
1065
|
{
|
|
741
1066
|
Interaction: {
|
|
742
|
-
ObjectID: metadataMap[key].
|
|
1067
|
+
ObjectID: metadataMap[key][this.definition.idField],
|
|
743
1068
|
},
|
|
744
1069
|
},
|
|
745
1070
|
'start',
|
|
@@ -753,21 +1078,22 @@ class Automation extends MetadataType {
|
|
|
753
1078
|
(schedule_interval > 1 ? 's' : ''));
|
|
754
1079
|
Util.logger.warn(
|
|
755
1080
|
` - scheduled automation '${
|
|
756
|
-
originalMetadataMap[
|
|
1081
|
+
originalMetadataMap[oldKey].name
|
|
757
1082
|
}' deployed as Active: runs every ${intervalString} starting ${
|
|
758
1083
|
schedule_StartDateTime.split('T').join(' ').split('.')[0]
|
|
759
1084
|
} ${schedule_timezoneString}`
|
|
760
1085
|
);
|
|
761
|
-
} catch
|
|
1086
|
+
} catch {
|
|
1087
|
+
// API does not return anything usefull here. We have to know the rules instead
|
|
762
1088
|
Util.logger.error(
|
|
763
|
-
|
|
1089
|
+
` ☇ error starting scheduled ${this.definition.type}${key}: Please check schedule settings`
|
|
764
1090
|
);
|
|
765
1091
|
}
|
|
766
1092
|
}
|
|
767
1093
|
} else {
|
|
768
1094
|
Util.logger.info(
|
|
769
1095
|
Util.getGrayMsg(
|
|
770
|
-
` - scheduled automation '${originalMetadataMap[
|
|
1096
|
+
` - scheduled automation '${originalMetadataMap[oldKey].name}' deployed as Paused`
|
|
771
1097
|
)
|
|
772
1098
|
);
|
|
773
1099
|
}
|
|
@@ -781,6 +1107,7 @@ class Automation extends MetadataType {
|
|
|
781
1107
|
metadataMap[key].schedule.typeId = metadataMap[key].schedule.scheduleTypeId;
|
|
782
1108
|
delete metadataMap[key].schedule.scheduleTypeId;
|
|
783
1109
|
}
|
|
1110
|
+
return { key, response };
|
|
784
1111
|
}
|
|
785
1112
|
|
|
786
1113
|
/**
|
|
@@ -867,6 +1194,9 @@ class Automation extends MetadataType {
|
|
|
867
1194
|
const a = obj.split('=');
|
|
868
1195
|
recurHelper[a[0]] = a[1];
|
|
869
1196
|
}
|
|
1197
|
+
if (recurHelper.INTERVAL) {
|
|
1198
|
+
recurHelper.INTERVAL = Number.parseInt(recurHelper.INTERVAL);
|
|
1199
|
+
}
|
|
870
1200
|
// the ical schedule is all in caps but soap objects require Title Case.
|
|
871
1201
|
const keyStem = recurHelper.FREQ.charAt(0) + recurHelper.FREQ.slice(1, -2).toLowerCase();
|
|
872
1202
|
|
|
@@ -895,13 +1225,18 @@ class Automation extends MetadataType {
|
|
|
895
1225
|
'Scheduling automatically not supported for Weekly, Monthly and Yearly, please configure manually.'
|
|
896
1226
|
);
|
|
897
1227
|
}
|
|
1228
|
+
if (recurHelper.FREQ === 'MINUTELY' && recurHelper.INTERVAL && recurHelper.INTERVAL < 5) {
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
'The smallest interval you can configure is 5 minutes. Please adjust your schedule.'
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
898
1233
|
|
|
899
1234
|
if (this.definition.timeZoneMapping[scheduleObject.timezoneName]) {
|
|
900
1235
|
scheduleObject.timezoneId =
|
|
901
1236
|
this.definition.timeZoneMapping[scheduleObject.timezoneName];
|
|
902
1237
|
} else {
|
|
903
|
-
|
|
904
|
-
|
|
1238
|
+
throw new Error(
|
|
1239
|
+
`Could not find timezone ${scheduleObject.timezoneName} in definition.timeZoneMapping`
|
|
905
1240
|
);
|
|
906
1241
|
}
|
|
907
1242
|
schedule.TimeZone.ID = scheduleObject.timezoneId;
|
|
@@ -995,8 +1330,8 @@ class Automation extends MetadataType {
|
|
|
995
1330
|
// create new Date object reflecting SFMC's servertime
|
|
996
1331
|
const dateServer = new Date(utc + 3600000 * offsetServer);
|
|
997
1332
|
|
|
998
|
-
// return time as a string without trailing "Z"
|
|
999
|
-
return dateServer.toISOString().slice(0, -1);
|
|
1333
|
+
// return time as a string without trailing "Z" and without miliseconds (separated by .)
|
|
1334
|
+
return dateServer.toISOString().slice(0, -1).split('.')[0];
|
|
1000
1335
|
}
|
|
1001
1336
|
/**
|
|
1002
1337
|
* Experimental: Only working for DataExtensions:
|
|
@@ -1023,7 +1358,7 @@ class Automation extends MetadataType {
|
|
|
1023
1358
|
const automationType = { scheduled: 'Schedule', triggered: 'File Drop' };
|
|
1024
1359
|
output += `**Started by:** ${automationType[json.type] || 'Not defined'}\n\n`;
|
|
1025
1360
|
output += `**Status:** ${json.status}\n\n`;
|
|
1026
|
-
if (json.type === 'scheduled') {
|
|
1361
|
+
if (json.type === 'scheduled' || json.schedule) {
|
|
1027
1362
|
const tz =
|
|
1028
1363
|
this.definition.timeZoneDifference[
|
|
1029
1364
|
this.definition.timeZoneMapping[json?.schedule?.timezoneName]
|
|
@@ -1033,7 +1368,7 @@ class Automation extends MetadataType {
|
|
|
1033
1368
|
output += `**Schedule:**\n\n`;
|
|
1034
1369
|
output += `* Start: ${json.schedule.startDate.split('T').join(' ')} ${tz}\n`;
|
|
1035
1370
|
output += `* End: ${json.schedule.endDate.split('T').join(' ')} ${tz}\n`;
|
|
1036
|
-
output += `* Timezone:
|
|
1371
|
+
output += `* Timezone: ${json.schedule.timezoneName}\n`;
|
|
1037
1372
|
|
|
1038
1373
|
const ical = {};
|
|
1039
1374
|
for (const item of json.schedule.icalRecur.split(';')) {
|
|
@@ -1042,9 +1377,20 @@ class Automation extends MetadataType {
|
|
|
1042
1377
|
}
|
|
1043
1378
|
const frequency = ical.FREQ.slice(0, -2).toLowerCase();
|
|
1044
1379
|
|
|
1045
|
-
output += `* Recurrance:
|
|
1046
|
-
|
|
1047
|
-
|
|
1380
|
+
output += `* Recurrance: `;
|
|
1381
|
+
output +=
|
|
1382
|
+
ical.COUNT == 1
|
|
1383
|
+
? 'run only once'
|
|
1384
|
+
: `every${ical.INTERVAL > 1 ? ' ' + ical.INTERVAL : ''} ${
|
|
1385
|
+
frequency === 'dai' ? 'day' : frequency
|
|
1386
|
+
}${ical.INTERVAL > 1 ? 's' : ''}${
|
|
1387
|
+
ical.COUNT
|
|
1388
|
+
? ` for ${ical.COUNT} times`
|
|
1389
|
+
: ical.UNTIL
|
|
1390
|
+
? ' until end date'
|
|
1391
|
+
: ''
|
|
1392
|
+
}`;
|
|
1393
|
+
output += '\n';
|
|
1048
1394
|
} else if (json.schedule) {
|
|
1049
1395
|
output += `**Schedule:** Not defined\n`;
|
|
1050
1396
|
}
|
|
@@ -1224,7 +1570,7 @@ class Automation extends MetadataType {
|
|
|
1224
1570
|
* @param {string} key customer key
|
|
1225
1571
|
* @returns {Promise.<string>} objectId or enpty string
|
|
1226
1572
|
*/
|
|
1227
|
-
static async
|
|
1573
|
+
static async #getObjectIdForSingleRetrieve(key) {
|
|
1228
1574
|
const response = await this.client.soap.retrieve('Program', ['ObjectID'], {
|
|
1229
1575
|
filter: {
|
|
1230
1576
|
leftOperand: 'CustomerKey',
|
|
@@ -1243,7 +1589,7 @@ class Automation extends MetadataType {
|
|
|
1243
1589
|
*/
|
|
1244
1590
|
static async deleteByKey(customerKey) {
|
|
1245
1591
|
// the delete endpoint returns a general exception if the automation does not exist; handle it gracefully instead by adding a retrieve first
|
|
1246
|
-
const objectId = customerKey ? await this
|
|
1592
|
+
const objectId = customerKey ? await this.#getObjectIdForSingleRetrieve(customerKey) : null;
|
|
1247
1593
|
if (!objectId) {
|
|
1248
1594
|
Util.logger.error(` - automation not found`);
|
|
1249
1595
|
return false;
|