Skip to content

Commit 7a37969

Browse files
committed
Merge pull request #51 from strongloop/refactor/email
Refactor email model into mail connector
2 parents 7037342 + a3f1d8d commit 7a37969

File tree

6 files changed

+239
-89
lines changed

6 files changed

+239
-89
lines changed

docs/api.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,22 @@ POST /users/logout
11641164
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.
11651165
11661166
```js
1167+
// first setup the mail datasource (see #mail-model for more info)
1168+
var mail = loopback.createDataSource({
1169+
connector: loopback.Mail,
1170+
transports: [{
1171+
type: 'smtp',
1172+
host: 'smtp.gmail.com',
1173+
secureConnection: true,
1174+
port: 465,
1175+
auth: {
1176+
1177+
pass: 'your-password'
1178+
}
1179+
}]
1180+
});
1181+
1182+
User.email.attachTo(mail);
11671183
User.requireEmailVerfication = true;
11681184
User.afterRemote('create', function(ctx, user, next) {
11691185
var options = {
@@ -1231,6 +1247,43 @@ MySession.attachTo(loopback.memory());
12311247
12321248
Send emails from your loopback app.
12331249
1250+
```js
1251+
// extend a one-off model for sending email
1252+
var MyEmail = loopback.Email.extend('my-email');
1253+
1254+
// create a mail data source
1255+
var mail = loopback.createDataSource({
1256+
connector: loopback.Mail,
1257+
transports: [{
1258+
type: 'smtp',
1259+
host: 'smtp.gmail.com',
1260+
secureConnection: true,
1261+
port: 465,
1262+
auth: {
1263+
1264+
pass: 'your-password'
1265+
}
1266+
}]
1267+
});
1268+
1269+
// attach the model
1270+
MyEmail.attachTo(mail);
1271+
1272+
// send an email
1273+
MyEmail.send({
1274+
1275+
1276+
subject: 'my subject',
1277+
text: 'my text',
1278+
html: 'my <em>html</em>'
1279+
}, function(err, mail) {
1280+
console.log('email sent!');
1281+
});
1282+
```
1283+
1284+
> NOTE: the mail connector uses [nodemailer](http://www.nodemailer.com/). See
1285+
> the [nodemailer docs](http://www.nodemailer.com/) for more info.
1286+
12341287
### REST Router
12351288
12361289
Expose models over rest using the `loopback.rest` router.

index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ var loopback = module.exports = require('./lib/loopback');
1010

1111
loopback.Connector = require('./lib/connectors/base-connector');
1212
loopback.Memory = require('./lib/connectors/memory');
13+
loopback.Mail = require('./lib/connectors/mail');
1314

1415
/**
1516
* Types
1617
*/
1718

18-
loopback.GeoPoint = require('loopback-datasource-juggler/lib/geo').GeoPoint;
19+
loopback.GeoPoint = require('loopback-datasource-juggler/lib/geo').GeoPoint;

lib/connectors/mail.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/**
2+
* Dependencies.
3+
*/
4+
5+
var mailer = require('nodemailer')
6+
, assert = require('assert');
7+
8+
/**
9+
* Export the MailConnector class.
10+
*/
11+
12+
module.exports = MailConnector;
13+
14+
/**
15+
* Create an instance of the connector with the given `settings`.
16+
*/
17+
18+
function MailConnector(settings) {
19+
assert(typeof settings === 'object', 'cannot initiaze MailConnector without a settings object');
20+
var transports = settings.transports || [];
21+
22+
transports.forEach(this.setupTransport.bind(this));
23+
}
24+
25+
MailConnector.initialize = function(dataSource, callback) {
26+
dataSource.connector = new MailConnector(dataSource.settings);
27+
callback();
28+
}
29+
30+
MailConnector.prototype.DataAccessObject = Mailer;
31+
32+
33+
/**
34+
* Add a transport to the available transports. See https://github.com/andris9/Nodemailer#setting-up-a-transport-method.
35+
*
36+
* Example:
37+
*
38+
* Email.setupTransport({
39+
* type: 'SMTP',
40+
* host: "smtp.gmail.com", // hostname
41+
* secureConnection: true, // use SSL
42+
* port: 465, // port for secure SMTP
43+
* auth: {
44+
* user: "gmail.user@gmail.com",
45+
* pass: "userpass"
46+
* }
47+
* });
48+
*
49+
*/
50+
51+
MailConnector.prototype.setupTransport = function(setting) {
52+
var connector = this;
53+
connector.transports = connector.transports || [];
54+
connector.transportsIndex = connector.transportsIndex || {};
55+
var transport = mailer.createTransport(setting.type, setting);
56+
connector.transportsIndex[setting.type] = transport;
57+
connector.transports.push(transport);
58+
}
59+
60+
function Mailer() {
61+
62+
}
63+
64+
/**
65+
* Send an email with the given `options`.
66+
*
67+
* Example Options:
68+
*
69+
* {
70+
* from: "Fred Foo ✔ <foo@blurdybloop.com>", // sender address
71+
* to: "[email protected], [email protected]", // list of receivers
72+
* subject: "Hello ✔", // Subject line
73+
* text: "Hello world ✔", // plaintext body
74+
* html: "<b>Hello world ✔</b>" // html body
75+
* }
76+
*
77+
* See https://github.com/andris9/Nodemailer for other supported options.
78+
*
79+
* @param {Object} options
80+
* @param {Function} callback Called after the e-mail is sent or the sending failed
81+
*/
82+
83+
Mailer.send = function (options, fn) {
84+
var dataSource = this.dataSource;
85+
var settings = dataSource && dataSource.settings;
86+
var connector = dataSource.connector;
87+
var transportsIndex = connector.transportsIndex;
88+
var transport = transportsIndex[options.transport || 'SMTP'] || connector.transports[0];
89+
assert(transport, 'You must supply an Email.settings.transports array containing at least one transport');
90+
91+
if(settings && settings.debug) {
92+
console.log('Sending Mail:');
93+
if(options.transport) {
94+
console.log('\t TRANSPORT:', options.transport);
95+
}
96+
console.log('\t TO:', options.to);
97+
console.log('\t FROM:', options.from);
98+
console.log('\t SUBJECT:', options.subject);
99+
console.log('\t TEXT:', options.text);
100+
console.log('\t HTML:', options.html);
101+
}
102+
103+
transport.sendMail(options, fn);
104+
}
105+
106+
/**
107+
* Send an email instance using `modelInstance.send()`.
108+
*/
109+
110+
Mailer.prototype.send = function (fn) {
111+
this.constructor.send(this, fn);
112+
}
113+
114+
/**
115+
* Access the node mailer object.
116+
*/
117+
118+
MailConnector.mailer =
119+
MailConnector.prototype.mailer =
120+
Mailer.mailer =
121+
Mailer.prototype.mailer = mailer;

lib/models/email.js

Lines changed: 1 addition & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
*/
44

55
var Model = require('../loopback').Model
6-
, loopback = require('../loopback')
7-
, mailer = require('nodemailer')
8-
, assert = require('assert');
6+
, loopback = require('../loopback');
97

108
/**
119
* Default Email properties.
@@ -24,87 +22,3 @@ var properties = {
2422
*/
2523

2624
var Email = module.exports = Model.extend('email', properties);
27-
28-
/*!
29-
* Setup the Email class after extension.
30-
*/
31-
32-
Email.setup = function (settings) {
33-
settings = settings || this.settings;
34-
var transports = settings.transports || [];
35-
36-
transports.forEach(this.setupTransport.bind(this));
37-
}
38-
39-
/**
40-
* Add a transport to the available transports. See https://github.com/andris9/Nodemailer#setting-up-a-transport-method.
41-
*
42-
* Example:
43-
*
44-
* Email.setupTransport({
45-
* type: 'SMTP',
46-
* host: "smtp.gmail.com", // hostname
47-
* secureConnection: true, // use SSL
48-
* port: 465, // port for secure SMTP
49-
* auth: {
50-
* user: "gmail.user@gmail.com",
51-
* pass: "userpass"
52-
* }
53-
* });
54-
*
55-
*/
56-
57-
Email.setupTransport = function (setting) {
58-
var Email = this;
59-
Email.transports = Email.transports || [];
60-
Email.transportsIndex = Email.transportsIndex || {};
61-
var transport = mailer.createTransport(setting.type, setting);
62-
Email.transportsIndex[setting.type] = transport;
63-
Email.transports.push(transport);
64-
}
65-
66-
/**
67-
* Send an email with the given `options`.
68-
*
69-
* Example Options:
70-
*
71-
* {
72-
* from: "Fred Foo ✔ <foo@blurdybloop.com>", // sender address
73-
* to: "[email protected], [email protected]", // list of receivers
74-
* subject: "Hello ✔", // Subject line
75-
* text: "Hello world ✔", // plaintext body
76-
* html: "<b>Hello world ✔</b>" // html body
77-
* }
78-
*
79-
* See https://github.com/andris9/Nodemailer for other supported options.
80-
*
81-
* @param {Object} options
82-
* @param {Function} callback Called after the e-mail is sent or the sending failed
83-
*/
84-
85-
Email.send = function (options, fn) {
86-
var transport = this.transportsIndex[options.transport || 'SMTP'] || this.transports[0];
87-
assert(transport, 'You must supply an Email.settings.transports array containing at least one transport');
88-
89-
transport.sendMail(options, fn);
90-
}
91-
92-
/**
93-
* Access the node mailer object.
94-
*
95-
* Email.mailer
96-
* // or
97-
* var email = new Email({to: 'foo@bar.com', from: '[email protected]'});
98-
* email.mailer
99-
*/
100-
101-
Email.mailer =
102-
Email.prototype.mailer = mailer;
103-
104-
/**
105-
* Send an email instance using `Email.send()`.
106-
*/
107-
108-
Email.prototype.send = function (fn) {
109-
this.constructor.send(this, fn);
110-
}

test/email.test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
var loopback = require('../');
2+
var MailConnector = loopback.Mail;
3+
var MyEmail = loopback.Email.extend('my-email');
4+
var assert = require('assert');
5+
6+
describe('Email and SMTP', function () {
7+
var mail = loopback.createDataSource({
8+
connector: MailConnector,
9+
transports: [
10+
{type: 'STUB'}
11+
]
12+
});
13+
14+
MyEmail.attachTo(mail);
15+
16+
it('should have a send method', function () {
17+
assert(typeof MyEmail.send === 'function');
18+
assert(typeof MyEmail.prototype.send === 'function');
19+
});
20+
21+
describe('MyEmail', function () {
22+
it('MyEmail.send(options, callback)', function (done) {
23+
var options = {
24+
25+
26+
subject: 'subject',
27+
text: 'text',
28+
html: '<h1>html</h1>'
29+
};
30+
31+
MyEmail.send(options, function(err, mail) {
32+
assert(mail.message);
33+
assert(mail.envelope);
34+
assert(mail.messageId);
35+
done(err);
36+
});
37+
});
38+
39+
it('myEmail.send(callback)', function (done) {
40+
var message = new MyEmail({
41+
42+
43+
subject: 'subject',
44+
text: 'text',
45+
html: '<h1>html</h1>'
46+
});
47+
48+
message.send(function (err, mail) {
49+
assert(mail.message);
50+
assert(mail.envelope);
51+
assert(mail.messageId);
52+
done(err);
53+
});
54+
});
55+
});
56+
});

test/user.test.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
var User = loopback.User.extend('user');
22
var Session = loopback.Session;
33
var passport = require('passport');
4+
var MailConnector = require('../lib/connectors/mail');
45

56
var userMemory = loopback.createDataSource({
67
connector: loopback.Memory
@@ -9,9 +10,13 @@ var userMemory = loopback.createDataSource({
910

1011
describe('User', function(){
1112

13+
var mailDataSource = loopback.createDataSource({
14+
connector: MailConnector,
15+
transports: [{type: 'STUB'}]
16+
});
1217
User.attachTo(userMemory);
1318
User.session.attachTo(userMemory);
14-
User.email.setup({transports: [{type: 'STUB'}]});
19+
User.email.attachTo(mailDataSource);
1520

1621
// allow many User.afterRemote's to be called
1722
User.setMaxListeners(0);

0 commit comments

Comments
 (0)