diff --git a/docs/dist/documentation.md b/docs/dist/documentation.md index b8d23f92e..eba3ef196 100644 --- a/docs/dist/documentation.md +++ b/docs/dist/documentation.md @@ -821,7 +821,7 @@ FileTransfer MetadataType * [._retrieveExtendedFile(metadata, subType, retrieveDir)](#Asset._retrieveExtendedFile) ⇒ Promise.<void> * [._readExtendedFileFromFS(metadata, subType, deployDir, [pathOnly])](#Asset._readExtendedFileFromFS) ⇒ Promise.<string> * [.postRetrieveTasks(metadata)](#Asset.postRetrieveTasks) ⇒ TYPE.CodeExtractItem - * [.postDeployTasks(metadata, _, [isRefresh])](#Asset.postDeployTasks) ⇒ Promise.<void> + * [.postDeployTasks(metadata, _, createdUpdated, [isRefresh])](#Asset.postDeployTasks) ⇒ Promise.<void> * [.preDeployTasks(metadata, deployDir)](#Asset.preDeployTasks) ⇒ Promise.<TYPE.AssetItem> * [._getMainSubtype(extendedSubType)](#Asset._getMainSubtype) ⇒ string * [.buildDefinitionForNested(templateDir, targetDir, metadata, templateVariables, templateName)](#Asset.buildDefinitionForNested) ⇒ Promise.<void> @@ -982,7 +982,7 @@ manages post retrieve steps -### Asset.postDeployTasks(metadata, _, [isRefresh]) ⇒ Promise.<void> +### Asset.postDeployTasks(metadata, _, createdUpdated, [isRefresh]) ⇒ Promise.<void> Gets executed after deployment of metadata type **Kind**: static method of [Asset](#Asset) @@ -992,6 +992,7 @@ Gets executed after deployment of metadata type | --- | --- | --- | | metadata | TYPE.MetadataTypeMap | metadata mapped by their keyField | | _ | TYPE.MetadataTypeMap | originalMetadata to be updated (contains additioanl fields) | +| createdUpdated | Object | counter representing successful creates/updates | | [isRefresh] | boolean | optional flag to indicate that triggeredSend should be refreshed after deployment of assets | @@ -1260,7 +1261,7 @@ Automation MetadataType * [.retrieveForCache()](#Automation.retrieveForCache) ⇒ Promise.<TYPE.AutomationMapObj> * [.retrieveAsTemplate(templateDir, name, templateVariables)](#Automation.retrieveAsTemplate) ⇒ Promise.<TYPE.AutomationItemObj> * [.postRetrieveTasks(metadata)](#Automation.postRetrieveTasks) ⇒ TYPE.AutomationItem - * [.deploy(metadata, targetBU, retrieveDir)](#Automation.deploy) ⇒ Promise.<TYPE.AutomationMap> + * [.deploy(metadata, targetBU, retrieveDir, [isRefresh])](#Automation.deploy) ⇒ Promise.<TYPE.AutomationMap> * [.create(metadata)](#Automation.create) ⇒ Promise * [.update(metadata, metadataBefore)](#Automation.update) ⇒ Promise * [.preDeployTasks(metadata)](#Automation.preDeployTasks) ⇒ Promise.<TYPE.AutomationItem> @@ -1331,7 +1332,7 @@ manages post retrieve steps -### Automation.deploy(metadata, targetBU, retrieveDir) ⇒ Promise.<TYPE.AutomationMap> +### Automation.deploy(metadata, targetBU, retrieveDir, [isRefresh]) ⇒ Promise.<TYPE.AutomationMap> Deploys automation - the saved file is the original one due to large differences required for deployment **Kind**: static method of [Automation](#Automation) @@ -1342,6 +1343,7 @@ Deploys automation - the saved file is the original one due to large differences | metadata | TYPE.AutomationMap | metadata mapped by their keyField | | targetBU | string | name/shorthand of target businessUnit for mapping | | retrieveDir | string | directory where metadata after deploy should be saved | +| [isRefresh] | boolean | optional flag - so far not used by automation | @@ -1610,7 +1612,7 @@ DataExtension MetadataType * [._filterUpsertResults(res)](#DataExtension._filterUpsertResults) ⇒ boolean * [.create(metadata)](#DataExtension.create) ⇒ Promise * [.update(metadata)](#DataExtension.update) ⇒ Promise - * [.postDeployTasks(upsertedMetadata, originalMetadata)](#DataExtension.postDeployTasks) ⇒ void + * [.postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated)](#DataExtension.postDeployTasks) ⇒ void * [.retrieve(retrieveDir, [additionalFields], [_], [key])](#DataExtension.retrieve) ⇒ Promise.<{metadata: TYPE.DataExtensionMap, type: string}> * [.retrieveChangelog([additionalFields])](#DataExtension.retrieveChangelog) ⇒ Promise.<{metadata: TYPE.DataExtensionMap, type: string}> * [.postRetrieveTasks(metadata)](#DataExtension.postRetrieveTasks) ⇒ TYPE.DataExtensionItem @@ -1674,7 +1676,7 @@ Updates a single dataExtension. Also updates their columns in 'dataExtension.col -### DataExtension.postDeployTasks(upsertedMetadata, originalMetadata) ⇒ void +### DataExtension.postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated) ⇒ void Gets executed after deployment of metadata type **Kind**: static method of [DataExtension](#DataExtension) @@ -1683,6 +1685,7 @@ Gets executed after deployment of metadata type | --- | --- | --- | | upsertedMetadata | TYPE.DataExtensionMap | metadata mapped by their keyField | | originalMetadata | TYPE.DataExtensionMap | metadata to be updated (contains additioanl fields) | +| createdUpdated | Object | counter representing successful creates/updates | @@ -2331,7 +2334,7 @@ EventDefinition MetadataType * [.retrieveAsTemplate(templateDir, name, templateVariables)](#EventDefinition.retrieveAsTemplate) ⇒ Promise.<TYPE.MetadataTypeItemObj> * [.postRetrieveTasks(eventDef)](#EventDefinition.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.deleteByKey(key)](#EventDefinition.deleteByKey) ⇒ Promise.<boolean> - * [.deploy(metadata, deployDir, retrieveDir)](#EventDefinition.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> + * [.deploy(metadata, deployDir, retrieveDir, [isRefresh])](#EventDefinition.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> * [.create(EventDefinition)](#EventDefinition.create) ⇒ Promise * [.update(metadataEntry)](#EventDefinition.update) ⇒ Promise * [.preDeployTasks(metadata)](#EventDefinition.preDeployTasks) ⇒ TYPE.MetadataTypeItem @@ -2401,7 +2404,7 @@ Delete a metadata item from the specified business unit -### EventDefinition.deploy(metadata, deployDir, retrieveDir) ⇒ Promise.<TYPE.MetadataTypeMap> +### EventDefinition.deploy(metadata, deployDir, retrieveDir, [isRefresh]) ⇒ Promise.<TYPE.MetadataTypeMap> Deploys metadata - merely kept here to be able to print [logBeta](#Util.logBeta) once per deploy **Kind**: static method of [EventDefinition](#EventDefinition) @@ -2412,6 +2415,7 @@ Deploys metadata - merely kept here to be able to print [logBeta](#Util.logBeta) | metadata | TYPE.MetadataTypeMap | metadata mapped by their keyField | | deployDir | string | directory where deploy metadata are saved | | retrieveDir | string | directory where metadata after deploy should be saved | +| [isRefresh] | boolean | optional flag - so far not used by eventDefinition | @@ -2918,7 +2922,7 @@ definitionId: A unique UUID provided by Salesforce Marketing Cloud. Each version * [Interaction](#Interaction) ⇐ [MetadataType](#MetadataType) * [.retrieve(retrieveDir, [_], [__], [key])](#Interaction.retrieve) ⇒ Promise.<TYPE.MetadataTypeMapObj> * [.deleteByKey(key)](#Interaction.deleteByKey) ⇒ Promise.<boolean> - * [.deploy(metadata, deployDir, retrieveDir)](#Interaction.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> + * [.deploy(metadata, deployDir, retrieveDir, [isRefresh])](#Interaction.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> * [.update(metadata)](#Interaction.update) ⇒ Promise * [.create(metadata)](#Interaction.create) ⇒ Promise * [.saveResults(results, retrieveDir, [overrideType], [templateVariables])](#Interaction.saveResults) ⇒ Promise.<TYPE.MetadataTypeMap> @@ -2956,7 +2960,7 @@ Delete a metadata item from the specified business unit -### Interaction.deploy(metadata, deployDir, retrieveDir) ⇒ Promise.<TYPE.MetadataTypeMap> +### Interaction.deploy(metadata, deployDir, retrieveDir, [isRefresh]) ⇒ Promise.<TYPE.MetadataTypeMap> Deploys metadata - merely kept here to be able to print [logBeta](#Util.logBeta) once per deploy **Kind**: static method of [Interaction](#Interaction) @@ -2967,6 +2971,7 @@ Deploys metadata - merely kept here to be able to print [logBeta](#Util.logBeta) | metadata | TYPE.MetadataTypeMap | metadata mapped by their keyField | | deployDir | string | directory where deploy metadata are saved | | retrieveDir | string | directory where metadata after deploy should be saved | +| [isRefresh] | boolean | optional flag - so far not used by interaction | @@ -3147,7 +3152,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.getJsonFromFS(dir, [listBadKeys])](#MetadataType.getJsonFromFS) ⇒ TYPE.MetadataTypeMap * [.getFieldNamesToRetrieve([additionalFields])](#MetadataType.getFieldNamesToRetrieve) ⇒ Array.<string> * [.deploy(metadata, deployDir, retrieveDir, [isRefresh])](#MetadataType.deploy) ⇒ Promise.<TYPE.MetadataTypeMap> - * [.postDeployTasks(metadata, originalMetadata, [isRefresh])](#MetadataType.postDeployTasks) ⇒ void + * [.postDeployTasks(metadata, originalMetadata, createdUpdated, [isRefresh])](#MetadataType.postDeployTasks) ⇒ void * [.postRetrieveTasks(metadata, targetDir, [isTemplating])](#MetadataType.postRetrieveTasks) ⇒ TYPE.MetadataTypeItem * [.setFolderPath(metadata)](#MetadataType.setFolderPath) * [.setFolderId(metadata)](#MetadataType.setFolderId) @@ -3162,7 +3167,7 @@ Provides default functionality that can be overwritten by child metadata type cl * [.refresh()](#MetadataType.refresh) ⇒ void * [.hasChanged(cachedVersion, metadata, [fieldName])](#MetadataType.hasChanged) ⇒ boolean * [.hasChangedGeneric(cachedVersion, metadata, [fieldName], [silent])](#MetadataType.hasChangedGeneric) ⇒ boolean - * [.upsert(metadata, deployDir)](#MetadataType.upsert) ⇒ Promise.<TYPE.MetadataTypeMap> + * [.upsert(metadata, deployDir, [isRefresh])](#MetadataType.upsert) ⇒ Promise.<TYPE.MetadataTypeMap> * [.createOrUpdate(metadata, metadataKey, hasError, metadataToUpdate, metadataToCreate)](#MetadataType.createOrUpdate) ⇒ void * [.createREST(metadataEntry, uri)](#MetadataType.createREST) ⇒ Promise.<object> \| null * [.createSOAP(metadataEntry, [overrideType], [handleOutside])](#MetadataType.createSOAP) ⇒ Promise.<object> \| null @@ -3255,7 +3260,7 @@ Deploys metadata -### MetadataType.postDeployTasks(metadata, originalMetadata, [isRefresh]) ⇒ void +### MetadataType.postDeployTasks(metadata, originalMetadata, createdUpdated, [isRefresh]) ⇒ void Gets executed after deployment of metadata type **Kind**: static method of [MetadataType](#MetadataType) @@ -3264,6 +3269,7 @@ Gets executed after deployment of metadata type | --- | --- | --- | | metadata | TYPE.MetadataTypeMap | metadata mapped by their keyField | | originalMetadata | TYPE.MetadataTypeMap | metadata to be updated (contains additioanl fields) | +| createdUpdated | Object | counter representing successful creates/updates | | [isRefresh] | boolean | optional flag to indicate that triggeredSend should be refreshed after deployment of assets | @@ -3447,7 +3453,7 @@ test if metadata was actually changed or not to potentially skip it during deplo -### MetadataType.upsert(metadata, deployDir) ⇒ Promise.<TYPE.MetadataTypeMap> +### MetadataType.upsert(metadata, deployDir, [isRefresh]) ⇒ Promise.<TYPE.MetadataTypeMap> MetadataType upsert, after retrieving from target and comparing to check if create or update operation is needed. **Kind**: static method of [MetadataType](#MetadataType) @@ -3457,6 +3463,7 @@ MetadataType upsert, after retrieving from target and comparing to check if crea | --- | --- | --- | | metadata | TYPE.MetadataTypeMap | metadata mapped by their keyField | | deployDir | string | directory where deploy metadata are saved | +| [isRefresh] | boolean | optional flag to indicate that triggeredSend should be refreshed after deployment of assets | diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js index 23c133593..3bec97f20 100644 --- a/lib/metadataTypes/Asset.js +++ b/lib/metadataTypes/Asset.js @@ -500,12 +500,20 @@ class Asset extends MetadataType { * * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField * @param {TYPE.MetadataTypeMap} _ originalMetadata to be updated (contains additioanl fields) + * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates * @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets * @returns {Promise.} - */ - static async postDeployTasks(metadata, _, isRefresh) { + static async postDeployTasks(metadata, _, createdUpdated, isRefresh) { if (isRefresh) { - this._refreshTriggeredSendDefinition(metadata); + if (createdUpdated.updated) { + // only run this if assets were updated. for created assets we do not expect + this._refreshTriggeredSendDefinition(metadata); + } else { + Util.logger.warn( + 'You set the --refresh flag but no updated assets found. Skipping refresh of triggeredSendDefinitions.' + ); + } } } diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js index cc031c2d3..836f40b00 100644 --- a/lib/metadataTypes/Automation.js +++ b/lib/metadataTypes/Automation.js @@ -230,12 +230,11 @@ class Automation extends MetadataType { * @param {TYPE.AutomationMap} metadata metadata mapped by their keyField * @param {string} targetBU name/shorthand of target businessUnit for mapping * @param {string} retrieveDir directory where metadata after deploy should be saved + * @param {boolean} [isRefresh] optional flag - so far not used by automation * @returns {Promise.} Promise */ - static async deploy(metadata, targetBU, retrieveDir) { - const orignalMetadata = JSON.parse(JSON.stringify(metadata)); - const upsertResults = await this.upsert(metadata, targetBU); - await this.postDeployTasks(upsertResults, orignalMetadata); + static async deploy(metadata, targetBU, retrieveDir, isRefresh) { + const upsertResults = await this.upsert(metadata, targetBU, isRefresh); await this.saveResults(upsertResults, retrieveDir, null); return upsertResults; } diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js index d4779228e..e329e88e7 100644 --- a/lib/metadataTypes/DataExtension.js +++ b/lib/metadataTypes/DataExtension.js @@ -121,6 +121,7 @@ class DataExtension extends MetadataType { `${this.definition.type} upsert: ${createResults.length} of ${deCreatePromises.length} created / ${updateResults.length} of ${deUpdatePromises.length} updated` + (filteredByPreDeploy > 0 ? ` / ${filteredByPreDeploy} filtered` : '') ); + let upsertResults; if (successfulResults.length > 0) { const metadataResults = successfulResults .map((r) => r.value.Results[0].Object) @@ -133,10 +134,15 @@ class DataExtension extends MetadataType { } return r; }); - return super.parseResponseBody({ Results: metadataResults }); + upsertResults = super.parseResponseBody({ Results: metadataResults }); } else { - return {}; + upsertResults = {}; } + await this.postDeployTasks(upsertResults, desToDeploy, { + created: createResults.length, + updated: updateResults.length, + }); + return upsertResults; } /** @@ -247,12 +253,15 @@ class DataExtension extends MetadataType { * * @param {TYPE.DataExtensionMap} upsertedMetadata metadata mapped by their keyField * @param {TYPE.DataExtensionMap} originalMetadata metadata to be updated (contains additioanl fields) + * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates * @returns {void} */ - static postDeployTasks(upsertedMetadata, originalMetadata) { + static postDeployTasks(upsertedMetadata, originalMetadata, createdUpdated) { for (const key in upsertedMetadata) { const item = upsertedMetadata[key]; - const cachedVersion = cache.getByKey('dataExtension', item.CustomerKey); + const cachedVersion = createdUpdated.updated + ? cache.getByKey('dataExtension', item.CustomerKey) + : null; if (cachedVersion) { // UPDATE // restore retention values that are typically not returned by the update call diff --git a/lib/metadataTypes/EventDefinition.js b/lib/metadataTypes/EventDefinition.js index 9fdd2855b..7b5b92988 100644 --- a/lib/metadataTypes/EventDefinition.js +++ b/lib/metadataTypes/EventDefinition.js @@ -137,11 +137,12 @@ class EventDefinition extends MetadataType { * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField * @param {string} deployDir directory where deploy metadata are saved * @param {string} retrieveDir directory where metadata after deploy should be saved + * @param {boolean} [isRefresh] optional flag - so far not used by eventDefinition * @returns {Promise.} Promise of keyField => metadata map */ - static async deploy(metadata, deployDir, retrieveDir) { + static async deploy(metadata, deployDir, retrieveDir, isRefresh) { Util.logBeta(this.definition.type); - return super.deploy(metadata, deployDir, retrieveDir); + return super.deploy(metadata, deployDir, retrieveDir, isRefresh); } /** diff --git a/lib/metadataTypes/Folder.js b/lib/metadataTypes/Folder.js index 984cdda5b..a23d3c063 100644 --- a/lib/metadataTypes/Folder.js +++ b/lib/metadataTypes/Folder.js @@ -167,6 +167,7 @@ class Folder extends MetadataType { * @returns {Promise.} Promise of saved metadata */ static async upsert(metadata) { + const orignalMetadata = JSON.parse(JSON.stringify(metadata)); let updateCount = 0; let updateFailedCount = 0; let createCount = 0; @@ -318,6 +319,10 @@ class Folder extends MetadataType { ` - Folders are recognized for updates based on their CustomerKey or, if that is not given, their folder-path.` ); } + await this.postDeployTasks(upsertResults, orignalMetadata, { + created: createCount, + updated: updateCount, + }); return upsertResults; } diff --git a/lib/metadataTypes/Interaction.js b/lib/metadataTypes/Interaction.js index 7157723c0..531fb0a04 100644 --- a/lib/metadataTypes/Interaction.js +++ b/lib/metadataTypes/Interaction.js @@ -216,11 +216,12 @@ class Interaction extends MetadataType { * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField * @param {string} deployDir directory where deploy metadata are saved * @param {string} retrieveDir directory where metadata after deploy should be saved + * @param {boolean} [isRefresh] optional flag - so far not used by interaction * @returns {Promise.} Promise of keyField => metadata map */ - static async deploy(metadata, deployDir, retrieveDir) { + static async deploy(metadata, deployDir, retrieveDir, isRefresh) { Util.logBeta(this.definition.type); - return super.deploy(metadata, deployDir, retrieveDir); + return super.deploy(metadata, deployDir, retrieveDir, isRefresh); } /** diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 5ce729b70..79e23d68c 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -116,8 +116,7 @@ class MetadataType { * @returns {Promise.} Promise of keyField => metadata map */ static async deploy(metadata, deployDir, retrieveDir, isRefresh) { - const upsertResults = await this.upsert(metadata, deployDir); - await this.postDeployTasks(upsertResults, metadata, isRefresh); + const upsertResults = await this.upsert(metadata, deployDir, isRefresh); const savedMetadata = await this.saveResults(upsertResults, retrieveDir, null); if ( this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type) && @@ -135,10 +134,11 @@ class MetadataType { * * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField * @param {TYPE.MetadataTypeMap} originalMetadata metadata to be updated (contains additioanl fields) + * @param {{created: number, updated: number}} createdUpdated counter representing successful creates/updates * @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets * @returns {void} */ - static postDeployTasks(metadata, originalMetadata, isRefresh) {} + static postDeployTasks(metadata, originalMetadata, createdUpdated, isRefresh) {} /** * Gets executed after retreive of metadata type @@ -448,9 +448,11 @@ class MetadataType { * * @param {TYPE.MetadataTypeMap} metadata metadata mapped by their keyField * @param {string} deployDir directory where deploy metadata are saved + * @param {boolean} [isRefresh] optional flag to indicate that triggeredSend should be refreshed after deployment of assets * @returns {Promise.} keyField => metadata map */ - static async upsert(metadata, deployDir) { + static async upsert(metadata, deployDir, isRefresh) { + const orignalMetadata = JSON.parse(JSON.stringify(metadata)); const metadataToUpdate = []; const metadataToCreate = []; let filteredByPreDeploy = 0; @@ -520,7 +522,7 @@ class MetadataType { `${this.definition.type} upsert: ${createResults.length} of ${metadataToCreate.length} created / ${updateResults.length} of ${metadataToUpdate.length} updated` + (filteredByPreDeploy > 0 ? ` / ${filteredByPreDeploy} filtered` : '') ); - + let upsertResults; if (this.definition.bodyIteratorField === 'Results') { // if Results then parse as SOAP // put in Retrieve Format for parsing @@ -532,15 +534,22 @@ class MetadataType { .filter((r) => r !== undefined && r !== null && Object.keys(r).length !== 0) .flatMap((r) => r.Results) .map((r) => r.Object); - return this.parseResponseBody({ Results: metadataResults }); + upsertResults = this.parseResponseBody({ Results: metadataResults }); } else { // likely comming from one of the many REST APIs // put in Retrieve Format for parsing // todo add handling when response does not contain items. // @ts-ignore const metadataResults = createResults.concat(updateResults).filter(Boolean); - return this.parseResponseBody(metadataResults); + upsertResults = this.parseResponseBody(metadataResults); } + await this.postDeployTasks( + upsertResults, + orignalMetadata, + { created: createResults.length, updated: updateResults.length }, + isRefresh + ); + return upsertResults; } /**