Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
"es2015"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
node_modules/
sandbox/
10 changes: 10 additions & 0 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

let gulp = require('gulp');
let gulpMocha = require('gulp-mocha');

gulp.task('test', () => {
return gulp.src(['**/*.spec.js', '!node_modules/**', '!**/node_modules/**'], {read: false}).pipe(gulpMocha());
});

gulp.task('default', ['test']);
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
},
"peerDependencies": {},
"dependencies": {
"lodash": "^4.11.1",
"lodash": "^4.13.1",
"mandrill-api": "^1.0.45",
"nodemailer": "^2.3.2",
"nodemailer": "^2.4.2",
"swig": "^1.4.2"
},
"devDependencies": {
"babel-core": "^6.9.1",
"babel-preset-es2015": "^6.9.0",
"gulp": "^3.9.1",
"gulp-mocha": "^2.2.0",
"rewire": "^2.5.1",
"should": "^8.3.1",
"sinon": "^1.17.3"
"should": "^9.0.0",
"sinon": "^1.17.4"
}
}
13 changes: 6 additions & 7 deletions providers/mandrill.provider.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
'use strict';
let templateService = require('../template.service.js'),
_ = require('lodash'),
swig = require('swig'),
mandrill = require('mandrill-api/mandrill');

class MandrillProvider {
constructor(config) {
this.name = "mandrill";

let useTestApi = process.env.NODE_ENV === 'e2e' || process.env.NODE_ENV === 'test';
let api_key = useTestApi ? config.testApiKey : config.apiKey;
this.mandrill_client = new mandrill.Mandrill(api_key);
}

send(template, mailSettings, templateData) {

return new Promise((resolve, reject) => {
templateService.getHtml(template, templateData).then((stringHtml) => {

let recipientMetadata = [];

if (mailSettings.existingUsers) {
Expand Down Expand Up @@ -52,7 +51,7 @@ class MandrillProvider {
from_email: from.email,
from_name: from.name,
to: to_emails,
subject:swig.render((mailSettings.subject || template.subject), {locals:templateData}),
subject: templateService.getHtmlSubject(mailSettings.subject || template.subject, templateData),
html: stringHtml,
track_opens: true,
track_clicks: true,
Expand All @@ -76,10 +75,10 @@ class MandrillProvider {
let results = responses.map((response) => {
let result = {
status: response.status,
email: response.email,
email: response.email
};

if(response.reject_reason) {
if (response.reject_reason) {
result.reason = response.reject_reason;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ describe('Mandrill email provider service', () => {
});

beforeEach(function () {
mandrillEmailServiceClass = rewire('../providers/mandrill.provider.js');
mandrillEmailServiceClass = rewire('./mandrill.provider.js');

templateService = {
getHtml: sinon.stub().returns(Promise.resolve())
getHtml: sinon.stub().returns(Promise.resolve()),
getHtmlSubject: () => Promise.resolve()
};

mandrill_client = {
Expand Down
76 changes: 35 additions & 41 deletions providers/nodemailer.provider.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,53 @@
'use strict';
var templateService = require('../template.service.js'),
swig = require('swig'),
nodemailer = require('nodemailer');

class NodemailerProvider {
constructor(config) {
this.name = "nodemailer";
this.transport = nodemailer.createTransport(config);
this.config = config;
}

send(template, mailSettings, templateData) {

let from = typeof mailSettings.from !== 'undefined' ? mailSettings.from : template.from;

return new Promise((resolve, reject) => {
templateService.getHtml(template, templateData).then((stringHtml) => {

// prepare options object
var transportOptions = {
from: `${from.name} <${from.email}>`, // Do not change - must be verified email address of startapp.com
to: mailSettings.to,
sender: mailSettings.sender || template.sender,
replyTo: mailSettings.replyTo || template.replyTo,
subject:swig.render((mailSettings.subject || template.subject), {locals:templateData}),
html: stringHtml
};

// send email
this.transport.sendMail(transportOptions, (err, responses) => {
this.transport.close();
if (err) {
return reject(err);
}

let results = [];

responses.accepted.forEach((accepted) => {
results.push({
status: 'sent',
email: accepted
});
});

responses.rejected.forEach((rejected) => {
results.push({
status: 'rejected',
email: rejected
});
});

resolve(results);
return templateService.getHtml(template, templateData).then((stringHtml) => {

let nodemailer_transport = nodemailer.createTransport(this.config);

// prepare options object
var transportOptions = {
from: `${from.name} <${from.email}>`, // Do not change - must be verified email address of startapp.com
to: mailSettings.to,
sender: mailSettings.sender || template.sender,
replyTo: mailSettings.replyTo || template.replyTo,
subject: templateService.getHtmlSubject(mailSettings.subject || template.subject, templateData),
html: stringHtml
};

// send email
return nodemailer_transport.sendMail(transportOptions);

}).then((responses) => {

let results = [];

responses.accepted.forEach((accepted) => {
results.push({
status: 'sent',
email: accepted
});
}, (err) => {
return reject(err);
});

responses.rejected.forEach((rejected) => {
results.push({
status: 'rejected',
email: rejected
});
});

return results;
});
}
}
Expand Down
107 changes: 107 additions & 0 deletions providers/nodemailer.provider.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use strict';

describe('nodemailer email provider service', () => {


let rewire = require('rewire');
let should = require('should');

let nodemailerEmailServiceClass,
nodemailerEmailProvider,
templateServiceMock,
template,
transport,
nodemailerMock;

before(function () {
template = {
"from": {
"email": "test@test.com",
"name": "Test"
}
};
});

beforeEach(function () {
nodemailerEmailServiceClass = rewire('./nodemailer.provider.js');

templateServiceMock = {
getHtml: () => Promise.resolve(),
getHtmlSubject: () => Promise.resolve()
};

transport = {
sendMail: () => Promise.resolve({
accepted: ["test@test.com"],
rejected: []
})
};

nodemailerMock = {
createTransport: () => {
return transport;
}
};

nodemailerEmailServiceClass.__set__("templateService", templateServiceMock);
nodemailerEmailServiceClass.__set__("nodemailer", nodemailerMock);

nodemailerEmailProvider = new nodemailerEmailServiceClass();

});


it("should return multiple results for multiple recipients", (done) => {
transport = {
sendMail: () => Promise.resolve({
accepted: [
"test@test.com",
"test2@test.com"
],
rejected: []
})
};

nodemailerEmailProvider.send(template, {}, {}).then((res) => {
res.length.should.equal(2);
return res;
}).then(done.bind(null, null), done);
});

it("should return single result with single recipient", (done) => {
nodemailerEmailProvider.send(template, {}, {}).then((res) => {
res.length.should.equal(1);
return res;
}).then(done.bind(null, null), done);
});

it("should return result object", (done) => {
nodemailerEmailProvider.send(template, {}, {}).then((res) => {
let result = res[0];
result.should.have.ownProperty('status');
result.should.have.ownProperty('email');
return res;
}).then(done.bind(null, null), done);
});

it("should reject if template service fails", () => {
templateServiceMock.getHtml = () => Promise.reject(new Error("Template Service Error"));
return nodemailerEmailProvider.send(template, {}, {}).should.be.rejectedWith("Template Service Error");
});

it("should reject if nodemailer service fails", () => {
transport.sendMail = () => Promise.reject(new Error("nodemailer error"));
return nodemailerEmailProvider.send(template, {}, {}).should.be.rejectedWith("nodemailer error");
});

it("should return status rejected if nodemailer rejects", (done) => {
transport.sendMail = () => Promise.resolve({
accepted: [],
rejected: ["test@test.com"]
});
return nodemailerEmailProvider.send(template, {}, {}).then((res) => {
res[0].status.should.equal("rejected");
}).then(done.bind(null, null), done);
});

});
8 changes: 5 additions & 3 deletions template.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ var swig = require('swig'),


class EmailTemplateService {

constructor() {}


/**
* Method to get a specific rendered template by name
* @param template - {Template Object} template from config
Expand All @@ -33,6 +31,10 @@ class EmailTemplateService {
});
});
}

getHtmlSubject(template, data) {
return swig.render(template, {locals: data});
}
}

module.exports = new EmailTemplateService();
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Email template service', () => {
templateService;

beforeEach(function () {
templateService = rewire('../template.service.js');
templateService = rewire('./template.service.js');

swig = {};
templateService.__set__("swig", swig);
Expand Down