nodejs exporting function email.send doesn't execute - node.js

Have mailer.js below but when importing 'var email = require('./models/mailer')' my 'email.send' doesn't execute ?
var config = require('./config');
var email = require('emailjs');
var server = email.server.connect({
host: config.smptRelay
});
module.exports = function send() {
server.send({
text: config.emailText,
from: config.emailFrom,
to: config.emailTo,
subject: config.emailSubject
}, function (err, message) { /*console.log(err || message); */
});
};

You're exporting the function directly, as module.exports, and not as a property of it. Just call it as email().

Related

How do you query data using Mongoose within a service function?

I have a service in my node application where I am trying to use nodemailer to send an email with a link to clients. The issue I am having is that when I try to query the recipient email address using Mongoose in the service I get this response message in the console log Promise { <pending> } and obviously my email does not send because I am not returning an email address. I am using the code below as the service that is called from my controller. It looks like everything is working fine in the controller because when I use a test email address as the recipient email the message is sent and function returns no errors. Am I writing the function to query the email wrong or is there a better way I should be setting this up?
var nodemailer = require('nodemailer');
const Obrf = require("../models/Obrf");
const sendLink = (x) => {
var id = x;
const offerid = id.toString();
const url = 'http://localhost:3000/offerletter/' + offerid
async function returnEmail (y) {
const email = await Obrf.findById(y, 'email_address').exec();
return email;
};
const sendToEmail = returnEmail(id);
console.log(sendToEmail);
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'sender#gmail.com',
pass: 'password'
}
});
var mailOptions = {
from: 'sender#gmail.com',
to: sendToEmail,
subject: 'Congratulations on your offer!',
text: 'Go to: ' + url + ' to sign your offer letter!'
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
};
module.exports = {
sendLink,
};
async before a function makes the function return a promise:
are you sure if Obrf.findById(y, 'email_address').exec() returning promise ?
if not remove the aysnc await
function returnEmail (y) {
const email = Obrf.findById(y, 'email_address').exec();
return email;
};
const sendToEmail = returnEmail(id);
if its returning the promise then do something like this
const sendToEmail = returnEmail(id).then(id=>id);

Nodejs and Mailgun: Send generated attachment *not* from file system

I generate attachments in a node app and would like to send them using Mailgun. I have no access to the file system (Netlify functions).
Is there an easy way to accomplish that?
The hole picture
It's a Jamstack (Gatsby/React, Netlify, Mailgun) web app. Customers configure and request offers. Browser generates and posts offers and images to a Netlify function (fetch api). The function send the offer mail with the PDF-offer and images attached.
Code I tested (edit: 02/25)
const path = require('path');
const fs = require('fs');
const mailgun = require('mailgun-js')
const FormData = require('form-data');
const { Readable } = require('stream');
const API_KEY = 'SECRET';
const DOMAIN = 'brasilius.de';
const mg = mailgun({apiKey: API_KEY, domain: DOMAIN, host: "api.eu.mailgun.net"});
const stream = fs.createReadStream('test.txt'); // This works
/* {
id: '<20210225115125.1.BF14CC322F8E0DAC#brasilius.de>',
message: 'Queued. Thank you.'
} */
/*
const stream = Readable.from(["Send this text as attachment."]) // Won't work
{ message: "'from' parameter is missing" }
*/
const data = {
from: 'Excited User <me#brasilius.de>',
to: 'test#user.de',
subject: 'Hello',
text: 'Testing some Mailgun awesomeness!',
attachment: stream
};
mg.messages().send(data, (error, body) => {
console.log(body);
});
The simplest solution I found here
// https://thecodebarbarian.com/sending-emails-using-the-mailgun-api.html
const mailgun = require('mailgun-js')
const mg = mailgun({apiKey: process.env.API_KEY, domain: process.env.DOMAIN, host: "api.eu.mailgun.net"});
const filename = 'test.txt';
const text = "Example test content."
const attch = new mg.Attachment({data: Buffer.from(text), filename: filename})
const data = {
from: process.env.FROM,
to: process.env.TO,
subject: 'Hello',
text: 'Testing Mailgun attachments.',
attachment: attch
};
mg.messages().send(data, (error, body) => {
console.log(body);
});
As #Joe recommended, a solution with Nodemailers Mailcomposer:
// See: https://documentation.mailgun.com/en/latest/api-sending.html#examples
// See: http://nodemailer.com/extras/mailcomposer/#attachments
const mailgun = require('mailgun-js')
const MailComposer = require('nodemailer/lib/mail-composer');
const mg = mailgun({apiKey: process.env.API_KEY, domain: process.env.DOMAIN, host: "api.eu.mailgun.net"});
const mailOptions = {
from: process.env.FROM,
subject: 'Hello',
text: 'Testing Mailgun attachments.',
attachments: [
{ // utf-8 string as an attachment
filename: 'text.txt',
content: 'For testing just a text file. This could be a ReadStream, Buffer or other.'
}
]
};
const mail = new MailComposer(mailOptions);
mail.compile().build(function(mailBuildError, message) {
var dataToSend = {
to: process.env.TO,
message: message.toString('ascii')
};
mg.messages().sendMime(dataToSend, function(sendError, body) {
if (sendError) {
console.log(sendError);
return;
}
});
});

