Skip to content

Commit

Permalink
Merge pull request #1315 from Accenture/feature/1314-resolve-email-te…
Browse files Browse the repository at this point in the history
…mplates

Feature/1314 resolve email templates
  • Loading branch information
JoernBerkefeld authored May 13, 2024
2 parents aa4bc9a + 06daff7 commit 76e8860
Show file tree
Hide file tree
Showing 14 changed files with 739 additions and 56 deletions.
108 changes: 108 additions & 0 deletions lib/metadataTypes/Asset.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ class Asset extends MetadataType {
*/
static async retrieve(retrieveDir, _, subTypeArr, key) {
const items = [];
if (subTypeArr) {
// check if elements in subTypeArr exist in this.definition.subTypes
const invalidSubTypes = subTypeArr.filter(
(subType) => !this.definition.subTypes.includes(subType)
);
if (invalidSubTypes.length) {
throw new Error(`Invalid subType(s) found: ${invalidSubTypes.join(', ')}`);
}
}
subTypeArr ||= this._getSubTypes();
if (retrieveDir) {
await File.initPrettier();
Expand All @@ -62,6 +71,44 @@ class Asset extends MetadataType {
}
return { metadata: metadata, type: this.definition.type };
}
/**
* Helper for writing Metadata to disk, used for Retrieve and deploy
*
* @param {MetadataTypeMap} results metadata results from deploy
* @param {string} retrieveDir directory where metadata should be stored after deploy/retrieve
* @param {string} [overrideType] for use when there is a subtype (such as folder-queries)
* @param {TemplateMap} [templateVariables] variables to be replaced in the metadata
* @returns {Promise.<MetadataTypeMap>} Promise of saved metadata
*/
static async saveResults(results, retrieveDir, overrideType, templateVariables) {
if (Object.keys(results).length) {
// only execute the following if records were found
await this._postRetrieveTasksBulk(results);
}
return super.saveResults(results, retrieveDir, overrideType, templateVariables);
}
/**
* helper for Journey's {@link Asset.saveResults}. Gets executed after retreive of metadata type and
*
* @param {MetadataTypeMap} metadataMap key=customer key, value=metadata
*/
static async _postRetrieveTasksBulk(metadataMap) {
// Template-Based Email
let needTemplates = false;
for (const key in metadataMap) {
if (metadataMap[key].assetType.name == 'templatebasedemail') {
needTemplates = true;
break;
}
}

if (needTemplates && !cache.getCache()?.asset) {
// for
Util.logger.info(' - Caching dependent Metadata: asset-template');
const result = await this.retrieveForCache(undefined, ['template']);
cache.setMetadata('asset', result.metadata);
}
}

/**
* Retrieves asset metadata for caching
Expand Down Expand Up @@ -514,6 +561,34 @@ class Asset extends MetadataType {
static postRetrieveTasks(metadata) {
// folder
this.setFolderPath(metadata);

// template-based emails
if (metadata.assetType.name === 'templatebasedemail') {
// get template
try {
if (metadata.views?.html?.template?.id) {
metadata.views.html.template.r__assetTemplate_Key = cache.searchForField(
'asset',
metadata.views?.html?.template?.id,
'id',
'customerKey'
);
delete metadata.views.html.template.id;
delete metadata.views.html.template.name;
delete metadata.views.html.template.assetType;
delete metadata.views.html.template.availableViews;
delete metadata.views.html.template.data;
delete metadata.views.html.template.modelVersion;
}
} catch {
Util.logger.warn(
` - ${this.definition.type} '${metadata[this.definition.nameField]}' (${
metadata[this.definition.keyField]
}): Could not find email template with id (${metadata.views.html.template.id})`
);
}
}

// extract HTML for selected subtypes and convert payload for easier processing in MetadataType.saveResults()
return this._extractCode(metadata);
}
Expand Down Expand Up @@ -606,6 +681,39 @@ class Asset extends MetadataType {
// folder
this.setFolderId(metadata);

// template-based emails
if (
metadata.assetType.name === 'templatebasedemail' &&
metadata.views?.html?.template?.r__assetTemplate_Key
) {
// template
metadata.views.html.template.id = cache.searchForField(
'asset',
metadata.views.html.template.r__assetTemplate_Key,
'customerKey',
'id'
);
metadata.views.html.template.name = cache.searchForField(
'asset',
metadata.views.html.template.r__assetTemplate_Key,
'customerKey',
'name'
);
metadata.views.html.template.assetType = {
id: 4,
name: 'template',
displayName: 'Template',
};

metadata.views.html.template.data = {
email: { options: {} },
};

metadata.views.html.template.availableViews = [];
metadata.views.html.template.modelVersion = 2;
delete metadata.views.html.template.r__assetTemplate_Key;
}

// restore asset type id which is needed for deploy
metadata.assetType.id = this.definition.typeMapping[metadata.assetType.name];

Expand Down
56 changes: 55 additions & 1 deletion test/resourceFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export const handleRESTRequest = async (config) => {
config.baseURL + (config.url.startsWith('/') ? config.url.slice(1) : config.url)
);
let filterName;
let filterBody;
if (urlObj.searchParams.get('$filter')) {
filterName = urlObj.searchParams.get('$filter').split(' eq ')[1];
}
Expand All @@ -256,6 +257,18 @@ export const handleRESTRequest = async (config) => {
'-' +
urlObj.searchParams.get('$filter').replaceAll(' eq ', '=').replaceAll(' ', '')
: null;

if (!testPathFilter && config.method === 'post' && config.data) {
const simpleOperators = { equal: '=', in: 'IN' };
const data = JSON.parse(config.data);
const myObj = data.query?.rightOperand || data.query;
if (myObj) {
const op = simpleOperators[myObj.simpleOperator];
filterBody = `${myObj.property}${op}${op === 'IN' ? myObj.value.join(',') : myObj.value}`;
}
}
const testPathFilterBody = filterBody ? testPath + '-' + filterBody : null;

if (testPathFilter && (await fs.pathExists(testPathFilter + '.json'))) {
// build filter logic to ensure templating works
if (filterName) {
Expand All @@ -282,6 +295,20 @@ export const handleRESTRequest = async (config) => {
encoding: 'utf8',
}),
];
} else if (testPathFilterBody && (await fs.pathExists(testPathFilterBody + '.json'))) {
return [
200,
await fs.readFile(testPathFilterBody + '.json', {
encoding: 'utf8',
}),
];
} else if (testPathFilterBody && (await fs.pathExists(testPathFilterBody + '.txt'))) {
return [
200,
await fs.readFile(testPathFilterBody + '.txt', {
encoding: 'utf8',
}),
];
} else if (await fs.pathExists(testPath + '.json')) {
if (testPathFilter) {
/* eslint-disable no-console */
Expand All @@ -297,6 +324,20 @@ export const handleRESTRequest = async (config) => {
/* eslint-enable no-console */
}

if (testPathFilterBody) {
/* eslint-disable no-console */
console.log(
`${color.bgYellow}${color.fgBlack}TEST-WARNING${
color.reset
}: You are loading your reponse from ${
testPath + '.json'
} instead of the more specific ${
testPathFilterBody + '.json'
}. Make sure this is intended`
);
/* eslint-enable no-console */
}

// build filter logic to ensure templating works
if (filterName) {
const response = JSON.parse(
Expand Down Expand Up @@ -329,6 +370,19 @@ export const handleRESTRequest = async (config) => {
);
/* eslint-enable no-console */
}
if (testPathFilterBody) {
/* eslint-disable no-console */
console.log(
`${color.bgYellow}${color.fgBlack}TEST-WARNING${
color.reset
}: You are loading your reponse from ${
testPath + '.txt'
} instead of the more specific ${
testPathFilterBody + '.txt'
}. Make sure this is intended`
);
/* eslint-enable no-console */
}

return [
200,
Expand All @@ -339,7 +393,7 @@ export const handleRESTRequest = async (config) => {
} else {
/* eslint-disable no-console */
console.log(
`${color.bgRed}${color.fgBlack}TEST-ERROR${color.reset}: Please create file ${testPath}.json/.txt${filterName ? ` or ${testPathFilter}.json/.txt` : ''}`
`${color.bgRed}${color.fgBlack}TEST-ERROR${color.reset}: Please create file ${testPath}.json/.txt${filterName ? ` or ${testPathFilter}.json/.txt` : testPathFilterBody ? ` or ${testPathFilterBody}.json/.txt` : ''}`
);
/* eslint-enable no-console */
process.exitCode = 404;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"customerKey": "testExisting_asset_templatebasedemail",
"contentType": "application/vnd.etmc.email.Message; kind=template",
"assetType": { "name": "templatebasedemail", "displayName": "Template-Based Email" },
"name": " testExisting_asset_templatebasedemail",
"owner": { "email": "joern.berkefeld@accenture.com", "name": "Jörn Berkefeld" },
"createdDate": "2024-04-22T06:36:03.117-06:00",
"createdBy": { "email": "joern.berkefeld@accenture.com", "name": "Jörn Berkefeld" },
"modifiedDate": "2024-05-13T07:15:29.77-06:00",
"modifiedBy": { "name": "IDE - joern.berkefeld app user" },
"memberId": 9999999,
"status": { "name": "Draft" },
"views": {
"subjectline": {
"contentType": "application/vnd.etmc.email.View; kind=subjectline",
"thumbnail": {},
"content": "my subject line",
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"preheader": {
"contentType": "application/vnd.etmc.email.View; kind=preheader",
"thumbnail": {},
"content": "my custom preheader text",
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"text": {
"thumbnail": {},
"availableViews": [],
"data": { "email": { "options": { "generateFrom": "html" } } },
"generateFrom": "html",
"modelVersion": 2
},
"viewAsAWebPage": {
"thumbnail": {},
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"subscriptioncenter": {
"thumbnail": {},
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"forwardHTML": {
"thumbnail": {},
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"forwardText": {
"thumbnail": {},
"availableViews": [],
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
},
"html": {
"thumbnail": {},
"availableViews": [],
"template": {
"meta": { "contentHash": -166410261 },
"r__assetTemplate_Key": "testExisting_asset_template"
},
"data": { "email": { "options": { "generateFrom": null } } },
"modelVersion": 2
}
},
"availableViews": [
"subjectline",
"preheader",
"text",
"viewAsAWebPage",
"subscriptioncenter",
"forwardHTML",
"forwardText",
"html"
],
"data": { "email": { "options": { "characterEncoding": "utf-8" } } },
"modelVersion": 2,
"r__folder_Path": "Content Builder"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"id": 5286,
"customerKey": "testExisting_asset_template",
"objectID": "50855339-3f87-47a7-8677-a8c63eb36a43",
"assetType": {
"id": 4,
"name": "template",
"displayName": "Template"
},
"fileProperties": {
"fileName": " testExisting_asset_template"
},
"name": " testExisting_asset_template",
"owner": {
"id": 710411605,
"email": "joern.berkefeld@accenture.com",
"name": "Jörn Berkefeld",
"userId": "710411605"
},
"createdDate": "2024-04-22T03:33:08.253-06:00",
"createdBy": {
"id": 710411605,
"email": "joern.berkefeld@accenture.com",
"name": "Jörn Berkefeld",
"userId": "710411605"
},
"modifiedDate": "2024-05-13T07:12:13.117-06:00",
"modifiedBy": {
"id": 710420432,
"name": "joern.berkefeld app user",
"userId": "710420432"
},
"enterpriseId": 510004860,
"memberId": 510009653,
"status": {
"id": 1,
"name": "Draft"
},
"thumbnail": {
"thumbnailUrl": "/v1/assets/5286/thumbnail"
},
"category": {
"id": 89397
},
"content": "<a href=\"%%ftaf_url%%\"\n ><img\n src=\"https://www.exacttarget.com/members/newsletters/gfx/forwardafriendicon.gif\"\n border=\"0\" /></a\n><a href=\"%%profile_center_url%%\" alias=\"Update Profile\">Update Profile</a>\n<table cellpadding=\"2\" cellspacing=\"0\" width=\"600\" id=\"Table5\" border=\"0\">\n <tr>\n <td>\n <font face=\"verdana\" size=\"1\" color=\"#444444\"\n >This email was sent by: <b>%%Member_Busname%%</b><br />%%Member_Addr%%\n %%Member_City%%, %%Member_State%%, %%Member_PostalCode%%, %%Member_Country%%<br /><br\n /></font>\n </td>\n </tr>\n</table>\n<a href=\"%%subscription_center_url%%\" alias=\"Manage Subscriptions\">Manage Subscriptions</a\n><img src=\"https://www.exacttarget.com/images/Powered_By_1206.jpg\" border=\"0\" /><custom\n name=\"opencounter\"\n type=\"tracking\"\n/>\n",
"availableViews": [],
"modelVersion": 2
}
Loading

0 comments on commit 76e8860

Please sign in to comment.