mcdev 4.3.4 → 5.0.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.md +20 -0
- package/.coverage-comment-template.svelte +178 -0
- package/.eslintrc.json +2 -0
- package/.github/ISSUE_TEMPLATE/bug.yml +1 -0
- package/.github/workflows/code-test.yml +36 -0
- package/.github/workflows/coverage-base-update.yml +57 -0
- package/.github/workflows/coverage-develop-branch.yml +41 -0
- package/.github/workflows/coverage-main-branch.yml +41 -0
- package/.github/workflows/coverage.yml +77 -0
- package/.husky/post-checkout +1 -1
- package/.prettierrc +1 -1
- package/.vscode/extensions.json +0 -4
- package/boilerplate/config.json +1 -1
- package/boilerplate/files/.prettierrc +1 -1
- package/boilerplate/files/.vscode/extensions.json +1 -1
- package/boilerplate/forcedUpdates.json +4 -0
- package/docs/dist/documentation.md +1196 -430
- package/lib/Builder.js +6 -1
- package/lib/Deployer.js +30 -5
- package/lib/MetadataTypeDefinitions.js +8 -6
- package/lib/MetadataTypeInfo.js +8 -6
- package/lib/cli.js +54 -42
- package/lib/index.js +82 -8
- package/lib/metadataTypes/Asset.js +73 -1
- package/lib/metadataTypes/AttributeGroup.js +0 -1
- package/lib/metadataTypes/Automation.js +48 -5
- package/lib/metadataTypes/Campaign.js +20 -7
- package/lib/metadataTypes/ContentArea.js +1 -1
- package/lib/metadataTypes/DataExtension.js +221 -184
- package/lib/metadataTypes/DataExtensionField.js +12 -19
- package/lib/metadataTypes/DataExtensionTemplate.js +1 -1
- package/lib/metadataTypes/DataExtract.js +1 -1
- package/lib/metadataTypes/DataExtractType.js +1 -1
- package/lib/metadataTypes/Email.js +1 -1
- package/lib/metadataTypes/{EmailSendDefinition.js → EmailSend.js} +5 -5
- package/lib/metadataTypes/{EventDefinition.js → Event.js} +17 -35
- package/lib/metadataTypes/{FtpLocation.js → FileLocation.js} +2 -2
- package/lib/metadataTypes/FileTransfer.js +8 -7
- package/lib/metadataTypes/Filter.js +1 -1
- package/lib/metadataTypes/Folder.js +8 -3
- package/lib/metadataTypes/ImportFile.js +6 -6
- package/lib/metadataTypes/{Interaction.js → Journey.js} +311 -147
- package/lib/metadataTypes/List.js +2 -2
- package/lib/metadataTypes/MetadataType.js +318 -90
- package/lib/metadataTypes/MobileCode.js +0 -1
- package/lib/metadataTypes/MobileKeyword.js +336 -40
- package/lib/metadataTypes/MobileMessage.js +473 -0
- package/lib/metadataTypes/Query.js +114 -32
- package/lib/metadataTypes/Role.js +60 -21
- package/lib/metadataTypes/Script.js +2 -3
- package/lib/metadataTypes/SendClassification.js +40 -0
- package/lib/metadataTypes/SetDefinition.js +1 -7
- package/lib/metadataTypes/TransactionalEmail.js +2 -3
- package/lib/metadataTypes/TransactionalMessage.js +1 -2
- package/lib/metadataTypes/TransactionalSMS.js +8 -15
- package/lib/metadataTypes/{TriggeredSendDefinition.js → TriggeredSend.js} +35 -27
- package/lib/metadataTypes/User.js +1177 -0
- package/lib/metadataTypes/definitions/Asset.definition.js +1 -0
- package/lib/metadataTypes/definitions/AttributeGroup.definition.js +1 -0
- package/lib/metadataTypes/definitions/Automation.definition.js +3 -2
- package/lib/metadataTypes/definitions/Campaign.definition.js +79 -4
- package/lib/metadataTypes/definitions/ContentArea.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtension.definition.js +2 -1
- package/lib/metadataTypes/definitions/DataExtensionField.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtract.definition.js +1 -0
- package/lib/metadataTypes/definitions/DataExtractType.definition.js +1 -0
- package/lib/metadataTypes/definitions/Discovery.definition.js +1 -0
- package/lib/metadataTypes/definitions/Email.definition.js +1 -0
- package/lib/metadataTypes/definitions/{EmailSendDefinition.definition.js → EmailSend.definition.js} +4 -2
- package/lib/metadataTypes/definitions/{EventDefinition.definition.js → Event.definition.js} +2 -1
- package/lib/metadataTypes/definitions/{FtpLocation.definition.js → FileLocation.definition.js} +4 -3
- package/lib/metadataTypes/definitions/FileTransfer.definition.js +3 -2
- package/lib/metadataTypes/definitions/Filter.definition.js +1 -0
- package/lib/metadataTypes/definitions/Folder.definition.js +2 -0
- package/lib/metadataTypes/definitions/ImportFile.definition.js +4 -3
- package/lib/metadataTypes/definitions/{Interaction.definition.js → Journey.definition.js} +11 -2
- package/lib/metadataTypes/definitions/List.definition.js +1 -0
- package/lib/metadataTypes/definitions/MobileCode.definition.js +3 -1
- package/lib/metadataTypes/definitions/MobileKeyword.definition.js +27 -17
- package/lib/metadataTypes/definitions/MobileMessage.definition.js +743 -0
- package/lib/metadataTypes/definitions/Query.definition.js +3 -2
- package/lib/metadataTypes/definitions/Role.definition.js +5 -0
- package/lib/metadataTypes/definitions/Script.definition.js +1 -0
- package/lib/metadataTypes/definitions/SendClassification.definition.js +114 -0
- package/lib/metadataTypes/definitions/SetDefinition.definition.js +1 -0
- package/lib/metadataTypes/definitions/TransactionalEmail.definition.js +2 -1
- package/lib/metadataTypes/definitions/TransactionalPush.definition.js +1 -0
- package/lib/metadataTypes/definitions/TransactionalSMS.definition.js +1 -0
- package/lib/metadataTypes/definitions/{TriggeredSendDefinition.definition.js → TriggeredSend.definition.js} +5 -3
- package/lib/metadataTypes/definitions/User.definition.js +365 -0
- package/lib/retrieveChangelog.js +1 -2
- package/lib/util/auth.js +29 -9
- package/lib/util/businessUnit.js +3 -3
- package/lib/util/cli.js +55 -7
- package/lib/util/devops.js +6 -4
- package/lib/util/file.js +55 -13
- package/lib/util/init.config.js +1 -2
- package/lib/util/init.npm.js +3 -3
- package/lib/util/util.js +23 -14
- package/package.json +16 -15
- package/test/general.test.js +62 -0
- package/test/mockRoot/.mcdevrc.json +7 -5
- package/test/mockRoot/deploy/testInstance/_ParentBU_/user/testBlocked_user.user-meta.json +23 -0
- package/test/mockRoot/deploy/testInstance/_ParentBU_/user/testExisting_user.user-meta.json +31 -0
- package/test/mockRoot/deploy/testInstance/_ParentBU_/user/testNew_user.user-meta.json +27 -0
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/{childBU_dataextension_test.dataExtension-meta.json → testExisting_dataExtension.dataExtension-meta.json} +2 -2
- package/test/mockRoot/deploy/testInstance/testBU/dataExtension/{testDataExtension.dataExtension-meta.json → testNew_dataExtension.dataExtension-meta.json} +2 -2
- package/test/mockRoot/deploy/testInstance/testBU/journey/testExisting_interaction.interaction-meta.json +576 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.amp +2 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword.mobileKeyword-meta.json +10 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.amp +2 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileKeyword/testNew_keyword_blocked.mobileKeyword-meta.json +10 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/NTIzOjc4OjA.mobileMessage-meta.amp +1 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/NTIzOjc4OjA.mobileMessage-meta.json +61 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/new.mobileMessage-meta.amp +1 -0
- package/test/mockRoot/deploy/testInstance/testBU/mobileMessage/new.mobileMessage-meta.json +60 -0
- package/test/mockRoot/deploy/testInstance/testBU/query/testExistingQuery.query-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/query/testNewQuery.query-meta.sql +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testExisting_temail.transactionalEmail-meta.json +1 -1
- package/test/mockRoot/deploy/testInstance/testBU/transactionalEmail/testNew_temail.transactionalEmail-meta.json +1 -1
- package/test/resourceFactory.js +13 -0
- package/test/resources/1111111/accountUser/configure-response.xml +70 -0
- package/test/resources/1111111/accountUser/create-response.xml +97 -0
- package/test/resources/1111111/accountUser/retrieve-response.xml +156 -0
- package/test/resources/1111111/accountUser/update-response.xml +111 -0
- package/test/resources/1111111/accountUserAccount/retrieve-response.xml +77 -0
- package/test/resources/1111111/platform/v1/setup/quickflow/data/get-response.json +455 -0
- package/test/resources/1111111/role/retrieve-response.xml +76 -0
- package/test/resources/1111111/user/build-expected.json +16 -0
- package/test/resources/1111111/user/create-expected.json +21 -0
- package/test/resources/1111111/user/retrieve-expected.json +24 -0
- package/test/resources/1111111/user/template-expected.json +16 -0
- package/test/resources/1111111/user/update-expected.json +21 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/delete-response.json +1 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/get-response.json +17 -0
- package/test/resources/9999999/automation/v1/queries/549f0568-607c-4940-afef-437965094dat/patch-response.json +3 -3
- package/test/resources/9999999/automation/v1/queries/get-response.json +21 -4
- package/test/resources/9999999/automation/v1/queries/post-response.json +4 -4
- package/test/resources/9999999/data/v1/customobjectdata/key/{childBU_dataextension_test → testExisting_dataExtension}/rowset/get-response.json +1 -1
- package/test/resources/9999999/dataExtension/build-expected.json +3 -3
- package/test/resources/9999999/dataExtension/create-expected.json +2 -2
- package/test/resources/9999999/dataExtension/create-response.xml +8 -3
- package/test/resources/9999999/dataExtension/retrieve-expected.json +3 -3
- package/test/resources/9999999/dataExtension/retrieve-response.xml +9 -4
- package/test/resources/9999999/dataExtension/template-expected.json +3 -3
- package/test/resources/9999999/dataExtension/update-expected.json +3 -3
- package/test/resources/9999999/dataExtension/update-response.xml +9 -4
- package/test/resources/9999999/dataExtensionField/retrieve-response.xml +14 -9
- package/test/resources/9999999/interaction/v1/interactions/get-response.json +312 -0
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/get-response.json +312 -0
- package/test/resources/9999999/interaction/v1/interactions/key_testExisting_interaction/put-response.json +592 -0
- package/test/resources/9999999/journey/build-expected.json +572 -0
- package/test/resources/9999999/journey/get-expected.json +576 -0
- package/test/resources/9999999/journey/put-expected.json +576 -0
- package/test/resources/9999999/journey/template-expected.json +572 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/NXV4ZFMwTEFwRVczd3RaLUF5X3p5dzo4Njow/get-response.json +42 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/cTVJaG5oSDJPVUNHcUh6Z3pQT2tVdzo4Njow/delete-response.json +0 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/get-response.json +1 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/keyword/post-response.json +3 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/delete-response.json +0 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/get-response.json +106 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTIzOjc4OjA/post-response.json +0 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/NTQ3Ojc4OjA/get-response.json +127 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/get-response.json +129 -0
- package/test/resources/9999999/legacy/v1/beta/mobile/message/post-response.json +3 -0
- package/test/resources/9999999/legacy/v1/beta2/data/campaign/get-response.json +29 -0
- package/test/resources/9999999/messaging/v1/email/definitions/post-response.json +1 -1
- package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/get-response.json +1 -1
- package/test/resources/9999999/messaging/v1/email/definitions/testExisting_temail/patch-response.json +1 -1
- package/test/resources/9999999/mobileKeyword/build-expected.amp +2 -0
- package/test/resources/9999999/mobileKeyword/build-expected.json +9 -0
- package/test/resources/9999999/mobileKeyword/get-expected.amp +2 -0
- package/test/resources/9999999/mobileKeyword/get-expected.json +15 -0
- package/test/resources/9999999/mobileKeyword/post-create-expected.amp +2 -0
- package/test/resources/9999999/mobileKeyword/post-create-expected.json +17 -0
- package/test/resources/9999999/mobileKeyword/template-expected.amp +2 -0
- package/test/resources/9999999/mobileKeyword/template-expected.json +9 -0
- package/test/resources/9999999/mobileMessage/build-expected.amp +1 -0
- package/test/resources/9999999/mobileMessage/build-expected.json +60 -0
- package/test/resources/9999999/mobileMessage/get-expected.amp +1 -0
- package/test/resources/9999999/mobileMessage/get-expected.json +61 -0
- package/test/resources/9999999/mobileMessage/post-create-expected.amp +1 -0
- package/test/resources/9999999/mobileMessage/post-create-expected.json +63 -0
- package/test/resources/9999999/mobileMessage/post-update-expected.amp +1 -0
- package/test/resources/9999999/mobileMessage/post-update-expected.json +61 -0
- package/test/resources/9999999/mobileMessage/template-expected.amp +1 -0
- package/test/resources/9999999/mobileMessage/template-expected.json +60 -0
- package/test/resources/9999999/query/build-expected.json +1 -1
- package/test/resources/9999999/query/get-expected.json +1 -1
- package/test/resources/9999999/query/get2-expected.json +11 -0
- package/test/resources/9999999/query/patch-expected.json +1 -1
- package/test/resources/9999999/query/post-expected.json +1 -1
- package/test/resources/9999999/query/template-expected.json +1 -1
- package/test/resources/9999999/queryDefinition/retrieve-response.xml +30 -0
- package/test/resources/9999999/transactionalEmail/build-expected.json +5 -5
- package/test/resources/9999999/transactionalEmail/get-expected.json +1 -1
- package/test/resources/9999999/transactionalEmail/patch-expected.json +1 -1
- package/test/resources/9999999/transactionalEmail/post-expected.json +1 -1
- package/test/resources/9999999/transactionalEmail/template-expected.json +5 -5
- package/test/resources/9999999/transactionalPush/build-expected.json +2 -2
- package/test/resources/9999999/transactionalPush/template-expected.json +2 -2
- package/test/resources/9999999/transactionalSMS/build-expected.json +3 -3
- package/test/resources/9999999/transactionalSMS/template-expected.json +3 -3
- package/test/{dataExtension.test.js → type.dataExtension.test.js} +78 -21
- package/test/{interaction.test.js → type.journey.test.js} +64 -30
- package/test/type.mobileKeyword.test.js +250 -0
- package/test/type.mobileMessage.test.js +205 -0
- package/test/{query.test.js → type.query.test.js} +102 -5
- package/test/{transactionalEmail.test.js → type.transactionalEmail.test.js} +40 -2
- package/test/{transactionalPush.test.js → type.transactionalPush.test.js} +41 -2
- package/test/{transactionalSMS.test.js → type.transactionalSMS.test.js} +73 -3
- package/test/type.user.test.js +160 -0
- package/test/utils.js +17 -5
- package/types/mcdev.d.js +48 -15
- package/.github/workflows/code-analysis.yml +0 -57
- package/lib/metadataTypes/AccountUser.js +0 -426
- package/lib/metadataTypes/definitions/AccountUser.definition.js +0 -227
- package/test/mockRoot/deploy/testInstance/testBU/interaction/testExisting_interaction.interaction-meta.json +0 -266
- package/test/resources/9999999/interaction/build-expected.json +0 -260
- package/test/resources/9999999/interaction/get-expected.json +0 -264
- package/test/resources/9999999/interaction/put-expected.json +0 -264
- package/test/resources/9999999/interaction/template-expected.json +0 -260
- package/test/resources/9999999/interaction/v1/interactions/put-response.json +0 -280
- /package/test/mockRoot/deploy/testInstance/testBU/{interaction → journey}/testNew_interaction.interaction-meta.json +0 -0
- /package/test/resources/9999999/{interaction → journey}/post-expected.json +0 -0
|
@@ -87,14 +87,16 @@ class MetadataType {
|
|
|
87
87
|
* Returns fieldnames of Metadata Type. 'this.definition.fields' variable only set in child classes.
|
|
88
88
|
*
|
|
89
89
|
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
90
|
+
* @param {boolean} [isCaching] if true, then check if field should be skipped for caching
|
|
90
91
|
* @returns {string[]} Fieldnames
|
|
91
92
|
*/
|
|
92
|
-
static getFieldNamesToRetrieve(additionalFields) {
|
|
93
|
+
static getFieldNamesToRetrieve(additionalFields, isCaching) {
|
|
93
94
|
const fieldNames = [];
|
|
94
95
|
for (const fieldName in this.definition.fields) {
|
|
95
96
|
if (
|
|
96
97
|
additionalFields?.includes(fieldName) ||
|
|
97
|
-
this.definition.fields[fieldName].retrieving
|
|
98
|
+
(this.definition.fields[fieldName].retrieving &&
|
|
99
|
+
!(isCaching && this.definition.fields[fieldName].skipCache))
|
|
98
100
|
) {
|
|
99
101
|
fieldNames.push(fieldName);
|
|
100
102
|
}
|
|
@@ -112,11 +114,11 @@ class MetadataType {
|
|
|
112
114
|
* @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField
|
|
113
115
|
* @param {string} deployDir directory where deploy metadata are saved
|
|
114
116
|
* @param {string} retrieveDir directory where metadata after deploy should be saved
|
|
117
|
+
* @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets
|
|
115
118
|
* @returns {Promise.<TYPE.MetadataTypeMap>} Promise of keyField => metadata map
|
|
116
119
|
*/
|
|
117
|
-
static async deploy(metadata, deployDir, retrieveDir) {
|
|
118
|
-
const upsertResults = await this.upsert(metadata, deployDir);
|
|
119
|
-
await this.postDeployTasks(upsertResults, metadata);
|
|
120
|
+
static async deploy(metadata, deployDir, retrieveDir, isRefresh) {
|
|
121
|
+
const upsertResults = await this.upsert(metadata, deployDir, isRefresh);
|
|
120
122
|
const savedMetadata = await this.saveResults(upsertResults, retrieveDir, null);
|
|
121
123
|
if (
|
|
122
124
|
this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type) &&
|
|
@@ -132,11 +134,60 @@ class MetadataType {
|
|
|
132
134
|
/**
|
|
133
135
|
* Gets executed after deployment of metadata type
|
|
134
136
|
*
|
|
135
|
-
* @param {TYPE.MetadataTypeMap}
|
|
137
|
+
* @param {TYPE.MetadataTypeMap} upsertResults metadata mapped by their keyField as returned by update/create
|
|
136
138
|
* @param {TYPE.MetadataTypeMap} originalMetadata metadata to be updated (contains additioanl fields)
|
|
139
|
+
* @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates
|
|
140
|
+
* @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets
|
|
141
|
+
* @returns {void}
|
|
142
|
+
*/
|
|
143
|
+
static postDeployTasks(upsertResults, originalMetadata, createdUpdated, isRefresh) {}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* helper for {@link createREST}
|
|
147
|
+
*
|
|
148
|
+
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
149
|
+
* @param {object} apiResponse varies depending on the API call
|
|
137
150
|
* @returns {void}
|
|
138
151
|
*/
|
|
139
|
-
static
|
|
152
|
+
static postCreateTasks(metadataEntry, apiResponse) {}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* helper for {@link updateREST}
|
|
156
|
+
*
|
|
157
|
+
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
158
|
+
* @param {object} apiResponse varies depending on the API call
|
|
159
|
+
* @returns {void}
|
|
160
|
+
*/
|
|
161
|
+
static postUpdateTasks(metadataEntry, apiResponse) {}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* helper for {@link createREST} when legacy API endpoints as these do not return the created item but only their new id
|
|
165
|
+
*
|
|
166
|
+
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
167
|
+
* @param {object} apiResponse varies depending on the API call
|
|
168
|
+
* @returns {Promise.<void>} -
|
|
169
|
+
*/
|
|
170
|
+
static async postDeployTasks_legacyApi(metadataEntry, apiResponse) {
|
|
171
|
+
if (!apiResponse?.[this.definition.idField] && !metadataEntry?.[this.definition.idField]) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const id =
|
|
175
|
+
apiResponse?.[this.definition.idField] || metadataEntry?.[this.definition.idField];
|
|
176
|
+
// re-retrieve created items because the API does not return any info for them except the new id (api key)
|
|
177
|
+
try {
|
|
178
|
+
const { metadata } = await this.retrieveForCache(null, null, 'id:' + id);
|
|
179
|
+
const item = Object.values(metadata)[0];
|
|
180
|
+
// ensure the "created item" cli log entry has the new auto-generated value
|
|
181
|
+
metadataEntry[this.definition.keyField] = item[this.definition.keyField];
|
|
182
|
+
// ensure postRetrieveTasks has the complete object in "apiResponse"
|
|
183
|
+
Object.assign(apiResponse, item);
|
|
184
|
+
// postRetrieveTasks will be run automatically on this via super.saveResult
|
|
185
|
+
} catch (ex) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Could not get details for new ${this.definition.type} ${id} from server (${ex.message})`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
140
191
|
|
|
141
192
|
/**
|
|
142
193
|
* Gets executed after retreive of metadata type
|
|
@@ -222,10 +273,11 @@ class MetadataType {
|
|
|
222
273
|
*
|
|
223
274
|
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
224
275
|
* @param {string[]} [subTypeArr] optionally limit to a single subtype
|
|
276
|
+
* @param {string} [key] customer key of single item to retrieve
|
|
225
277
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} metadata
|
|
226
278
|
*/
|
|
227
|
-
static async retrieveForCache(additionalFields, subTypeArr) {
|
|
228
|
-
return this.retrieve(null, additionalFields, subTypeArr);
|
|
279
|
+
static async retrieveForCache(additionalFields, subTypeArr, key) {
|
|
280
|
+
return this.retrieve(null, additionalFields, subTypeArr, key);
|
|
229
281
|
}
|
|
230
282
|
/**
|
|
231
283
|
* Gets metadata cache with limited fields and does not store value to disk
|
|
@@ -246,6 +298,19 @@ class MetadataType {
|
|
|
246
298
|
);
|
|
247
299
|
return { metadata: null, type: this.definition.type };
|
|
248
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Retrieve a specific Script by Name
|
|
303
|
+
*
|
|
304
|
+
* @param {string} templateDir Directory where retrieved metadata directory will be saved
|
|
305
|
+
* @param {string} uri rest endpoint for GET
|
|
306
|
+
* @param {TYPE.TemplateMap} templateVariables variables to be replaced in the metadata
|
|
307
|
+
* @param {string} name name (not key) of the metadata item
|
|
308
|
+
* @returns {Promise.<{metadata: TYPE.MetadataTypeItem, type: string}>} Promise
|
|
309
|
+
*/
|
|
310
|
+
static async retrieveTemplateREST(templateDir, uri, templateVariables, name) {
|
|
311
|
+
return this.retrieveREST(templateDir, uri, templateVariables, name);
|
|
312
|
+
}
|
|
313
|
+
|
|
249
314
|
/**
|
|
250
315
|
* Gets metadata cache with limited fields and does not store value to disk
|
|
251
316
|
*
|
|
@@ -293,6 +358,26 @@ class MetadataType {
|
|
|
293
358
|
}
|
|
294
359
|
// return;
|
|
295
360
|
}
|
|
361
|
+
if (this.definition.stringifyFieldsBeforeTemplate) {
|
|
362
|
+
// numeric fields are returned as numbers by the SDK/API. If we try to replace them in buildTemplate it would break the JSON format - but not if we stringify them first because then the {{{var}}} is in quotes
|
|
363
|
+
metadataStr = JSON.parse(metadataStr);
|
|
364
|
+
for (const field of this.definition.stringifyFieldsBeforeTemplate) {
|
|
365
|
+
if (metadataStr[field]) {
|
|
366
|
+
if (Array.isArray(metadataStr[field])) {
|
|
367
|
+
for (let i = 0; i < metadataStr[field].length; i++) {
|
|
368
|
+
metadataStr[field][i] = metadataStr[field][i].toString();
|
|
369
|
+
}
|
|
370
|
+
} else if ('object' === typeof metadataStr[field]) {
|
|
371
|
+
for (const subField in metadataStr[field]) {
|
|
372
|
+
metadataStr[field][subField] = metadataStr[field][subField].toString();
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
metadataStr[field] = metadataStr[field].toString();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
metadataStr = JSON.stringify(metadataStr);
|
|
380
|
+
}
|
|
296
381
|
const metadata = JSON.parse(Util.replaceByObject(metadataStr, templateVariables));
|
|
297
382
|
this.keepTemplateFields(metadata);
|
|
298
383
|
|
|
@@ -444,28 +529,30 @@ class MetadataType {
|
|
|
444
529
|
/**
|
|
445
530
|
* MetadataType upsert, after retrieving from target and comparing to check if create or update operation is needed.
|
|
446
531
|
*
|
|
447
|
-
* @param {TYPE.MetadataTypeMap}
|
|
532
|
+
* @param {TYPE.MetadataTypeMap} metadataMap metadata mapped by their keyField
|
|
448
533
|
* @param {string} deployDir directory where deploy metadata are saved
|
|
534
|
+
* @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets
|
|
449
535
|
* @returns {Promise.<TYPE.MetadataTypeMap>} keyField => metadata map
|
|
450
536
|
*/
|
|
451
|
-
static async upsert(
|
|
537
|
+
static async upsert(metadataMap, deployDir, isRefresh) {
|
|
538
|
+
const orignalMetadataMap = JSON.parse(JSON.stringify(metadataMap));
|
|
452
539
|
const metadataToUpdate = [];
|
|
453
540
|
const metadataToCreate = [];
|
|
454
541
|
let filteredByPreDeploy = 0;
|
|
455
|
-
for (const metadataKey in
|
|
542
|
+
for (const metadataKey in metadataMap) {
|
|
456
543
|
let hasError = false;
|
|
457
544
|
try {
|
|
458
545
|
// preDeployTasks parsing
|
|
459
546
|
let deployableMetadata;
|
|
460
547
|
try {
|
|
461
548
|
deployableMetadata = await this.preDeployTasks(
|
|
462
|
-
|
|
549
|
+
metadataMap[metadataKey],
|
|
463
550
|
deployDir
|
|
464
551
|
);
|
|
465
552
|
} catch (ex) {
|
|
466
553
|
// do this in case something went wrong during pre-deploy steps to ensure the total counter is correct
|
|
467
554
|
hasError = true;
|
|
468
|
-
deployableMetadata =
|
|
555
|
+
deployableMetadata = metadataMap[metadataKey];
|
|
469
556
|
Util.logger.error(
|
|
470
557
|
` ☇ skipping ${this.definition.type} ${
|
|
471
558
|
deployableMetadata[this.definition.keyField]
|
|
@@ -474,10 +561,10 @@ class MetadataType {
|
|
|
474
561
|
}
|
|
475
562
|
// if preDeploy returns nothing then it cannot be deployed so skip deployment
|
|
476
563
|
if (deployableMetadata) {
|
|
477
|
-
|
|
564
|
+
metadataMap[metadataKey] = deployableMetadata;
|
|
478
565
|
// create normalizedKey off of whats in the json rather than from "metadataKey" because preDeployTasks might have altered something (type asset)
|
|
479
|
-
this.createOrUpdate(
|
|
480
|
-
|
|
566
|
+
await this.createOrUpdate(
|
|
567
|
+
metadataMap,
|
|
481
568
|
metadataKey,
|
|
482
569
|
hasError,
|
|
483
570
|
metadataToUpdate,
|
|
@@ -518,7 +605,7 @@ class MetadataType {
|
|
|
518
605
|
`${this.definition.type} upsert: ${createResults.length} of ${metadataToCreate.length} created / ${updateResults.length} of ${metadataToUpdate.length} updated` +
|
|
519
606
|
(filteredByPreDeploy > 0 ? ` / ${filteredByPreDeploy} filtered` : '')
|
|
520
607
|
);
|
|
521
|
-
|
|
608
|
+
let upsertResults;
|
|
522
609
|
if (this.definition.bodyIteratorField === 'Results') {
|
|
523
610
|
// if Results then parse as SOAP
|
|
524
611
|
// put in Retrieve Format for parsing
|
|
@@ -530,73 +617,172 @@ class MetadataType {
|
|
|
530
617
|
.filter((r) => r !== undefined && r !== null && Object.keys(r).length !== 0)
|
|
531
618
|
.flatMap((r) => r.Results)
|
|
532
619
|
.map((r) => r.Object);
|
|
533
|
-
|
|
620
|
+
upsertResults = this.parseResponseBody({ Results: metadataResults });
|
|
534
621
|
} else {
|
|
535
622
|
// likely comming from one of the many REST APIs
|
|
536
623
|
// put in Retrieve Format for parsing
|
|
537
624
|
// todo add handling when response does not contain items.
|
|
538
625
|
// @ts-ignore
|
|
539
626
|
const metadataResults = createResults.concat(updateResults).filter(Boolean);
|
|
540
|
-
|
|
627
|
+
upsertResults = this.parseResponseBody(metadataResults);
|
|
541
628
|
}
|
|
629
|
+
await this.postDeployTasks(
|
|
630
|
+
upsertResults,
|
|
631
|
+
orignalMetadataMap,
|
|
632
|
+
{ created: createResults.length, updated: updateResults.length },
|
|
633
|
+
isRefresh
|
|
634
|
+
);
|
|
635
|
+
return upsertResults;
|
|
542
636
|
}
|
|
543
637
|
|
|
544
638
|
/**
|
|
639
|
+
* helper for {@link MetadataType.upsert}
|
|
545
640
|
*
|
|
546
|
-
* @param {TYPE.
|
|
641
|
+
* @param {TYPE.MetadataTypeMap} metadataMap list of metadata
|
|
547
642
|
* @param {string} metadataKey key of item we are looking at
|
|
548
643
|
* @param {boolean} hasError error flag from previous code
|
|
549
644
|
* @param {TYPE.MetadataTypeItemDiff[]} metadataToUpdate list of items to update
|
|
550
645
|
* @param {TYPE.MetadataTypeItem[]} metadataToCreate list of items to create
|
|
551
|
-
* @returns {
|
|
646
|
+
* @returns {'create' | 'update' | 'skip'} action to take
|
|
552
647
|
*/
|
|
553
|
-
static createOrUpdate(
|
|
648
|
+
static createOrUpdate(metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate) {
|
|
554
649
|
const normalizedKey = File.reverseFilterIllegalFilenames(
|
|
555
|
-
|
|
650
|
+
metadataMap[metadataKey][this.definition.keyField]
|
|
556
651
|
);
|
|
557
652
|
// Update if it already exists; Create it if not
|
|
558
|
-
if (
|
|
653
|
+
if (
|
|
654
|
+
Util.logger.level === 'debug' &&
|
|
655
|
+
metadataMap[metadataKey][this.definition.idField] &&
|
|
656
|
+
this.definition.idField !== this.definition.keyField
|
|
657
|
+
) {
|
|
559
658
|
// TODO: re-evaluate in future releases if & when we managed to solve folder dependencies once and for all
|
|
560
659
|
// only used if resource is excluded from cache and we still want to update it
|
|
561
660
|
// needed e.g. to rewire lost folders
|
|
562
661
|
Util.logger.warn(
|
|
563
662
|
' - Hotfix for non-cachable resource found in deploy folder. Trying update:'
|
|
564
663
|
);
|
|
565
|
-
Util.logger.warn(JSON.stringify(
|
|
664
|
+
Util.logger.warn(JSON.stringify(metadataMap[metadataKey]));
|
|
566
665
|
if (hasError) {
|
|
567
666
|
metadataToUpdate.push(null);
|
|
667
|
+
return 'skip';
|
|
568
668
|
} else {
|
|
569
669
|
metadataToUpdate.push({
|
|
570
670
|
before: {},
|
|
571
|
-
after:
|
|
671
|
+
after: metadataMap[metadataKey],
|
|
572
672
|
});
|
|
673
|
+
return 'update';
|
|
573
674
|
}
|
|
574
675
|
} else if (cache.getByKey(this.definition.type, normalizedKey)) {
|
|
575
676
|
// normal way of processing update files
|
|
576
677
|
const cachedVersion = cache.getByKey(this.definition.type, normalizedKey);
|
|
577
|
-
if (!this.hasChanged(cachedVersion,
|
|
678
|
+
if (!this.hasChanged(cachedVersion, metadataMap[metadataKey])) {
|
|
578
679
|
hasError = true;
|
|
579
680
|
}
|
|
681
|
+
if (Util.OPTIONS.changeKeyField) {
|
|
682
|
+
if (this.definition.keyField == this.definition.idField) {
|
|
683
|
+
Util.logger.error(
|
|
684
|
+
` - --changeKeyField cannot be used for types that use their ID as key. Skipping change.`
|
|
685
|
+
);
|
|
686
|
+
hasError = true;
|
|
687
|
+
} else if (this.definition.keyIsFixed) {
|
|
688
|
+
Util.logger.error(
|
|
689
|
+
` - type ${this.definition.type} does not support --changeKeyField and --changeKeyValue. Skipping change.`
|
|
690
|
+
);
|
|
691
|
+
hasError = true;
|
|
692
|
+
} else if (!metadataMap[metadataKey][Util.OPTIONS.changeKeyField]) {
|
|
693
|
+
Util.logger.error(
|
|
694
|
+
` - --changeKeyField is set to ${Util.OPTIONS.changeKeyField} but no value was found in the metadata. Skipping change.`
|
|
695
|
+
);
|
|
696
|
+
hasError = true;
|
|
697
|
+
} else if (Util.OPTIONS.changeKeyField === this.definition.keyField) {
|
|
698
|
+
// simple issue, used WARN to avoid signaling a problem to ci/cd and don't fail deploy
|
|
699
|
+
Util.logger.warn(
|
|
700
|
+
` - --changeKeyField is set to the same value as the keyField for ${this.definition.type}. Skipping change.`
|
|
701
|
+
);
|
|
702
|
+
} else if (metadataMap[metadataKey][Util.OPTIONS.changeKeyField]) {
|
|
703
|
+
// NOTE: trim twice while getting the newKey value to remove leading spaces before limiting the length
|
|
704
|
+
const newKey = (metadataMap[metadataKey][Util.OPTIONS.changeKeyField] + '')
|
|
705
|
+
.trim()
|
|
706
|
+
.slice(0, 36)
|
|
707
|
+
.trim();
|
|
708
|
+
if (metadataMap[metadataKey][Util.OPTIONS.changeKeyField] + '' > 36) {
|
|
709
|
+
Util.logger.warn(
|
|
710
|
+
`Customer Keys may not exceed 36 characters. Truncated the value in field ${Util.OPTIONS.changeKeyField} to ${newKey}`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
if (metadataKey == newKey) {
|
|
714
|
+
Util.logger.warn(
|
|
715
|
+
` - --changeKeyField(${Util.OPTIONS.changeKeyField}) is providing the current value of the key (${metadataKey}). Skipping change.`
|
|
716
|
+
);
|
|
717
|
+
} else {
|
|
718
|
+
Util.logger.info(
|
|
719
|
+
` - Changing ${this.definition.type} key from ${metadataKey} to ${newKey} via --changeKeyField=${Util.OPTIONS.changeKeyField}`
|
|
720
|
+
);
|
|
721
|
+
metadataMap[metadataKey][this.definition.keyField] = newKey;
|
|
722
|
+
|
|
723
|
+
// ensure we can delete the old file(s) after the successful update
|
|
724
|
+
Util.changedKeysMap[this.definition.type] ||= {};
|
|
725
|
+
Util.changedKeysMap[this.definition.type][newKey] = metadataKey;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
} else if (Util.OPTIONS.changeKeyValue) {
|
|
729
|
+
// 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, 36).trim();
|
|
731
|
+
if (Util.OPTIONS.changeKeyValue.trim().length > 36) {
|
|
732
|
+
Util.logger.warn(
|
|
733
|
+
`Customer Keys may not exceed 36 characters. Truncated your value to ${newKey}`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
if (this.definition.keyField == this.definition.idField) {
|
|
737
|
+
Util.logger.error(
|
|
738
|
+
` - --changeKeyValue cannot be used for types that use their ID as key. Skipping change.`
|
|
739
|
+
);
|
|
740
|
+
hasError = true;
|
|
741
|
+
} else if (this.definition.keyIsFixed) {
|
|
742
|
+
Util.logger.error(
|
|
743
|
+
` - type ${this.definition.type} does not support --changeKeyField and --changeKeyValue. Skipping change.`
|
|
744
|
+
);
|
|
745
|
+
hasError = true;
|
|
746
|
+
} else if (metadataKey == newKey) {
|
|
747
|
+
Util.logger.warn(
|
|
748
|
+
` - --changeKeyValue is providing the current value of the key (${metadataKey}). Skipping change.`
|
|
749
|
+
);
|
|
750
|
+
} else {
|
|
751
|
+
Util.logger.info(
|
|
752
|
+
` - Changing ${this.definition.type} key from ${metadataKey} to ${newKey} via --changeKeyValue`
|
|
753
|
+
);
|
|
754
|
+
metadataMap[metadataKey][this.definition.keyField] = newKey;
|
|
755
|
+
|
|
756
|
+
// ensure we can delete the old file(s) after the successful update
|
|
757
|
+
Util.changedKeysMap[this.definition.type] ||= {};
|
|
758
|
+
Util.changedKeysMap[this.definition.type][newKey] = metadataKey;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
580
761
|
|
|
581
762
|
if (hasError) {
|
|
582
763
|
// do this in case something went wrong during pre-deploy steps to ensure the total counter is correct
|
|
583
764
|
metadataToUpdate.push(null);
|
|
765
|
+
return 'skip';
|
|
584
766
|
} else {
|
|
585
767
|
// add ObjectId to allow actual update
|
|
586
|
-
|
|
768
|
+
metadataMap[metadataKey][this.definition.idField] =
|
|
587
769
|
cachedVersion[this.definition.idField];
|
|
588
770
|
|
|
589
771
|
metadataToUpdate.push({
|
|
590
|
-
before:
|
|
591
|
-
after:
|
|
772
|
+
before: cachedVersion,
|
|
773
|
+
after: metadataMap[metadataKey],
|
|
592
774
|
});
|
|
775
|
+
|
|
776
|
+
return 'update';
|
|
593
777
|
}
|
|
594
778
|
} else {
|
|
595
779
|
if (hasError) {
|
|
596
780
|
// do this in case something went wrong during pre-deploy steps to ensure the total counter is correct
|
|
597
781
|
metadataToCreate.push(null);
|
|
782
|
+
return 'skip';
|
|
598
783
|
} else {
|
|
599
|
-
metadataToCreate.push(
|
|
784
|
+
metadataToCreate.push(metadataMap[metadataKey]);
|
|
785
|
+
return 'create';
|
|
600
786
|
}
|
|
601
787
|
}
|
|
602
788
|
}
|
|
@@ -611,7 +797,9 @@ class MetadataType {
|
|
|
611
797
|
static async createREST(metadataEntry, uri) {
|
|
612
798
|
this.removeNotCreateableFields(metadataEntry);
|
|
613
799
|
try {
|
|
614
|
-
|
|
800
|
+
// set to empty object in case API returned nothing to be able to update it in helper classes
|
|
801
|
+
const response = (await this.client.rest.post(uri, metadataEntry)) || {};
|
|
802
|
+
await this.postCreateTasks(metadataEntry, response);
|
|
615
803
|
Util.logger.info(
|
|
616
804
|
` - created ${this.definition.type}: ${
|
|
617
805
|
metadataEntry[this.definition.keyField] ||
|
|
@@ -627,8 +815,12 @@ class MetadataType {
|
|
|
627
815
|
metadataEntry[this.definition.nameField]
|
|
628
816
|
} / ${metadataEntry[this.definition.nameField]}:`
|
|
629
817
|
);
|
|
630
|
-
|
|
631
|
-
|
|
818
|
+
if (parsedErrors.length) {
|
|
819
|
+
for (const msg of parsedErrors) {
|
|
820
|
+
Util.logger.error(' • ' + msg);
|
|
821
|
+
}
|
|
822
|
+
} else if (ex?.message) {
|
|
823
|
+
Util.logger.debug(ex.message);
|
|
632
824
|
}
|
|
633
825
|
return null;
|
|
634
826
|
}
|
|
@@ -638,16 +830,15 @@ class MetadataType {
|
|
|
638
830
|
* Creates a single metadata entry via fuel-soap (generic lib not wrapper)
|
|
639
831
|
*
|
|
640
832
|
* @param {TYPE.MetadataTypeItem} metadataEntry single metadata entry
|
|
641
|
-
* @param {string} [overrideType] can be used if the API type differs from the otherwise used type identifier
|
|
642
833
|
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
643
834
|
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
644
835
|
*/
|
|
645
|
-
static async createSOAP(metadataEntry,
|
|
836
|
+
static async createSOAP(metadataEntry, handleOutside) {
|
|
837
|
+
const soapType = this.definition.soapType || this.definition.type;
|
|
838
|
+
this.removeNotCreateableFields(metadataEntry);
|
|
646
839
|
try {
|
|
647
|
-
this.removeNotCreateableFields(metadataEntry);
|
|
648
840
|
const response = await this.client.soap.create(
|
|
649
|
-
|
|
650
|
-
this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1),
|
|
841
|
+
soapType.charAt(0).toUpperCase() + soapType.slice(1),
|
|
651
842
|
metadataEntry,
|
|
652
843
|
null
|
|
653
844
|
);
|
|
@@ -671,16 +862,17 @@ class MetadataType {
|
|
|
671
862
|
*
|
|
672
863
|
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
673
864
|
* @param {string} uri rest endpoint for PATCH
|
|
674
|
-
* @param {
|
|
865
|
+
* @param {'patch'|'post'|'put'} [httpMethod='patch'] defaults to 'patch'; some update requests require PUT instead of PATCH
|
|
675
866
|
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
676
867
|
*/
|
|
677
|
-
static async updateREST(metadataEntry, uri,
|
|
868
|
+
static async updateREST(metadataEntry, uri, httpMethod = 'patch') {
|
|
678
869
|
this.removeNotUpdateableFields(metadataEntry);
|
|
679
870
|
try {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
871
|
+
// set to empty object in case API returned nothing to be able to update it in helper classes
|
|
872
|
+
const response = (await this.client.rest[httpMethod](uri, metadataEntry)) || {};
|
|
873
|
+
await this._postChangeKeyTasks(metadataEntry);
|
|
683
874
|
this.checkForErrors(response);
|
|
875
|
+
await this.postUpdateTasks(metadataEntry, response);
|
|
684
876
|
// some times, e.g. automation dont return a key in their update response and hence we need to fall back to name
|
|
685
877
|
Util.logger.info(
|
|
686
878
|
` - updated ${this.definition.type}: ${
|
|
@@ -704,20 +896,54 @@ class MetadataType {
|
|
|
704
896
|
}
|
|
705
897
|
}
|
|
706
898
|
|
|
899
|
+
/**
|
|
900
|
+
* helper for {@link updateREST} and {@link updateSOAP} that removes old files after the key was changed
|
|
901
|
+
*
|
|
902
|
+
* @private
|
|
903
|
+
* @param {TYPE.MetadataTypeItem} metadataEntry a single metadata Entry
|
|
904
|
+
* @param {boolean} [keepMap] some types require to check the old-key new-key relationship in their postDeployTasks; currently used by dataExtension only
|
|
905
|
+
* @returns {void}
|
|
906
|
+
*/
|
|
907
|
+
static async _postChangeKeyTasks(metadataEntry, keepMap = false) {
|
|
908
|
+
if (
|
|
909
|
+
(Util.OPTIONS.changeKeyField || Util.OPTIONS.changeKeyValue) &&
|
|
910
|
+
Util.changedKeysMap?.[this.definition.type]?.[metadataEntry[this.definition.keyField]]
|
|
911
|
+
) {
|
|
912
|
+
const oldKey =
|
|
913
|
+
Util.changedKeysMap?.[this.definition.type]?.[
|
|
914
|
+
metadataEntry[this.definition.keyField]
|
|
915
|
+
];
|
|
916
|
+
|
|
917
|
+
// delete file(s) of old key
|
|
918
|
+
await this.postDeleteTasks(oldKey);
|
|
919
|
+
|
|
920
|
+
// fix key in cache
|
|
921
|
+
const typeCache = cache.getCache()[this.definition.type];
|
|
922
|
+
typeCache[metadataEntry[this.definition.keyField]] = typeCache[oldKey];
|
|
923
|
+
delete typeCache[oldKey];
|
|
924
|
+
|
|
925
|
+
if (!keepMap) {
|
|
926
|
+
// clean entry from to-do list
|
|
927
|
+
delete Util.changedKeysMap?.[this.definition.type]?.[
|
|
928
|
+
metadataEntry[this.definition.keyField]
|
|
929
|
+
];
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
707
934
|
/**
|
|
708
935
|
* Updates a single metadata entry via fuel-soap (generic lib not wrapper)
|
|
709
936
|
*
|
|
710
937
|
* @param {TYPE.MetadataTypeItem} metadataEntry single metadata entry
|
|
711
|
-
* @param {string} [overrideType] can be used if the API type differs from the otherwise used type identifier
|
|
712
938
|
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
713
939
|
* @returns {Promise.<object> | null} Promise of API response or null in case of an error
|
|
714
940
|
*/
|
|
715
|
-
static async updateSOAP(metadataEntry,
|
|
941
|
+
static async updateSOAP(metadataEntry, handleOutside) {
|
|
942
|
+
const soapType = this.definition.soapType || this.definition.type;
|
|
943
|
+
this.removeNotUpdateableFields(metadataEntry);
|
|
716
944
|
try {
|
|
717
|
-
this.removeNotUpdateableFields(metadataEntry);
|
|
718
945
|
const response = await this.client.soap.update(
|
|
719
|
-
|
|
720
|
-
this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1),
|
|
946
|
+
soapType.charAt(0).toUpperCase() + soapType.slice(1),
|
|
721
947
|
metadataEntry,
|
|
722
948
|
null
|
|
723
949
|
);
|
|
@@ -728,6 +954,7 @@ class MetadataType {
|
|
|
728
954
|
} / ${metadataEntry[this.definition.nameField]}`
|
|
729
955
|
);
|
|
730
956
|
}
|
|
957
|
+
await this._postChangeKeyTasks(metadataEntry);
|
|
731
958
|
return response;
|
|
732
959
|
} catch (ex) {
|
|
733
960
|
this._handleSOAPErrors(ex, 'updating', metadataEntry, handleOutside);
|
|
@@ -766,19 +993,17 @@ class MetadataType {
|
|
|
766
993
|
*
|
|
767
994
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
768
995
|
* @param {TYPE.SoapRequestParams} [requestParams] required for the specific request (filter for example)
|
|
996
|
+
* @param {string|number} [singleRetrieve] key of single item to filter by
|
|
769
997
|
* @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true
|
|
770
998
|
* @returns {Promise.<TYPE.MetadataTypeMapObj>} Promise of item map
|
|
771
999
|
*/
|
|
772
|
-
static async retrieveSOAP(retrieveDir, requestParams, additionalFields) {
|
|
773
|
-
requestParams
|
|
774
|
-
const fields = this.getFieldNamesToRetrieve(additionalFields);
|
|
1000
|
+
static async retrieveSOAP(retrieveDir, requestParams, singleRetrieve, additionalFields) {
|
|
1001
|
+
requestParams ||= {};
|
|
1002
|
+
const fields = this.getFieldNamesToRetrieve(additionalFields, !retrieveDir);
|
|
1003
|
+
const soapType = this.definition.soapType || this.definition.type;
|
|
775
1004
|
let response;
|
|
776
1005
|
try {
|
|
777
|
-
response = await this.client.soap.retrieveBulk(
|
|
778
|
-
this.definition.type,
|
|
779
|
-
fields,
|
|
780
|
-
requestParams
|
|
781
|
-
);
|
|
1006
|
+
response = await this.client.soap.retrieveBulk(soapType, fields, requestParams);
|
|
782
1007
|
} catch (ex) {
|
|
783
1008
|
this._handleSOAPErrors(ex, 'retrieving');
|
|
784
1009
|
return {};
|
|
@@ -788,7 +1013,8 @@ class MetadataType {
|
|
|
788
1013
|
if (retrieveDir) {
|
|
789
1014
|
const savedMetadata = await this.saveResults(metadata, retrieveDir, null);
|
|
790
1015
|
Util.logger.info(
|
|
791
|
-
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})`
|
|
1016
|
+
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
1017
|
+
Util.getKeysString(singleRetrieve)
|
|
792
1018
|
);
|
|
793
1019
|
if (
|
|
794
1020
|
this.buObject &&
|
|
@@ -805,12 +1031,11 @@ class MetadataType {
|
|
|
805
1031
|
*
|
|
806
1032
|
* @param {string} retrieveDir Directory where retrieved metadata directory will be saved
|
|
807
1033
|
* @param {string} uri rest endpoint for GET
|
|
808
|
-
* @param {string} [overrideType] force a metadata type (mainly used for Folders)
|
|
809
1034
|
* @param {TYPE.TemplateMap} [templateVariables] variables to be replaced in the metadata
|
|
810
1035
|
* @param {string|number} [singleRetrieve] key of single item to filter by
|
|
811
1036
|
* @returns {Promise.<{metadata: (TYPE.MetadataTypeMap | TYPE.MetadataTypeItem), type: string}>} Promise of item map (single item for templated result)
|
|
812
1037
|
*/
|
|
813
|
-
static async retrieveREST(retrieveDir, uri,
|
|
1038
|
+
static async retrieveREST(retrieveDir, uri, templateVariables, singleRetrieve) {
|
|
814
1039
|
const response =
|
|
815
1040
|
this.definition.restPagination && !singleRetrieve
|
|
816
1041
|
? await this.client.rest.getBulk(uri, this.definition.restPageSize || 500)
|
|
@@ -831,19 +1056,18 @@ class MetadataType {
|
|
|
831
1056
|
const savedMetadata = await this.saveResults(
|
|
832
1057
|
results,
|
|
833
1058
|
retrieveDir,
|
|
834
|
-
|
|
1059
|
+
null,
|
|
835
1060
|
templateVariables
|
|
836
1061
|
);
|
|
837
1062
|
Util.logger.info(
|
|
838
|
-
`Downloaded: ${
|
|
839
|
-
|
|
840
|
-
})` + Util.getKeysString(singleRetrieve)
|
|
1063
|
+
`Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` +
|
|
1064
|
+
Util.getKeysString(singleRetrieve)
|
|
841
1065
|
);
|
|
842
1066
|
}
|
|
843
1067
|
|
|
844
1068
|
return {
|
|
845
1069
|
metadata: templateVariables ? Object.values(results)[0] : results,
|
|
846
|
-
type:
|
|
1070
|
+
type: this.definition.type,
|
|
847
1071
|
};
|
|
848
1072
|
}
|
|
849
1073
|
|
|
@@ -1535,8 +1759,8 @@ class MetadataType {
|
|
|
1535
1759
|
* @returns {string[] | void} formatted Error Message
|
|
1536
1760
|
*/
|
|
1537
1761
|
static checkForErrors(ex) {
|
|
1762
|
+
const errors = [];
|
|
1538
1763
|
if (ex?.response?.status >= 400 && ex?.response?.status < 600) {
|
|
1539
|
-
const errors = [];
|
|
1540
1764
|
if (ex.response.data.errors) {
|
|
1541
1765
|
for (const errMsg of ex.response.data.errors) {
|
|
1542
1766
|
errors.push(
|
|
@@ -1562,8 +1786,8 @@ class MetadataType {
|
|
|
1562
1786
|
}
|
|
1563
1787
|
Util.logger.debug(JSON.stringify(ex.config));
|
|
1564
1788
|
Util.logger.debug(JSON.stringify(ex.response.data));
|
|
1565
|
-
return errors;
|
|
1566
1789
|
}
|
|
1790
|
+
return errors;
|
|
1567
1791
|
}
|
|
1568
1792
|
|
|
1569
1793
|
/**
|
|
@@ -1593,38 +1817,44 @@ class MetadataType {
|
|
|
1593
1817
|
* clean up after deleting a metadata item
|
|
1594
1818
|
*
|
|
1595
1819
|
* @param {string} customerKey Identifier of metadata item
|
|
1596
|
-
* @
|
|
1820
|
+
* @param {string[]} [additionalExtensions] additional file extensions to delete on top of `${this.definition.type}-meta.json`
|
|
1821
|
+
* @returns {Promise.<void>} - Promise
|
|
1597
1822
|
*/
|
|
1598
|
-
static async postDeleteTasks(customerKey) {
|
|
1599
|
-
// delete local copy: retrieve/cred/bu/type/...json
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1823
|
+
static async postDeleteTasks(customerKey, additionalExtensions) {
|
|
1824
|
+
// delete local copy: retrieve/cred/bu/type/...json + whatever additional extensions were passed
|
|
1825
|
+
const extArr = [`${this.definition.type}-meta.json`, ...(additionalExtensions || [])];
|
|
1826
|
+
for (const ext of extArr) {
|
|
1827
|
+
const jsonFile = File.normalizePath([
|
|
1828
|
+
this.properties.directories.retrieve,
|
|
1829
|
+
this.buObject.credential,
|
|
1830
|
+
this.buObject.businessUnit,
|
|
1831
|
+
this.definition.type,
|
|
1832
|
+
`${customerKey}.${ext}`,
|
|
1833
|
+
]);
|
|
1834
|
+
await File.remove(jsonFile);
|
|
1835
|
+
}
|
|
1608
1836
|
}
|
|
1609
1837
|
|
|
1610
1838
|
/**
|
|
1611
1839
|
* Delete a data extension from the specified business unit
|
|
1612
1840
|
*
|
|
1613
1841
|
* @param {string} customerKey Identifier of metadata
|
|
1842
|
+
* @param {string} [overrideKeyField] optionally change the name of the key field if the api uses a different name
|
|
1614
1843
|
* @param {boolean} [handleOutside] if the API reponse is irregular this allows you to handle it outside of this generic method
|
|
1615
1844
|
* @returns {boolean} deletion success flag
|
|
1616
1845
|
*/
|
|
1617
|
-
static async deleteByKeySOAP(customerKey, handleOutside) {
|
|
1618
|
-
const
|
|
1619
|
-
|
|
1846
|
+
static async deleteByKeySOAP(customerKey, overrideKeyField, handleOutside) {
|
|
1847
|
+
const metadata = {};
|
|
1848
|
+
metadata[overrideKeyField || this.definition.keyField] = customerKey;
|
|
1849
|
+
const soapType = this.definition.soapType || this.definition.type;
|
|
1620
1850
|
try {
|
|
1621
|
-
this.client.soap.delete(
|
|
1622
|
-
|
|
1623
|
-
|
|
1851
|
+
await this.client.soap.delete(
|
|
1852
|
+
soapType.charAt(0).toUpperCase() + soapType.slice(1),
|
|
1853
|
+
metadata,
|
|
1624
1854
|
null
|
|
1625
1855
|
);
|
|
1626
1856
|
if (!handleOutside) {
|
|
1627
|
-
Util.logger.info(
|
|
1857
|
+
Util.logger.info(` - deleted ${this.definition.type}: ${customerKey}`);
|
|
1628
1858
|
}
|
|
1629
1859
|
this.postDeleteTasks(customerKey);
|
|
1630
1860
|
|
|
@@ -1653,12 +1883,10 @@ class MetadataType {
|
|
|
1653
1883
|
* @returns {boolean} deletion success flag
|
|
1654
1884
|
*/
|
|
1655
1885
|
static async deleteByKeyREST(url, key, handleOutside) {
|
|
1656
|
-
const keyObj = {};
|
|
1657
|
-
keyObj[this.definition.keyField] = key;
|
|
1658
1886
|
try {
|
|
1659
1887
|
await this.client.rest.delete(url);
|
|
1660
1888
|
if (!handleOutside) {
|
|
1661
|
-
Util.logger.info(
|
|
1889
|
+
Util.logger.info(` - deleted ${this.definition.type}: ${key}`);
|
|
1662
1890
|
}
|
|
1663
1891
|
this.postDeleteTasks(key);
|
|
1664
1892
|
|
|
@@ -1682,7 +1910,7 @@ class MetadataType {
|
|
|
1682
1910
|
* @returns {object} Metadata of BU in local directory
|
|
1683
1911
|
*/
|
|
1684
1912
|
static readBUMetadataForType(readDir, listBadKeys, buMetadata) {
|
|
1685
|
-
buMetadata
|
|
1913
|
+
buMetadata ||= {};
|
|
1686
1914
|
readDir = File.normalizePath([readDir, this.definition.type]);
|
|
1687
1915
|
try {
|
|
1688
1916
|
if (File.pathExistsSync(readDir)) {
|