mcdev
Version:
Accenture Salesforce Marketing Cloud DevTools
419 lines (401 loc) • 16.5 kB
JavaScript
;
import MetadataType from './MetadataType.js';
import { Util } from '../util/util.js';
import cache from '../util/cache.js';
/**
* @typedef {import('../../types/mcdev.d.js').BuObject} BuObject
* @typedef {import('../../types/mcdev.d.js').CodeExtract} CodeExtract
* @typedef {import('../../types/mcdev.d.js').CodeExtractItem} CodeExtractItem
* @typedef {import('../../types/mcdev.d.js').MetadataTypeItem} MetadataTypeItem
* @typedef {import('../../types/mcdev.d.js').MetadataTypeItemDiff} MetadataTypeItemDiff
* @typedef {import('../../types/mcdev.d.js').MetadataTypeItemObj} MetadataTypeItemObj
* @typedef {import('../../types/mcdev.d.js').MetadataTypeMap} MetadataTypeMap
* @typedef {import('../../types/mcdev.d.js').MetadataTypeMapObj} MetadataTypeMapObj
* @typedef {import('../../types/mcdev.d.js').SoapRequestParams} SoapRequestParams
* @typedef {import('../../types/mcdev.d.js').TemplateMap} TemplateMap
*/
/**
* MessageSendActivity MetadataType
*
* @augments MetadataType
*/
class EmailSend extends MetadataType {
/**
* Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata
*
* @param {string} [retrieveDir] Directory where retrieved metadata directory will be saved
* @param {void | string[]} [_] unused parameter
* @param {void | string[]} [__] unused parameter
* @param {string} [key] customer key of single item to retrieve
* @returns {Promise.<MetadataTypeMapObj>} Promise of metadata
*/
static retrieve(retrieveDir, _, __, key) {
/** @type {SoapRequestParams} */
let requestParams = {
filter: {
leftOperand: {
// somehow that parameter controls visible (non deleted?) email send activities
leftOperand: 'IsPlatformObject',
operator: 'equals',
rightOperand: false,
},
operator: 'AND',
rightOperand: {
leftOperand: 'Description',
operator: 'notEquals',
rightOperand: 'SFSendDefinition',
},
},
};
if (key) {
// move original filter down one level into rightOperand and add key filter into leftOperand
requestParams = {
filter: {
leftOperand: {
leftOperand: 'CustomerKey',
operator: 'equals',
rightOperand: key,
},
operator: 'AND',
rightOperand: requestParams.filter,
},
};
}
return super.retrieveSOAP(retrieveDir, requestParams, key);
}
/**
* Updates a single item
*
* @param {MetadataTypeItem} metadataItem a single item
* @returns {Promise} Promise
*/
static update(metadataItem) {
return super.updateSOAP(metadataItem);
}
/**
* Creates a single item
*
* @param {MetadataTypeItem} metadataItem a single item
* @returns {Promise} Promise
*/
static create(metadataItem) {
return super.createSOAP(metadataItem);
}
/**
* Delete a metadata item from the specified business unit
*
* @param {string} key Identifier of data extension
* @returns {Promise.<boolean>} deletion success status
*/
static deleteByKey(key) {
return super.deleteByKeySOAP(key, undefined, 42002);
}
/**
* prepares a single item for deployment
*
* @param {MetadataTypeItem} metadata a single script activity definition
* @returns {Promise.<MetadataTypeItem>} Promise
*/
static async preDeployTasks(metadata) {
// re-add IsPlatformObject, required for visibility
metadata.IsPlatformObject = false;
// folder
super.setFolderId(metadata);
// email; in case we still have Email.ID, keep it but warn
metadata.Email ||= {};
if (metadata.r__email_name) {
// classic
metadata.Email.ID = cache.searchForField('email', metadata.r__email_name, 'Name', 'ID');
delete metadata.r__email_name;
} else if (metadata.r__asset_key) {
// content builder
// * this ignores r__asset_name_readOnly on purpose\ as that is only unique per parent folder but useful during PR reviews
// will try to find the key with the bu mid at the end, if unable, will try to find the key without it
try {
// check asset key as provided
metadata.Email.ID = cache.searchForField(
'asset',
metadata.r__asset_key,
'customerKey',
'legacyData.legacyId'
);
delete metadata.r__asset_key;
delete metadata.r__asset_name_readOnly;
} catch {
// if we deploy to another BU, try applying the BU's MID to the end, which we do in preDeployTasks for assets
// get suffix to update customer key at the end
const suffix = '-' + this.buObject.mid;
metadata.Email.ID = cache.searchForField(
'asset',
metadata.r__asset_key.slice(0, Math.max(0, 36 - suffix.length)) + suffix,
'customerKey',
'legacyData.legacyId'
);
delete metadata.r__asset_key;
delete metadata.r__asset_name_readOnly;
}
} else if (metadata.Email.ID) {
Util.logger.warn(
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
metadata[this.definition.keyField]
}): Email.ID was provided manually in your deployment file. We recommend using r__asset_key instead.`
);
try {
// content builder - test only
cache.searchForField(
'asset',
metadata.Email.ID,
'legacyData.legacyId',
'customerKey'
);
} catch {
try {
// classic - test only
cache.searchForField('email', metadata.Email.ID, 'ID', 'Name');
} catch {
throw new Error(
` ☇ skipping ${this.definition.type} ${metadata.Name} (${metadata.CustomerKey}): Could not find email with ID ${metadata.Email.ID} in Content Builder or Classic Emails.`
);
}
}
}
// Target Audience DataExtension
// normalize first because this can be an array
if (!metadata.SendDefinitionList) {
metadata.SendDefinitionList = [];
} else if (!Array.isArray(metadata.SendDefinitionList)) {
metadata.SendDefinitionList = [metadata.SendDefinitionList];
}
// Target Audience source
// - DataSourceTypeID=CustomObject --> DataExtension is source; list is also defined
// - DataSourceTypeID=List --> List is source; DE is not defined
for (const sdl of metadata.SendDefinitionList) {
// get DataExtension (optional)
if (sdl.r__dataExtension_key) {
if (sdl.DataSourceTypeID !== 'CustomObject') {
throw new Error(
`Expecting DataSourceTypeID to equal 'CustomObject' when r__dataExtension_key is defined; Found '${sdl.DataSourceTypeID}'`
);
}
sdl.CustomObjectID = cache.searchForField(
'dataExtension',
sdl.r__dataExtension_key,
'CustomerKey',
'ObjectID'
);
delete sdl.r__dataExtension_key;
} else if (sdl.DataSourceTypeID === 'CustomObject') {
throw new Error(
`Expecting r__dataExtension_key to be defined if DataSourceTypeID='CustomObject'`
);
}
if (!sdl.SalesForceObjectID || sdl.SalesForceObjectID === '') {
// otherwise this causes error 42117 / invalid ObjectID
delete sdl.SalesForceObjectID;
}
// get List (required)
if (sdl.r__list_PathName) {
sdl.List = {
ID: cache.getListObjectId(sdl.r__list_PathName, 'ID'),
};
delete sdl.r__list_PathName;
} else if (sdl.SendDefinitionListType === 'SourceList') {
// dont throw an error for type 'ExclusionList'
throw new Error(
`Field SendDefinitionList.r__list_PathName was not defined. Please try re-retrieving this ESD from your source BU.`
);
}
}
// sender profile
if (metadata.r__senderProfile_key) {
cache.searchForField(
'senderProfile',
metadata.r__senderProfile_key,
'CustomerKey',
'CustomerKey'
);
metadata.SenderProfile = {
CustomerKey: metadata.r__senderProfile_key,
};
delete metadata.r__senderProfile_key;
}
// send classification
if (metadata.r__sendClassification_key) {
cache.searchForField(
'sendClassification',
metadata.r__sendClassification_key,
'CustomerKey',
'CustomerKey'
);
metadata.SendClassification = {
CustomerKey: metadata.r__sendClassification_key,
};
delete metadata.r__sendClassification_key;
}
// delivery profile
if (metadata.r__deliveryProfile_key) {
cache.searchForField('deliveryProfile', metadata.r__deliveryProfile_key, 'key', 'key');
metadata.DeliveryProfile = {
CustomerKey: metadata.r__deliveryProfile_key,
};
delete metadata.r__deliveryProfile_key;
}
return metadata;
}
/**
* manages post retrieve steps
*
* @param {MetadataTypeItem} metadata a single query
* @returns {MetadataTypeItem} Array with one metadata object and one query string
*/
static postRetrieveTasks(metadata) {
// remove IsPlatformObject, always has to be 'false'
delete metadata.IsPlatformObject;
// folder
super.setFolderPath(metadata);
// email
if (metadata.Email?.ID) {
try {
// content builder
const contentBuilderEmailName = cache.searchForField(
'asset',
metadata.Email.ID,
'legacyData.legacyId',
'name'
);
metadata.r__asset_name_readOnly = contentBuilderEmailName;
const contentBuilderEmailKey = cache.searchForField(
'asset',
metadata.Email.ID,
'legacyData.legacyId',
'customerKey'
);
metadata.r__asset_key = contentBuilderEmailKey;
delete metadata.Email;
} catch {
try {
// classic
const classicEmail = cache.searchForField(
'email',
metadata.Email.ID,
'ID',
'Name'
);
metadata.r__email_name = classicEmail;
delete metadata.Email;
} catch {
Util.logger.warn(
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
metadata[this.definition.keyField]
}): Could not find email with ID ${
metadata.Email.ID
} Content Builder or Classic Emails.`
);
}
}
}
// Target Audience DataExtension
// normalize first because this can be an array
if (!metadata.SendDefinitionList) {
metadata.SendDefinitionList = [];
} else if (!Array.isArray(metadata.SendDefinitionList)) {
metadata.SendDefinitionList = [metadata.SendDefinitionList];
}
// Target Audience DataExtension
for (const sdl of metadata.SendDefinitionList) {
// get DataExtension keys
if (sdl.CustomObjectID) {
try {
sdl.r__dataExtension_key = cache.searchForField(
'dataExtension',
sdl.CustomObjectID,
'ObjectID',
'CustomerKey'
);
delete sdl.CustomObjectID;
} catch {
Util.logger.warn(
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
metadata[this.definition.keyField]
}): Could not find Target Audience (DataExtension) with ObjectID ${
sdl.CustomObjectID
}.`
);
}
}
// List
if (sdl.List?.ID) {
try {
sdl.r__list_PathName = cache.getListPathName(sdl.List.ID, 'ID');
delete sdl.List;
} catch (ex) {
Util.logger.warn(
` - ${this.definition.type} ${metadata[this.definition.nameField]} (${
metadata[this.definition.keyField]
}): ${ex.message}`
);
}
}
if (!sdl.SalesForceObjectID) {
// otherwise this causes error 42117 / invalid ObjectID
delete sdl.SalesForceObjectID;
}
}
// sender profile
if (metadata.SenderProfile?.CustomerKey) {
try {
cache.searchForField(
'senderProfile',
metadata.SenderProfile.CustomerKey,
'CustomerKey',
'CustomerKey'
);
metadata.r__senderProfile_key = metadata.SenderProfile.CustomerKey;
delete metadata.SenderProfile;
} catch (ex) {
Util.logger.warn(
` - ${this.definition.type} ${metadata.CustomerKey}: ${ex.message}`
);
}
}
// send classification
if (metadata.SendClassification?.CustomerKey) {
try {
cache.searchForField(
'sendClassification',
metadata.SendClassification.CustomerKey,
'CustomerKey',
'CustomerKey'
);
metadata.r__sendClassification_key = metadata.SendClassification.CustomerKey;
delete metadata.SendClassification;
} catch (ex) {
Util.logger.warn(
` - ${this.definition.type} ${metadata.CustomerKey}: ${ex.message}`
);
}
}
// delivery profile
if (metadata.DeliveryProfile?.CustomerKey) {
try {
cache.searchForField(
'deliveryProfile',
metadata.DeliveryProfile.CustomerKey,
'key',
'key'
);
metadata.r__deliveryProfile_key = metadata.DeliveryProfile.CustomerKey;
delete metadata.DeliveryProfile;
} catch (ex) {
Util.logger.warn(
` - ${this.definition.type} ${metadata.CustomerKey}: ${ex.message}`
);
}
}
return metadata;
}
}
// Assign definition to static attributes
import MetadataTypeDefinitions from '../MetadataTypeDefinitions.js';
EmailSend.definition = MetadataTypeDefinitions.emailSend;
export default EmailSend;