AWS Lambda not firing Email via nodemailer, but does in the local development environment

I'm working on a aws-lambda which is supposed to shoot mail when an event is triggered. I using nodejs for this and below is the code:
"use strict";
exports.sendEmail = function(event, context, callback) {
var config = require('./config');
var fs = require('fs');
var _ = require('lodash');
if (_validSchema(event.payload)) {
var templatePath = config.schemaMapping[event.payload.emailDetails.emailType]["templatePath"]
var emailHTML = _getHTML(templatePath, event.payload.params)
if (emailHTML && templatePath) {
_sendSESEmail(_emailParams(event.payload.emailDetails), emailHTML)
context.succeed(JSON.stringify(_setResponse(200, [{
code: "11",
source: "Email template or Email params in payload",
message: "Please provide correct Email template and correct email params",
detail: "Template path is provided via config and Params via Payload"
}])));
} else
context.fail(JSON.stringify(_setResponse(400, [{
code: "01",
source: "Email template or Email params in payload",
message: "Please provide correct Email template and correct email params",
detail: "Template path is provided via config and Params via Payload"
}])));
} else {
context.fail(JSON.stringify(_setResponse(400, [{
code: "02",
source: "Payload schema",
message: "Please provide correct schema to validate and a payload validating it",
detail: "Payload is provided "
}])));
}
function _validSchema(payload) {
var schemaPath = config.schemaMapping[payload.emailDetails.emailType]["schemaPath"];
var payloadVerification = _verifyPayload(payload, schemaPath);
console.log(payloadVerification.valid);
return payloadVerification.valid;
}
function _emailParams(emailDetails) {
var details = {};
details.to = _.join(emailDetails.to, ',');
details.from = emailDetails.from;
details.cc = _.join(emailDetails.cc, ',');
details.bcc = _.join(emailDetails.bcc, ',');
details.attachments = emailDetails.attachments;
details.subject = emailDetails.subject;
return details;
}
function _verifyPayload(payload, schemaPath) {
var schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
var Validator = require('jsonschema').Validator;
var verifier = new Validator();
console.log(verifier.validate(payload, schema))
return verifier.validate(payload, schema);
}
function _setResponse(status_code, error_list) {
return {
status: status_code,
errors: error_list
};
}
function _sendSESEmail(email, emailHTML) {
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var transporter = nodemailer.createTransport(sesTransport({
accessKeyId: config.SES.accessKeyId,
secretAccessKey: config.SES.secretAccessKey
}));
transporter.sendMail({
from: email.from,
to: email.to,
cc: email.cc,
bcc: email.bcc,
attachments: email.attachments,
subject: email.subject,
html: emailHTML
});
}
function _getHTML(templateFile, params) {
var ejs = require('ejs');
console.log({ params: params })
var baseHTML = fs.readFileSync(templateFile, 'ascii');
return ejs.render(baseHTML, { params: params });
}
}
Above code works fine when tested in the dev environment with the below code, but does not fire a mail when tested on aws-lamda.
"use strict";
var exports = require('./exports');
var bankDetailsSchemaSample = {
"payload": {
"emailDetails": {
"from": 'some#something.com',
"to": ['kunal#something.com'],
"subject": 'My Amazon SES Simple Email',
"html": '',
"cc": ['nimesh.verma#something.com'],
"bcc": ['lokesh.gour#something.com'],
"emailType": 'bankDetails',
"attachments": [{
"filename": 'test.md',
"path": 'https://raw.github.com/nodemailer/nodemailer/master/LICENSE'
}]
},
"params": {
"orderId": 1234567,
"firstName": "Nimesh",
}
}
}
var context = {
fail: function(x) { console.log(" Fail " + x) },
succeed: function(x) { console.log(" Success " + x) }
}
exports.sendEmail(bankDetailsSchemaSample, context, {})
I can't find out, why this is happening, I also tried it using nodemailer-smtp-transport instead of nodemailer-ses-transport but the same results were obtained. When nothing helped I tried using aws-sdk instead of nodemailer and nodemailer-ses-transport and the mail is fired in both dev environment as well via aws lamda testing.
// load aws sdk
exports.sendEmail = function(event, context, callback) {
var aws = require('aws-sdk');
// load aws config
aws.config.loadFromPath('config.json');
// load AWS SES
var ses = new aws.SES({ apiVersion: '2010-12-01' });
// send to list
var to = ['nimesh.verma#something.com']
// this must relate to a verified SES account
var from = 'some#something.com'
// this sends the email
// #todo - add HTML version
ses.sendEmail({
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'A Message To You Rudy'
},
Body: {
Text: {
Data: 'Stop your messing around',
}
}
}
}, function(err, data) {
if (err) throw err
console.log('Email sent:');
});
}
Why is this happening?
The problem was that the context.succeed method should be placed in the callback of _sendSESEmail method.
The complete working code is present at: https://github.com/nimeshkverma/aws-lambda-node-mailer

How to include an html email template in a node js application

I have an html template that we use to send to new website registrations. It is a simple html file that I would like to load into a variable so that I can replace certain parts before sending out using nodemailer (eg [FIRST_NAME]). I am trying to avoid having to paste a large chunk of html into my exports function. Any ideas on how I could go about doing that?
For a clearer idea, what I need to know is how to actually do this:
var first_name = 'Bob';
var html = loadfile('abc.html').replace('[FIRST_NAME]', first_name);
Here's an example of how to do it using ejs, but you can use any templating engine:
var nodemailer = require("nodemailer");
var ejs = require('ejs');
var transport = nodemailer.createTransport("SMTP", {
service: <your mail service>,
auth: {
user: <user>,
pass: <password>
}
});
function sendMail(cb) {
var user = {firstName : 'John', lastName: 'Doe'};
var subject = ejs.render('Hello <%= firstName %>', user);
var text = ejs.render('Hello, <%= firstName %> <%= lastName %>!', user);
var options = {
from: <from>,
replyTo: <replyto>,
to: <to>,
subject: subject,
text: text
};
transport.sendMail(options, cb);
}
To load the template file just us the fs module. Here's how to do it synchronously when the file is encoded in utf-8:
var fs = require('fs');
var template = fs.readFileSync('abc.html',{encoding:'utf-8'});
Maybe this will be useful for someone, as this question is already answered.
I'm working with jade and it was quite challenging figuring it out, at the end turned out to be very simple :)
(PS: this code is not optimized, is just an example)
Js part with nodemailer:
var nodemailer = require('nodemailer')
var jade = require('jade');
var config = {
// config for sending emails like username, password, ...
}
var emailFrom = 'this#email.com';
var emailTo = 'this#email.com';
var templateDir = 'path/to/templates/';
var transporter = nodemailer.createTransport(config);
var username = 'thisUsername'
// rendering html template (same way can be done for subject, text)
var html = jade.renderFile(templateDir+'/html.jade', {username: 'testUsername'});
//build options
var options = {
from: emailFrom,
to: emailTo,
subject: 'subject',
html: html,
text:'text'
};
transporter.sendMail(options, function(error, info) {
if(error) {
console.log('Message not sent');
console.log(info);
return false;
}
else{
console.log('Message sent: ' + info.response);
console.log(info);
return true;
};
});
html.jade
p test email html jade
p
| Username:
| !{username}
Here is the example for using email-templates and nodemailer.
js file:
var path = require('path');
var EmailTemplate = require('email-templates').EmailTemplate;
var transporter = nodemailer.createTransport(config);
var templateDir = path.join(__dirname, '/yourPath/emailTemplates', 'subdir');
var template = new EmailTemplate(templateDir)
var username = 'testUsername';
var transport = nodemailer.createTransport(config)
template.render(locals, function (err, results) {
if (err) {
return console.error(err)
}
// replace values in html template
console.log('template render')
console.log(err);
// default is results.html in this case
// read template and replace desired values
var res1 = results.html.toString();
var str = res1.replace('__username__', username);
console.log(str);
console.log('end template render')
transport.sendMail({
from: emailFrom,
to: emailTo,
subject: 'subject',
html: str,
text: results.text
}, function (err, responseStatus) {
if (err) {
return console.error(err)
}
console.log(responseStatus)
})
})
html.html
test email html
username:
<div>
__username__
</div>

Mocking email function in nodejs

