Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nodemailer-sparkpost-transport

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nodemailer-sparkpost-transport - npm Package Compare versions

Comparing version 0.1.2 to 1.0.0

.eslintrc

14

CHANGELOG.md
## Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
### v0.1.1 (2016/05/17)
## [Unreleased][unreleased]
### [1.0.0] - 2016/07/26
- [#7](https://github.com/SparkPost/nodemailer-sparkpost-transport/pull/7) Implemented the Nodemailer API (@ewandennis)
### [0.1.2] - 2016/05/17
- [#4](https://github.com/SparkPost/nodemailer-sparkpost-transport/pull/4) Removed dotenv, updated README, and general cleanup. Closes #3 (@aydrian)

@@ -8,3 +15,6 @@ - [#2](https://github.com/SparkPost/nodemailer-sparkpost-transport/pull/2) Sort available options lists (@simison)

### v0.1.0 (2015/08/28)
### [0.1.3] - 2016/05/17
- Unpublished from NPM. Republished as v0.1.2
### [0.1.0] - 2015/08/28
- Initial release
'use strict';
// Dependencies
var pkg = require('../package');
var SparkPost = require('sparkpost');
var pkg = require('../package')
, SparkPost = require('sparkpost');
// Constructor
var SparkPostTransport = function SparkPostTransport(options) {
function SparkPostTransport(options) {
var opt;
// Set required properties

@@ -16,44 +18,147 @@ this.name = 'SparkPost';

this.sparkPostApiKey = process.env.SPARKPOST_API_KEY || options.sparkPostApiKey;
this.sparkPostEmailClient = new SparkPost( this.sparkPostApiKey );
this.sparkPostEmailClient = new SparkPost(this.sparkPostApiKey);
// Set any options which are valid
for( var opt in options ) {
this[opt] = (options.hasOwnProperty( opt )) ? options[opt] : undefined;
for(opt in options) {
this[opt] = (options.hasOwnProperty(opt)) ? options[opt] : undefined;
}
return this;
}
function populateCustomFields(message, defaults, request) {
var data = message.data
, customFields = ['campaign_id', 'metadata', 'substitution_data', 'options', 'content', 'recipients'];
// Apply default SP-centric options and override if provided in mail object
customFields.forEach(function(fld) {
if (data.hasOwnProperty(fld)) {
request[fld] = data[fld];
} else if (defaults.hasOwnProperty(fld)) {
request[fld] = defaults[fld];
}
});
}
function populateInlineStdFields(message, resolveme, request) {
var data = message.data;
if (data.from) {
request.content.from = data.from;
}
// cc
// bcc
if (data.subject) {
request.content.subject = data.subject;
}
if (data.html) {
resolveme.html = 'html';
}
if (data.text) {
resolveme.text = 'text';
}
// attachments
// headers
}
SparkPostTransport.prototype.send = function send(message, callback) {
var data = message.data
, request = {
content: {}
}
, resolveme = {};
// Conventional nodemailer fields override SparkPost-specific ones and defaults
populateCustomFields(message, this, request);
if (data.to) {
request.recipients = emailList(data.to) || [];
}
if (data.raw) {
resolveme.raw = 'email_rfc822';
} else {
populateInlineStdFields(message, resolveme, request);
}
this.resolveAndSend(message, resolveme, request, callback);
};
SparkPostTransport.prototype.send = function send(payload, callback) {
var email = {
transmissionBody: {}
};
SparkPostTransport.prototype.resolveAndSend = function(mail, toresolve, request, callback) {
var self = this
, keys = Object.keys(toresolve)
, srckey
, dstkey;
// Apply default options and override if provided in mail object
email.transmissionBody.tags = (payload.data.tags) ? payload.data.tags : this.tags;
email.transmissionBody.campaign_id = (payload.data.campaign_id) ? payload.data.campaign_id : this.campaign_id;
email.transmissionBody.metadata = (payload.data.metadata) ? payload.data.metadata : this.metadata;
email.transmissionBody.substitution_data = (payload.data.substitution_data) ? payload.data.substitution_data : this.substitution_data;
email.transmissionBody.options = (payload.data.options) ? payload.data.options : this.options;
email.transmissionBody.content = (payload.data.content) ? payload.data.content : this.content;
email.transmissionBody.recipients = (payload.data.recipients) ? payload.data.recipients : this.recipients;
if (keys.length === 0) {
return this.sendWithSparkPost(request, callback);
}
// Send the transmission using Sparkpost
this.sparkPostEmailClient.transmissions.send(email, function( err, res ) {
if( err ) {
return callback( err );
} else {
// Example successful Sparkpost transmission response:
// { "results": { "total_rejected_recipients": 0, "total_accepted_recipients": 1, "id": "66123596945797072" } }
return callback( null, {
messageId: res.body.results.id,
accepted: res.body.results.total_accepted_recipients,
rejected: res.body.results.total_rejected_recipients
});
srckey = keys[0];
dstkey = toresolve[keys[0]];
delete toresolve[srckey];
this.loadContent(mail, srckey, function(err, content) {
request.content[dstkey] = content;
self.resolveAndSend(mail, toresolve, request, callback);
});
};
SparkPostTransport.prototype.loadContent = function(mail, key, callback) {
var content = mail.data[key];
if (typeof content === 'string') {
return process.nextTick(function() {
callback(null, content);
});
}
mail.resolveContent(mail.data, key, function(err, res) {
if (err) {
return callback(err);
}
callback(null, res.toString());
});
};
SparkPostTransport.prototype.sendWithSparkPost = function(transBody, callback) {
this.sparkPostEmailClient.transmissions.send({transmissionBody: transBody}, function(err, res) {
if (err) {
return callback(err);
}
// Example successful Sparkpost transmission response:
// { "results": { "total_rejected_recipients": 0, "total_accepted_recipients": 1, "id": "66123596945797072" } }
return callback(null, {
messageId: res.body.results.id,
accepted: res.body.results.total_accepted_recipients,
rejected: res.body.results.total_rejected_recipients
});
});
};
function emailList(strOrLst) {
var lst = strOrLst;
if (typeof strOrLst === 'string') {
lst = strOrLst.split(',');
}
return lst.map(function(addr) {
if (typeof addr === 'string') {
return {address: addr};
}
return {
address: {
name: addr.name,
email: addr.address
}};
});
}
module.exports = function(options) {
return new SparkPostTransport(options);
};

8

package.json
{
"name": "nodemailer-sparkpost-transport",
"version": "0.1.2",
"version": "1.0.0",
"description": "SparkPost transport for Nodemailer",
"main": "lib/sparkPostTransport.js",
"scripts": {
"pretest": "eslint examples lib test *.js",
"test": "mocha",
"release": "with-package git commit -am pkg.version && with-package git tag pkg.version && git push upstream && npm publish && git push --tags upstream"
"release": "with-package git commit -am pkg.version && with-package git tag pkg.version && git push upstream && git push --tags upstream"
},

@@ -29,3 +30,6 @@ "repository": {

"chai": "^3.5.0",
"eslint": "=3.0.0",
"eslint-config-sparkpost": "^1.0.1",
"mocha": "^2.4.5",
"nodemailer": "^2.5.0",
"sinon": "^1.17.4",

@@ -32,0 +36,0 @@ "with-package": "^0.2.0"

@@ -10,3 +10,3 @@ # nodemailer-sparkpost-transport

Install with npm
### Install

@@ -17,3 +17,3 @@ ```

Require to your script
### Create a Nodemailer transport object

@@ -23,88 +23,46 @@ ```javascript

var sparkPostTransport = require('nodemailer-sparkpost-transport');
```
Create a Nodemailer transport object
```javascript
var transporter = nodemailer.createTransport(sparkPostTransport(options))
```
Where
where:
- **options** defines connection and message data
- **options** defines connection _default_ transmission properties
- `sparkPostApiKey` - SparkPost [API Key](https://app.sparkpost.com/account/credentials). If not provided, it will use the `SPARKPOST_API_KEY` env var.
- `campaign_id` - Name of the campaign
- `content` - Content that will be used to construct a message
- `metadata` - Transmission level metadata containing key/value pairs
- `options` - JSON object in which transmission options are defined
- `substitution_data` - Key/value pairs that are provided to the substitution engine
- `campaign_id` - Name of the campaign (optional)
- `metadata` - Transmission level metadata containing key/value pairs (optional)
- `options` - JSON object in which transmission options are defined (optional)
- `substitution_data` - Key/value pairs that are provided to the substitution engine (optional)
For more information, see the [SparkPost API Documentation for Transmissions](https://developers.sparkpost.com/api/#/reference/transmissions)
For more information, see the [SparkPost API Documentation for Transmissions](https://developers.sparkpost.com/api/transmissions)
## Send a message
Send a message
```javascript
transport.sendMail(options, function(err, info) {});
transport.sendMail({
from: 'me@here.com',
to: 'you@there.com',
subject: 'Very important stuff',
text: 'Plain text',
html: 'Rich taggery'
}, function(err, info) {
if (err) {
console.log('Error: ' + err);
} else {
console.log('Success: ' + info);
});
```
Where
[Read more about Nodemailer's `sendMail()` method here](https://github.com/nodemailer/nodemailer#sending-mail).
- **options** defines connection and message data
- `recipients` - Inline recipient objects or object containing stored recipient list ID. See [SparkPost API Documentation for Recipient Lists](https://developers.sparkpost.com/api/#/reference/recipient-lists) for more information.
- `campaign_id` - Override for option above
- `content` - Override for option above
- `metadata` - Override for option above
- `options` - Override for option above
- `substitution_data` - Override for option above
### Additional Options
## Example
The SparkPost Nodemailer transport also supports a few SparkPost-specific `sendMail()` options in both the transport constructor and the 'sendMail()` method.
```javascript
'use strict';
Note: `sendMail()` options override their constructor counterparts:
var nodemailer = require('nodemailer');
var sparkPostTransport = require('nodemailer-sparkpost-transport');
- **options**
- `campaign_id` - Overrides for constructor option
- `metadata` - Override for constructor option
- `options` - Override for constructor option
- `substitution_data` - Override for constructor option
var transporter = nodemailer.createTransport(sparkPostTransport({
"sparkPostApiKey": "<YOUR_API_KEY>",
"options": {
"open_tracking": true,
"click_tracking": true,
"transactional": true
},
"campaign_id": "Nodemailer Default",
"metadata": {
"some_useful_metadata": "testing_sparkpost"
},
"substitution_data": {
"sender": "YOUR NAME",
"fullName": "YOUR NAME",
"productName": "The coolest product ever",
"sparkpostSupportEmail": "support@sparkpost.com",
"sparkpostSupportPhone": "123-456-7890"
},
"content": {
"template_id": "ADD YOUR TEMPLATE ID HERE"
}
}));
transporter.sendMail({
"recipients": [
{
"address": {
"email": "CHANGE TO YOUR TARGET TEST EMAIL",
"name": "CHANGE TO YOUR RECIPIENT NAME"
}
}
]
}, function(err, info) {
if (err) {
console.error(err);
} else {
console.log(info);
}
});
```

@@ -1,44 +0,43 @@

"use strict";
'use strict';
var sinon = require("sinon");
var expect = require("chai").expect;
var sinon = require('sinon')
, expect = require('chai').expect
, nodemailer = require('nodemailer')
, sparkPostTransport = require('../lib/sparkPostTransport.js')
, pkg = require('../package.json');
var sparkPostTransport = require("../lib/sparkPostTransport.js");
describe('SparkPost Transport', function() {
var transport = sparkPostTransport({sparkPostApiKey: '12345678901234567890'});
var pkg = require("../package.json");
describe("SparkPost Transport", function() {
var transport = sparkPostTransport({sparkPostApiKey: "12345678901234567890"});
it("should have a name and version property", function(done) {
expect(transport).to.have.property("name", "SparkPost");
expect(transport).to.have.property("version", pkg.version);
it('should have a name and version property', function(done) {
expect(transport).to.have.property('name', 'SparkPost');
expect(transport).to.have.property('version', pkg.version);
done();
});
it("should expose a send method", function(done) {
it('should expose a send method', function(done) {
expect(transport.send).to.exist;
expect(transport.send).to.be.a("function");
expect(transport.send).to.be.a('function');
done();
});
it("should be able to set options", function(done) {
it('should be able to set options', function(done) {
var transport = sparkPostTransport({
sparkPostApiKey: "12345678901234567890",
campaign_id: "sample_campaign",
tags: ["new-account-notification"],
metadata: {"source": "event"},
substitution_data: {"salutatory": "Welcome to SparkPost!"},
options: {"click_tracking": true, "open_tracking": true},
content: {"template_id": "newAccountNotification"},
recipients: [{"email": "john.doe@example.com", "name": "John Doe"}]
sparkPostApiKey: '12345678901234567890',
campaign_id: 'sample_campaign',
tags: ['new-account-notification'],
metadata: {'source': 'event'},
substitution_data: {'salutatory': 'Welcome to SparkPost!'},
options: {'click_tracking': true, 'open_tracking': true},
content: {'template_id': 'newAccountNotification'},
recipients: [{'email': 'john.doe@example.com', 'name': 'John Doe'}]
});
expect(transport.campaign_id).to.equal("sample_campaign");
expect(transport.tags).to.deep.equal(["new-account-notification"]);
expect(transport.metadata).to.deep.equal({"source": "event"});
expect(transport.substitution_data).to.deep.equal({"salutatory": "Welcome to SparkPost!"});
expect(transport.options).to.deep.equal({"click_tracking": true, "open_tracking": true});
expect(transport.content).to.deep.equal({"template_id": "newAccountNotification"});
expect(transport.recipients).to.deep.equal([{"email": "john.doe@example.com", "name": "John Doe"}]);
expect(transport.campaign_id).to.equal('sample_campaign');
expect(transport.tags).to.deep.equal(['new-account-notification']);
expect(transport.metadata).to.deep.equal({'source': 'event'});
expect(transport.substitution_data).to.deep.equal({'salutatory': 'Welcome to SparkPost!'});
expect(transport.options).to.deep.equal({'click_tracking': true, 'open_tracking': true});
expect(transport.content).to.deep.equal({'template_id': 'newAccountNotification'});
expect(transport.recipients).to.deep.equal([{'email': 'john.doe@example.com', 'name': 'John Doe'}]);

@@ -50,34 +49,107 @@ done();

describe("Send Method", function() {
describe('Send Method', function() {
it("should be able to overload options at the transmission", function(done) {
// Create the default transport
var transport = sparkPostTransport({
sparkPostApiKey: "12345678901234567890",
campaign_id: "sample_campaign",
tags: ["new-account-notification"],
metadata: {"source": "event"},
substitution_data: {"salutatory": "Welcome to SparkPost!"},
options: {"click_tracking": true, "open_tracking": true},
content: {"template_id": "newAccountNotification"},
recipients: [{"email": "john.doe@example.com", "name": "John Doe"}]
describe('SP-centric mail structure', function() {
it('should be able to overload options at the transmission', function(done) {
// Create the default transport
var transport = sparkPostTransport({
sparkPostApiKey: '12345678901234567890',
campaign_id: 'sample_campaign',
tags: ['new-account-notification'],
metadata: {'source': 'event'},
substitution_data: {'salutatory': 'Welcome to SparkPost!'},
options: {'click_tracking': true, 'open_tracking': true},
content: {'template_id': 'newAccountNotification'},
recipients: [{'email': 'john.doe@example.com', 'name': 'John Doe'}]
})
// Create the modified options for use with the above stub test
, overloadedTransmission = {
campaign_id: 'another_sample_campaign',
tags: ['alternative-tag'],
metadata: {'changedKey': 'value'},
substitution_data: {'salutatory': 'And now...for something completely different'},
options: {'click_tracking': false, 'open_tracking': false, 'transactional': true},
recipients: [{
list_id: 'myStoredRecipientTestList'
}],
content: {
template_id: 'someOtherTemplate'
}
};
// Stub the send method of the SDK out
sinon.stub(transport, 'send', function(data, resolve) {
// Grab the transmissionBody from the send() payload for assertions
expect(data.campaign_id).to.equal('another_sample_campaign');
expect(data.tags).to.deep.equal(['alternative-tag']);
expect(data.metadata).to.deep.equal({'changedKey': 'value'});
expect(data.substitution_data).to.deep.equal({'salutatory': 'And now...for something completely different'});
expect(data.options).to.deep.equal({'click_tracking': false, 'open_tracking': false, 'transactional': true});
expect(data.content).to.deep.equal({'template_id': 'someOtherTemplate'});
expect(data.recipients).to.deep.equal([{'list_id': 'myStoredRecipientTestList'}]);
// Resolve the stub's spy
resolve({
results: {
total_rejected_recipients: 0,
total_accepted_recipients: 1,
id: '66123596945797072'
}
});
});
// Call the stub from above
transport.send(overloadedTransmission, function(data) {
expect(data.results.id).to.exist;
expect(data.results.total_rejected_recipients).to.exist;
expect(data.results.total_accepted_recipients).to.exist;
done();
});
// Return the original method to its proper state
transport.send.restore();
});
});
// Stub the send method of the SDK out
sinon.stub(transport, "send", function(data, resolve) {
// Grab the transmissionBody from the send() payload for assertions
expect(data.campaign_id).to.equal("another_sample_campaign");
expect(data.tags).to.deep.equal(["alternative-tag"]);
expect(data.metadata).to.deep.equal({"changedKey": "value"});
expect(data.substitution_data).to.deep.equal({"salutatory": "And now...for something completely different"});
expect(data.options).to.deep.equal({"click_tracking": false, "open_tracking": false, "transactional": true});
expect(data.content).to.deep.equal({"template_id": "someOtherTemplate"});
expect(data.recipients).to.deep.equal([{"list_id": "myStoredRecipientTestList"}]);
describe('conventional nodemailer mail structure', function() {
var sptrans
, transport
, mail
, rcp1
, rcp2;
// Resolve the stub's spy
resolve({
function checkTo(done) {
var req = sptrans.sparkPostEmailClient.transmissions.send.firstCall.args[0]
, transBody = req.transmissionBody;
expect(req).to.have.keys('transmissionBody');
expect(transBody).to.have.keys(['recipients', 'content']);
expect(transBody.recipients).to.have.length(2);
expect(transBody.recipients[0]).to.deep.equal({ address: rcp1 });
expect(transBody.recipients[1]).to.deep.equal({ address: rcp2 });
done();
}
beforeEach(function() {
sptrans = sparkPostTransport({
sparkPostApiKey: '12345678901234567890'
});
transport = nodemailer.createTransport(sptrans);
rcp1 = 'Mrs. Asoni <a@a.com>';
rcp2 = 'b@b.com';
mail = {
from: 'roberto@from.example.com',
to: 'kingcnut@to.example.com',
subject: 'Modern Kinging',
text: 'Edicts and surfeits...',
html: '<p>Edicts and surfeits...</p>'
};
sptrans.sparkPostEmailClient.transmissions.send = sinon.stub().yields({
results: {
total_rejected_recipients: 0,
total_accepted_recipients: 1,
id: "66123596945797072"
id: '66123596945797072'
}

@@ -87,27 +159,57 @@ });

// Create the modified options for use with the above stub test
var overloadedTransmission = {
campaign_id: "another_sample_campaign",
tags: ["alternative-tag"],
metadata: {"changedKey": "value"},
substitution_data: {"salutatory": "And now...for something completely different"},
options: {"click_tracking": false, "open_tracking": false, "transactional": true},
recipients: [{
list_id: "myStoredRecipientTestList"
}],
content: {
template_id: "someOtherTemplate"
}
};
it('should accept basic nodemailer mail content fields', function(done) {
transport.sendMail(mail, function() {
var req = sptrans.sparkPostEmailClient.transmissions.send.firstCall.args[0]
, transBody = req.transmissionBody;
// Call the stub from above
transport.send(overloadedTransmission, function(data) {
expect(data.results.id).to.exist;
expect(data.results.total_rejected_recipients).to.exist;
expect(data.results.total_accepted_recipients).to.exist;
done();
expect(req).to.have.keys('transmissionBody');
expect(transBody).to.have.keys(['recipients', 'content']);
expect(transBody.content.html).to.equal(mail.html);
expect(transBody.content.text).to.equal(mail.text);
expect(transBody.content.subject).to.equal(mail.subject);
expect(transBody.content.from).to.equal(mail.from);
expect(transBody.recipients).to.have.length(1);
expect(transBody.recipients[0]).to.have.keys('address');
expect(transBody.recipients[0].address).to.be.a('string');
expect(transBody.recipients[0].address).to.equal(mail.to);
done();
});
});
// Return the original method to its proper state
transport.send.restore();
it('should accept raw mail structure', function(done) {
delete mail.subject;
delete mail.text;
delete mail.html;
delete mail.from;
mail.raw = 'rawmsg';
transport.sendMail(mail, function() {
var req = sptrans.sparkPostEmailClient.transmissions.send.firstCall.args[0]
, transBody = req.transmissionBody;
expect(req).to.have.keys('transmissionBody');
expect(transBody).to.have.keys(['recipients', 'content']);
expect(transBody.content).to.have.keys('email_rfc822');
expect(transBody.recipients).to.have.length(1);
expect(transBody.recipients[0]).to.have.keys('address');
expect(transBody.recipients[0].address).to.be.a('string');
expect(transBody.recipients[0].address).to.equal(mail.to);
done();
});
});
it('should accept to as an array', function(done) {
mail.to = [rcp1, rcp2];
transport.sendMail(mail, function() {
checkTo(done);
});
});
it('should accept to as a string', function(done) {
mail.to = [rcp1, rcp2].join(',');
transport.sendMail(mail, function() {
checkTo(done);
});
});
});
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc