mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Refactored email utils to a class with DI params
refs https://github.com/TryGhost/Toolbox/issues/292 - There's a need to reuse these utils in the version mismatch notification service. Having loads of tightly coupled dependencies makes it super hard to rip out this module for reuse - It's a groundwork for extraction of the email-utils package - Rewrote the unit tests that were written for these utils previously - they weren't testing anything useful. The goal of this util is to generate specific content based on provided data and available templates - now the tests do test those specific things, not the mailer itself!
This commit is contained in:
parent
499bb293c9
commit
551bd5e511
4 changed files with 103 additions and 74 deletions
55
core/server/services/mail/EmailContentGenerator.js
Normal file
55
core/server/services/mail/EmailContentGenerator.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
const _ = require('lodash').runInContext();
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const htmlToText = require('html-to-text');
|
||||||
|
|
||||||
|
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
|
||||||
|
|
||||||
|
class EmailContentGenerator {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {function} options.getSiteUrl
|
||||||
|
* @param {function} options.getSiteTitle
|
||||||
|
* @param {string} options.templatesDir - path to the directory containing email templates
|
||||||
|
*/
|
||||||
|
constructor({getSiteUrl, getSiteTitle, templatesDir}) {
|
||||||
|
this.getSiteUrl = getSiteUrl;
|
||||||
|
this.getSiteTitle = getSiteTitle;
|
||||||
|
this.templatesDir = templatesDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {string} options.template - HTML template name to use for generation
|
||||||
|
* @param {Object} [options.data] - variable data to use during HTML template compilation
|
||||||
|
* @returns {Promise} resolves with an object containing html and text properties
|
||||||
|
*/
|
||||||
|
async getContent(options) {
|
||||||
|
const defaults = {
|
||||||
|
siteUrl: this.getSiteUrl(),
|
||||||
|
siteTitle: this.getSiteTitle()
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = _.defaults(defaults, options.data);
|
||||||
|
|
||||||
|
// read the proper email body template
|
||||||
|
return fs.readFile(path.join(this.templatesDir, options.template + '.html'), 'utf8')
|
||||||
|
.then(function (content) {
|
||||||
|
// insert user-specific data into the email
|
||||||
|
const compiled = _.template(content);
|
||||||
|
const htmlContent = compiled(data);
|
||||||
|
|
||||||
|
// generate a plain-text version of the same email
|
||||||
|
const textContent = htmlToText.fromString(htmlContent);
|
||||||
|
|
||||||
|
return {
|
||||||
|
html: htmlContent,
|
||||||
|
text: textContent
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = EmailContentGenerator;
|
|
@ -1,2 +1,15 @@
|
||||||
|
const path = require('path');
|
||||||
|
const urlUtils = require('../../../shared/url-utils');
|
||||||
|
const settingsCache = require('../../../shared/settings-cache');
|
||||||
|
const EmailContentGenerator = require('./EmailContentGenerator');
|
||||||
|
|
||||||
|
const emailContentGenerator = new EmailContentGenerator({
|
||||||
|
getSiteUrl: () => urlUtils.urlFor('home', true),
|
||||||
|
getSiteTitle: () => settingsCache.get('title'),
|
||||||
|
templatesDir: path.resolve(__dirname, '..', 'mail', 'templates')
|
||||||
|
});
|
||||||
|
|
||||||
exports.GhostMailer = require('./GhostMailer');
|
exports.GhostMailer = require('./GhostMailer');
|
||||||
exports.utils = require('./utils');
|
exports.utils = {
|
||||||
|
generateContent: emailContentGenerator.getContent.bind(emailContentGenerator)
|
||||||
|
};
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
const _ = require('lodash').runInContext();
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
|
||||||
const htmlToText = require('html-to-text');
|
|
||||||
const urlUtils = require('../../../shared/url-utils');
|
|
||||||
const settingsCache = require('../../../shared/settings-cache');
|
|
||||||
const templatesDir = path.resolve(__dirname, '..', 'mail', 'templates');
|
|
||||||
|
|
||||||
_.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
|
|
||||||
|
|
||||||
exports.generateContent = function generateContent(options) {
|
|
||||||
const defaults = {
|
|
||||||
siteUrl: urlUtils.urlFor('home', true),
|
|
||||||
siteTitle: settingsCache.get('title')
|
|
||||||
};
|
|
||||||
|
|
||||||
const data = _.defaults(defaults, options.data);
|
|
||||||
|
|
||||||
// read the proper email body template
|
|
||||||
return fs.readFile(path.join(templatesDir, options.template + '.html'), 'utf8')
|
|
||||||
.then(function (content) {
|
|
||||||
// insert user-specific data into the email
|
|
||||||
const compiled = _.template(content);
|
|
||||||
const htmlContent = compiled(data);
|
|
||||||
|
|
||||||
// generate a plain-text version of the same email
|
|
||||||
const textContent = htmlToText.fromString(htmlContent);
|
|
||||||
|
|
||||||
return {
|
|
||||||
html: htmlContent,
|
|
||||||
text: textContent
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,41 +1,38 @@
|
||||||
const should = require('should');
|
const assert = require('assert');
|
||||||
const sinon = require('sinon');
|
const path = require('path');
|
||||||
const mail = require('../../../../../core/server/services/mail');
|
|
||||||
const configUtils = require('../../../../utils/configUtils');
|
|
||||||
|
|
||||||
describe('Mail: Utils', function () {
|
const EmailContentGenerator = require('../../../../../core/server/services/mail/EmailContentGenerator');
|
||||||
const scope = {ghostMailer: null};
|
|
||||||
|
|
||||||
beforeEach(function () {
|
describe('Mail: EmailContentGenerator', function () {
|
||||||
configUtils.set({mail: {transport: 'stub'}});
|
it('generate welcome', async function () {
|
||||||
scope.ghostMailer = new mail.GhostMailer();
|
const emailContentGenerator = new EmailContentGenerator({
|
||||||
|
getSiteTitle: () => 'The Ghost Blog',
|
||||||
|
getSiteUrl: () => 'http://myblog.com',
|
||||||
|
templatesDir: path.resolve(__dirname, '../../../../../core/server/services/mail/templates/')
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function () {
|
const content = await emailContentGenerator.getContent({
|
||||||
sinon.restore();
|
|
||||||
configUtils.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('generate welcome', function (done) {
|
|
||||||
mail.utils.generateContent({
|
|
||||||
template: 'welcome',
|
template: 'welcome',
|
||||||
data: {
|
data: {
|
||||||
ownerEmail: 'test@example.com'
|
ownerEmail: 'test@example.com'
|
||||||
}
|
}
|
||||||
}).then(function (result) {
|
|
||||||
return scope.ghostMailer.send({
|
|
||||||
to: 'test@example.com',
|
|
||||||
subject: 'lol',
|
|
||||||
html: result.html,
|
|
||||||
text: result.text
|
|
||||||
});
|
|
||||||
}).then(function () {
|
|
||||||
done();
|
|
||||||
}).catch(done);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generates newsletter template', function (done) {
|
assert.match(content.html, /<title>Welcome to Ghost<\/title>/);
|
||||||
mail.utils.generateContent({
|
assert.match(content.html, /This email was sent from <a href="http:\/\/myblog.com" style="color: #738A94;">http:\/\/myblog.com<\/a> to <a href="mailto:test@example.com" style="color: #738A94;">test@example.com<\/a><\/p>/);
|
||||||
|
|
||||||
|
assert.match(content.text, /Email Address: test@example.com \[test@example.com\]/);
|
||||||
|
assert.match(content.text, /This email was sent from http:\/\/myblog.com/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generates newsletter template', async function () {
|
||||||
|
const emailContentGenerator = new EmailContentGenerator({
|
||||||
|
getSiteTitle: () => 'The Ghost Blog',
|
||||||
|
getSiteUrl: () => 'http://myblog.com',
|
||||||
|
templatesDir: path.resolve(__dirname, '../../../../../core/server/services/mail/templates/')
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = await emailContentGenerator.getContent({
|
||||||
template: 'newsletter',
|
template: 'newsletter',
|
||||||
data: {
|
data: {
|
||||||
blog: {
|
blog: {
|
||||||
|
@ -93,15 +90,13 @@ describe('Mail: Utils', function () {
|
||||||
date: 'june, 9th 2016'
|
date: 'june, 9th 2016'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then(function (result) {
|
|
||||||
return scope.ghostMailer.send({
|
|
||||||
to: 'jbloggs@example.com',
|
|
||||||
subject: 'The Newsletter Blog',
|
|
||||||
html: result.html,
|
|
||||||
text: result.text
|
|
||||||
});
|
});
|
||||||
}).then(function () {
|
|
||||||
done();
|
assert.match(content.html, /<title>The Ghost Blog<\/title>/);
|
||||||
}).catch(done);
|
assert.match(content.html, /<span style="text-transform:capitalize">monthly<\/span> digest/);
|
||||||
|
assert.match(content.html, /<span style="text-transform:capitalize">june, 9th 2016<\/span><\/h3>/);
|
||||||
|
|
||||||
|
assert.match(content.text, /MONTHLY DIGEST — JUNE, 9TH 2016/);
|
||||||
|
assert.match(content.text, /SECOND BLOG POST \[HTTP:\/\/MYBLOG.COM\/SECOND-BLOG-POST\]/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue