Refactor email model into mail connector

This commit is contained in:
Ritchie Martori 2013-10-11 19:06:16 -07:00
parent 26d0f74d66
commit a3f1d8d944
6 changed files with 239 additions and 89 deletions

View File

@ -1164,6 +1164,22 @@ POST /users/logout
Require a user to verify their email address before being able to login. This will send an email to the user containing a link to verify their address. Once the user follows the link they will be redirected to `/` and be able to login normally.
```js
// first setup the mail datasource (see #mail-model for more info)
var mail = loopback.createDataSource({
connector: loopback.Mail,
transports: [{
type: 'smtp',
host: 'smtp.gmail.com',
secureConnection: true,
port: 465,
auth: {
user: 'you@gmail.com',
pass: 'your-password'
}
}]
});
User.email.attachTo(mail);
User.requireEmailVerfication = true;
User.afterRemote('create', function(ctx, user, next) {
var options = {
@ -1231,6 +1247,43 @@ MySession.attachTo(loopback.memory());
Send emails from your loopback app.
```js
// extend a one-off model for sending email
var MyEmail = loopback.Email.extend('my-email');
// create a mail data source
var mail = loopback.createDataSource({
connector: loopback.Mail,
transports: [{
type: 'smtp',
host: 'smtp.gmail.com',
secureConnection: true,
port: 465,
auth: {
user: 'you@gmail.com',
pass: 'your-password'
}
}]
});
// attach the model
MyEmail.attachTo(mail);
// send an email
MyEmail.send({
to: 'foo@bar.com',
from: 'you@gmail.com',
subject: 'my subject',
text: 'my text',
html: 'my <em>html</em>'
}, function(err, mail) {
console.log('email sent!');
});
```
> NOTE: the mail connector uses [nodemailer](http://www.nodemailer.com/). See
> the [nodemailer docs](http://www.nodemailer.com/) for more info.
### REST Router
Expose models over rest using the `loopback.rest` router.

View File

@ -10,9 +10,10 @@ var loopback = module.exports = require('./lib/loopback');
loopback.Connector = require('./lib/connectors/base-connector');
loopback.Memory = require('./lib/connectors/memory');
loopback.Mail = require('./lib/connectors/mail');
/**
* Types
*/
loopback.GeoPoint = require('loopback-datasource-juggler/lib/geo').GeoPoint;
loopback.GeoPoint = require('loopback-datasource-juggler/lib/geo').GeoPoint;

121
lib/connectors/mail.js Normal file
View File

@ -0,0 +1,121 @@
/**
* Dependencies.
*/
var mailer = require('nodemailer')
, assert = require('assert');
/**
* Export the MailConnector class.
*/
module.exports = MailConnector;
/**
* Create an instance of the connector with the given `settings`.
*/
function MailConnector(settings) {
assert(typeof settings === 'object', 'cannot initiaze MailConnector without a settings object');
var transports = settings.transports || [];
transports.forEach(this.setupTransport.bind(this));
}
MailConnector.initialize = function(dataSource, callback) {
dataSource.connector = new MailConnector(dataSource.settings);
callback();
}
MailConnector.prototype.DataAccessObject = Mailer;
/**
* Add a transport to the available transports. See https://github.com/andris9/Nodemailer#setting-up-a-transport-method.
*
* Example:
*
* Email.setupTransport({
* type: 'SMTP',
* host: "smtp.gmail.com", // hostname
* secureConnection: true, // use SSL
* port: 465, // port for secure SMTP
* auth: {
* user: "gmail.user@gmail.com",
* pass: "userpass"
* }
* });
*
*/
MailConnector.prototype.setupTransport = function(setting) {
var connector = this;
connector.transports = connector.transports || [];
connector.transportsIndex = connector.transportsIndex || {};
var transport = mailer.createTransport(setting.type, setting);
connector.transportsIndex[setting.type] = transport;
connector.transports.push(transport);
}
function Mailer() {
}
/**
* Send an email with the given `options`.
*
* Example Options:
*
* {
* from: "Fred Foo ✔ <foo@blurdybloop.com>", // sender address
* to: "bar@blurdybloop.com, baz@blurdybloop.com", // list of receivers
* subject: "Hello ✔", // Subject line
* text: "Hello world ✔", // plaintext body
* html: "<b>Hello world ✔</b>" // html body
* }
*
* See https://github.com/andris9/Nodemailer for other supported options.
*
* @param {Object} options
* @param {Function} callback Called after the e-mail is sent or the sending failed
*/
Mailer.send = function (options, fn) {
var dataSource = this.dataSource;
var settings = dataSource && dataSource.settings;
var connector = dataSource.connector;
var transportsIndex = connector.transportsIndex;
var transport = transportsIndex[options.transport || 'SMTP'] || connector.transports[0];
assert(transport, 'You must supply an Email.settings.transports array containing at least one transport');
if(settings && settings.debug) {
console.log('Sending Mail:');
if(options.transport) {
console.log('\t TRANSPORT:', options.transport);
}
console.log('\t TO:', options.to);
console.log('\t FROM:', options.from);
console.log('\t SUBJECT:', options.subject);
console.log('\t TEXT:', options.text);
console.log('\t HTML:', options.html);
}
transport.sendMail(options, fn);
}
/**
* Send an email instance using `modelInstance.send()`.
*/
Mailer.prototype.send = function (fn) {
this.constructor.send(this, fn);
}
/**
* Access the node mailer object.
*/
MailConnector.mailer =
MailConnector.prototype.mailer =
Mailer.mailer =
Mailer.prototype.mailer = mailer;

View File

@ -3,9 +3,7 @@
*/
var Model = require('../loopback').Model
, loopback = require('../loopback')
, mailer = require('nodemailer')
, assert = require('assert');
, loopback = require('../loopback');
/**
* Default Email properties.
@ -24,87 +22,3 @@ var properties = {
*/
var Email = module.exports = Model.extend('email', properties);
/*!
* Setup the Email class after extension.
*/
Email.setup = function (settings) {
settings = settings || this.settings;
var transports = settings.transports || [];
transports.forEach(this.setupTransport.bind(this));
}
/**
* Add a transport to the available transports. See https://github.com/andris9/Nodemailer#setting-up-a-transport-method.
*
* Example:
*
* Email.setupTransport({
* type: 'SMTP',
* host: "smtp.gmail.com", // hostname
* secureConnection: true, // use SSL
* port: 465, // port for secure SMTP
* auth: {
* user: "gmail.user@gmail.com",
* pass: "userpass"
* }
* });
*
*/
Email.setupTransport = function (setting) {
var Email = this;
Email.transports = Email.transports || [];
Email.transportsIndex = Email.transportsIndex || {};
var transport = mailer.createTransport(setting.type, setting);
Email.transportsIndex[setting.type] = transport;
Email.transports.push(transport);
}
/**
* Send an email with the given `options`.
*
* Example Options:
*
* {
* from: "Fred Foo ✔ <foo@blurdybloop.com>", // sender address
* to: "bar@blurdybloop.com, baz@blurdybloop.com", // list of receivers
* subject: "Hello ✔", // Subject line
* text: "Hello world ✔", // plaintext body
* html: "<b>Hello world ✔</b>" // html body
* }
*
* See https://github.com/andris9/Nodemailer for other supported options.
*
* @param {Object} options
* @param {Function} callback Called after the e-mail is sent or the sending failed
*/
Email.send = function (options, fn) {
var transport = this.transportsIndex[options.transport || 'SMTP'] || this.transports[0];
assert(transport, 'You must supply an Email.settings.transports array containing at least one transport');
transport.sendMail(options, fn);
}
/**
* Access the node mailer object.
*
* Email.mailer
* // or
* var email = new Email({to: 'foo@bar.com', from: 'bar@bat.com'});
* email.mailer
*/
Email.mailer =
Email.prototype.mailer = mailer;
/**
* Send an email instance using `Email.send()`.
*/
Email.prototype.send = function (fn) {
this.constructor.send(this, fn);
}

56
test/email.test.js Normal file
View File

@ -0,0 +1,56 @@
var loopback = require('../');
var MailConnector = loopback.Mail;
var MyEmail = loopback.Email.extend('my-email');
var assert = require('assert');
describe('Email and SMTP', function () {
var mail = loopback.createDataSource({
connector: MailConnector,
transports: [
{type: 'STUB'}
]
});
MyEmail.attachTo(mail);
it('should have a send method', function () {
assert(typeof MyEmail.send === 'function');
assert(typeof MyEmail.prototype.send === 'function');
});
describe('MyEmail', function () {
it('MyEmail.send(options, callback)', function (done) {
var options = {
to: 'to@to.com',
from: 'from@from.com',
subject: 'subject',
text: 'text',
html: '<h1>html</h1>'
};
MyEmail.send(options, function(err, mail) {
assert(mail.message);
assert(mail.envelope);
assert(mail.messageId);
done(err);
});
});
it('myEmail.send(callback)', function (done) {
var message = new MyEmail({
to: 'to@to.com',
from: 'from@from.com',
subject: 'subject',
text: 'text',
html: '<h1>html</h1>'
});
message.send(function (err, mail) {
assert(mail.message);
assert(mail.envelope);
assert(mail.messageId);
done(err);
});
});
});
});

View File

@ -1,6 +1,7 @@
var User = loopback.User.extend('user');
var Session = loopback.Session;
var passport = require('passport');
var MailConnector = require('../lib/connectors/mail');
var userMemory = loopback.createDataSource({
connector: loopback.Memory
@ -9,9 +10,13 @@ var userMemory = loopback.createDataSource({
describe('User', function(){
var mailDataSource = loopback.createDataSource({
connector: MailConnector,
transports: [{type: 'STUB'}]
});
User.attachTo(userMemory);
User.session.attachTo(userMemory);
User.email.setup({transports: [{type: 'STUB'}]});
User.email.attachTo(mailDataSource);
// allow many User.afterRemote's to be called
User.setMaxListeners(0);