I've got a mailer function I've built and trying to shore up the coverage. Trying to test parts of it have proven tricky, specifically this mailer.smtpTransport.sendMail
var nodemailer = require('nodemailer')
var mailer = {}
mailer.smtpTransport = nodemailer.createTransport('SMTP', {
'service': 'Gmail',
'auth': {
'XOAuth2': {
'user': 'test#test.com',
'clientId': 'googleClientID',
'clientSecret': 'superSekrit',
'refreshToken': '1/refreshYoSelf'
}
}
})
var mailOptions = {
from: 'Some Admin <test#tester.com>',
}
mailer.verify = function(email, hash) {
var emailhtml = 'Welcome to TestCo. Click this '+hash+''
var emailtxt = 'Welcome to TestCo. This is your hash: '+hash
mailOptions.to = email
mailOptions.subject = 'Welcome to TestCo!'
mailOptions.html = emailhtml
mailOptions.text = emailtxt
mailer.smtpTransport.sendMail(mailOptions, function(error, response){
if(error) {
console.log(error)
} else {
console.log('Message sent: '+response.message)
}
})
}
I'm unsure of how to go about testing, specifically ensuring that my mailer.smtpTransport.sendMail function is passing the correct parameters without actually sending the email. I'm trying to use https://github.com/whatser/mock-nodemailer/tree/master, but I'm probably doing it wrong. Should I be mocking out the method?
var _ = require('lodash')
var should = require('should')
var nodemailer = require('nodemailer')
var mockMailer = require('./helpers/mock-nodemailer')
var transport = nodemailer.createTransport('SMTP', '')
var mailer = require('../../../server/lib/account/mailer')
describe('Mailer', function() {
describe('.verify()', function() {
it('sends a verify email with a hashto an address when invoked', function(done) {
var email ={
'to': 'dave#testco.com',
'html': 'Welcome to Testco. Click this bleh',
'text': 'Welcome to Testco. This is your hash: bleh',
'subject': 'Welcome to Testco!'
}
mockMailer.expectEmail(function(sentEmail) {
return _.isEqual(email, sentEmail)
}, done)
mailer.verify('dave#testco.com','bleh')
transport.sendMail(email, function() {})
})
})
You can use a 'Stub' transport layer on your test instead of SMTP.
var stubMailer = require("nodemailer").createTransport("Stub"),
options = {
from: "from#email.com",
to: "to#email.com",
text: "My Message!"
};
stubMailer.sendMail(options, function(err, response){
var message = response.message;
})
So, in that case, 'message' will be the email in text format. Something like this:
MIME-Version: 1.0
X-Mailer: Nodemailer (0.3.43; +http://www.nodemailer.com/)
Date: Fri, 25 Feb 2014 11:11:48 GMT
Message-Id: <123412341234.e23232#Nodemailer>
From: from#email.com
To: to#email.com
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
My Message!
For more examples, take a look at nodemailer test suite:
https://github.com/andris9/Nodemailer/blob/master/test/nodemailer-test.js
You can directly mock the sendMail function but it's not obvious how to access it from the tests. A Mailer instance is returned when you create a transport so you need to directly import that class in to your test.
const Mailer = require('nodemailer/lib/mailer')
Then you can mock or stub the sendMail method on the prototype in the usual way. Using Jasmine, you can do it like this:
beforeEach(function () {
spyOn(Mailer.prototype, 'sendMail').and.callFake(function (mailOptions, cb) {
cb(null, true)
})
})
The callFake ensures that the sendMail's callback is still executed encase you need to test what happens next. You can easily simulate an error by passing a first argument to cb: cb(new Error('Email failed'))
Now that the mock is set up, you can check everything is working as intended:
expect(Mailer.prototype.sendMail).toHaveBeenCalled()
expectEmail simply hooks into the transport layer, and expects you to identify the email ( return true if this is the email you are expecting ) by looking at the sentEmail contents.
In this case, return sentEmail.to === 'dave#testco.com' should suffice.
Keep in mind however, this module was designed in an environment where tests are ran in a random order and concurrently. You should propably randomize your data heavily to prevent collisions and false positives. BTW we use something like: var to = Date.now().toString(36) + Faker.Internet.email();
This example works fine for me
======== myfile.js ========
// SOME CODE HERE
transporter.sendMail(mailOptions, (err, info) => {
// PROCESS RESULT HERE
});
======== myfile.spec.js (unit test file) ========
const sinon = require('sinon');
const nodemailer = require('nodemailer');
const sandbox = sinon.sandbox.create();
describe('XXX', () => {
afterEach(function() {
sandbox.restore();
});
it('XXX', done => {
const transport = {
sendMail: (data, callback) => {
const err = new Error('some error');
callback(err, null);
}
};
sandbox.stub(nodemailer, 'createTransport').returns(transport);
// CALL FUNCTION TO TEST
// EXPECT RESULT
});
});

Resources