Sending a mail via NODE.JS in an IBM Cloud function - node.js

I have similar problem as here: PHP mail function doesn't complete sending of e-mail
But after several tries I don't think it's my solution...
Objective:
Create an action which can send email.
Code:
function main(params) {
const params = {
"payload": {
"id": "sender.address#gmail.com",
"password": "CodeForTheSenderAccount",
"receiver": "another.mail.address#gmail.com",
"subject": "Test Wikit",
"body": "<html>HELLO WORLD</html>"
}
}
const nodemailer = require('nodemailer');
//Creation of a SMTP transporter
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: params.payload.id,
pass: params.payload.password
}
});
//Creation of data to send
const mail = {
from: '"Wikitest" <' + params.payload.id + '>',
to: params.payload.receiver,
subject: params.payload.subject,
text: 'Hello World',
html: params.payload.body
}
//Sending the mail
return(transporter.sendMail(mail, function (err, info) {
if (err) {
const ret = {status: 'OK'};
} else {
const ret = {status: 'KO'};
}
transporter.close();
return (ret);
}));
}
This code works locally and I receive the email. But not when running the function in the IBM Cloud console.
I think it's due to SMTP servers but I'm not sure...
Some of you will see the "payload" param. It's because this action is in a sequence and the action before send the params.

When working with asynchronous JavaScript in serverless functions, you need to return and resolve a Promise. Here is relevant documentation for your example https://github.com/apache/incubator-openwhisk/blob/master/docs/actions-node.md#creating-asynchronous-actions.
return(new Promise(function(resolve, reject) {
transporter.sendMail(mail, function (err, info) {
if (err) {
const ret = {status: 'OK'};
} else {
const ret = {status: 'KO'};
}
transporter.close();
resolve(ret);
}}));

Related

SES.sendEmail in Lambda not calling Promise

When I make a call to ses.sendEmail the promise is never called. This is function in my lambda layer being called by my lambda function.
Here's the sendEmail function I have..
var aws = require ('aws-sdk')
var ses = new aws.SES ({region: 'us-west-2'});
exports.sendEmail = async (to, from, subject, body) => {
var params = {
Destination: {
ToAddresses : [to]
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data : body
}
},
Subject: {
Charset: "UTF-8",
Data : subject
}
},
Source: from
}
console.log ("Sending email with params (stringify) : ", JSON.stringify(params));
console.log ("SES = ", ses);
await ses.sendEmail (params, function (err, data) {
console.log ("inside email");
});
}
I see the "Sending email with params (stringify)" log output and the "SES = " log output (which shows what appears to be a valid SES). But I never see "inside email" in the log nor do I receive any emails.
I'm also outside of the ses sandbox as I've gotten approval after setting up my domain and successfully verifying it all. I am on the us-west-2 region. I have not verified any email addresses. Just the domain (ie. name#user.host... user.host is verified) as I assumed if the domain is verified then any email from that domain should be good for use in the "from".
I have used async await instead of promises. I was facing the same issue. Email is dispatched successfully in my local computer but when I deploy the lambda and start testing it live. Emails were not sent to the email address mentioned.
async function sendMail(content, email) {
const params = {
Destination: {
ToAddresses: [email.toLowerCase()],
},
Message: {
Body: {
Text: {
Charset: "UTF-8",
Data: content.text,
},
Html: {
Charset: "UTF-8",
Data: content.html,
},
},
Subject: {
Charset: "UTF-8",
Data: content.subject,
},
},
Source: secretManagerData.senderEmail,
};
const sendPromise = await AWS_SES.sendEmail(params).promise();
console.log(sendPromise);
}
There are mistakes in calling the functions. When you call the function, you don't need to give a space with it.
var aws = require('aws-sdk'); // these are functions, call them without space
var ses = new aws.SES({region: 'us-west-2'}); // this
exports.sendEmail = async (to, from, subject, body) => {
try {
var params = {
Destination: {
ToAddresses : [to]
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data : body
}
},
Subject: {
Charset: "UTF-8",
Data : subject
}
},
Source: from
}
console.log("Sending email with params (stringify) : ", JSON.stringify(params));
console.log("SES = ", ses);
const data = await sendmail(params);
return data;
} catch (e) {
console.log(e);
}
}
const sendmail = (params) => {
return new Promise((res, rej) => {
sendEmail(params = {}, (err, data)=>{
if (err) rej(err);
else res(data);
})
})
}
When you have to use async/await, you don't need to use callback and always use try/catch with async/await.
I got somehow same issue, it was resolved after used await before invoke sendEmail with promise and added API-Version for SES object.
const AWS = require ('aws-sdk')
const SES = new AWS.SES({apiVersion: '2010-12-01'});
try{
await SES.sendEmail(params).promise();
console.log("Email sent successfully");
}catch(err){
console.log("Failed to send email");
}

Using Jade mail template in Nodemailer

I have a contactform created with Nodemailer. Now I want a Jade tempate mail being send whenever the customer submits the contactform.
I already got it working and the mail template is already being send, but somehow the content of the Jade file is being presented in the 'subject' header of the mail. And everyting is presented with all the HTML tags. So, somewhere it goes wrong.
This is my Nodemailer code:
router.post('/contact/send', function(req, res) {
var transporter = nodeMailer.createTransport({
service : 'Gmail',
auth : {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_PASS
}
});
var mailOptions = {
from: req.body.name + ' <' + req.body.email + '>',
to: 'xxxxx#gmail.com',
subject:'Website verzoek',
text:'Er is een website verzoek binnengekomen van '+ req.body.name+' Email: '+req.body.email+'Soort website: '+req.body.website+'Message: '+req.body.message,
html:'<p>Websiteverzoek van: </p><ul><li>Naam: '+req.body.name+' </li><li>Email: '+req.body.email+' </li><li>Soort website: '+req.body.website+' </li><li>Message: '+req.body.message+' </li></ul>'
};
transporter.sendMail(mailOptions, function (err, info) {
if(err) {
console.log(err);
res.redirect('/#contact');
} else {
console.log('Message send');
res.redirect('/#contact');
}
});
var toAddress = req.body.email;
var sendMail = function(toAddress, subject, content, next) {
var mailTemplate = {
from: 'xxxxxx#gmail.com',
to: toAddress,
subject: subject,
html: content
};
transporter.sendMail(mailTemplate, next);
};
var template = process.cwd() + '/views/mails/mail.jade';
fs.readFile(template, 'utf8', function(err, file) {
if (err) {
console.log('Error');
} else {
var compiledTmpl = jade.compile(file, {filename: template});
var context = {title: 'Express'};
var html = compiledTmpl(context);
sendMail(toAddress, html, function(err, response) {
if(err) {
console.log('ERROR!');
} else {
console.log('Template send');
}
});
}
});
});
The problem is a typo mistake. Your sendMail function takes subject as second paramter.
var sendMail = function(toAddress, subject, content, next) {
var mailTemplate = {
from: 'xxxxxx#gmail.com',
to: toAddress,
subject: subject,
html: content
};
transporter.sendMail(mailTemplate, next);
};
Your are passing the compiled html as a second parameter to the function. So it takes the html as header.
sendMail(toAddress, html, function(err, response) {
if(err) {
console.log('ERROR!');
} else {
console.log('Template send');
}
});
Cheers.

SendGrid Azure Issue

I'm trying to send an email with my Node JS app on Azure and get this error:
TypeError: sendgrid.Email is not a constructor
Here is my code. I used the documentation from Microsoft (https://learn.microsoft.com/en-us/azure/store-sendgrid-nodejs-how-to-send-email).
var sendgrid = require('sendgrid')('SendGrid User ID', 'SendGrid password');
function createEmail() {
console.log('CREATE EMAIL');
var emailToSend = new sendgrid.Email({
to: example#example.com,
from: 'example#example.com',
subject: 'Subject',
text: 'some text';
});
sendEmail(emailToSend);
}
function sendEmail(email) {
console.log('SEND EMAIL');
sendgrid.send(email, function (err, json) {
if (err) {
return console.error(err);
}
});
}
As #David Tansey mentioned, the SendGrid team added a breaking change to support the v3 Web API since v3.0.0. Here is a working code example with the latest version (v5.1.2).
var helper = require('sendgrid').mail;
var fromEmail = new helper.Email('test#example.com');
var toEmail = new helper.Email('test#example.com');
var subject = 'Sending with SendGrid is Fun';
var content = new helper.Content('text/plain', 'and easy to do anywhere, even with Node.js');
var mail = new helper.Mail(fromEmail, subject, toEmail, content);
var sg = require('sendgrid')(process.env.SENDGRID_API_KEY);
var request = sg.emptyRequest({
method: 'POST',
path: '/v3/mail/send',
body: mail.toJSON()
});
sg.API(request, function (error, response) {
if (error) {
console.log('Error response received');
}
console.log(response.statusCode);
console.log(response.body);
console.log(response.headers);
});
However, if you want your provided code run smoothly, you'll need to revert the version to 2.0.0.

Gmail API for sending mails in Node.js

Disclaimer:
I have followed Google's own Node.js quickstart guide and successfully connect and use the gmail.users.labels.list() functionality.
I have checked for questions/answers here, like this one (that is not using the Node.js API I am asking about), or this one (similar to this one) which apparently is the same problem I have but the solution does not work.
My problem:
When using Google's Node.js API I get a error trying to send a email. The error is:
{
"code": 403,
"errors": [{
"domain": "global",
"reason": "insufficientPermissions",
"message": "Insufficient Permission"
}]
}
My setup:
fs.readFile(secretlocation, function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
authorize(JSON.parse(content), sendMessage);
});
function sendMessage(auth) {
var raw = makeBody('myrealmail#gmail.com', 'myrealmail#gmail.com', 'subject', 'message test');
gmail.users.messages.send({
auth: auth,
userId: 'me',
message: {
raw: raw
}
}, function(err, response) {
res.send(err || response)
});
}
The function processClientSecrets is from the Google guide i mentioned above. It reads my .json file that has my access_token and refresh_token. The makeBody function is a to make a encoded body message.
In the config variabels I have also:
var SCOPES = [
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.send'
];
Why it should work:
the authorization process works for the gmail.users.labels.list() method.
the message body I'm testing works if I test it at Google's test page.
My question:
Is my setup wrong? Have there been changes in the API? What am I missing?
Ok, so I found the problem(s).
Problem #1
While following the Node.js quickstart guide the example in that tutorial has
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
And when I got the .json that looks like:
{
"access_token": "xxx_a_long_secret_string_i_hided_xxx",
"token_type": "Bearer",
"refresh_token": "xxx_a_token_i_hided_xxx",
"expiry_date": 1451721044161
}
those tokens where produced taking into account only the auth/gmail.readonly scope in the tutorial code.
So I deleted the first .json, added the scopes from my final scope array (i posted in the question) and ran the tutorial setup again, receiving a new token.
Problem #2
In the object passed to the API I was sending:
{
auth: auth,
userId: 'me',
message: {
raw: raw
}
}
but that is wrong, message key should be called resource.
Final setup:
This is what I added to the tutorial's code:
function makeBody(to, from, subject, message) {
var str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",
"MIME-Version: 1.0\n",
"Content-Transfer-Encoding: 7bit\n",
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message
].join('');
var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
return encodedMail;
}
function sendMessage(auth) {
var raw = makeBody('myrealemail#gmail.com', 'myrealemail#gmail.com', 'test subject', 'test message');
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
res.send(err || response)
});
}
And call everything with:
fs.readFile(secretlocation, function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), sendMessage);
});
So for anyone looking at this trying to get a test email sent from their API but cant get this work heres what you gotta do:
Step 1:
Replace the
var SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
with this:
var SCOPES = [
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.send'
];
Step 2:
At the end of googles sample code add this:
function makeBody(to, from, subject, message) {
var str = ["Content-Type: text/plain; charset=\"UTF-8\"\n",
"MIME-Version: 1.0\n",
"Content-Transfer-Encoding: 7bit\n",
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message
].join('');
var encodedMail = new Buffer(str).toString("base64").replace(/\+/g, '-').replace(/\//g, '_');
return encodedMail;
}
function sendMessage(auth) {
var raw = makeBody('Receiverofyouremail#mail.com', 'whereyouaresendingstufffrom#gmail.com', 'This is your subject', 'I got this working finally!!!');
const gmail = google.gmail({version: 'v1', auth});
gmail.users.messages.send({
auth: auth,
userId: 'me',
resource: {
raw: raw
}
}, function(err, response) {
return(err || response)
});
}
fs.readFile('credentials.json', function processClientSecrets(err, content) {
if (err) {
console.log('Error loading client secret file: ' + err);
return;
}
// Authorize a client with the loaded credentials, then call the
// Gmail API.
authorize(JSON.parse(content), sendMessage);
});
Step 3(Optional)
Delete this line:
authorize(JSON.parse(content), listLabels);
And these:
/**
* Lists the labels in the user's account.
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listLabels(auth) {
const gmail = google.gmail({version: 'v1', auth});
gmail.users.labels.list({
userId: 'me',
}, (err, res) => {
if (err) return console.log('The API returned an error: ' + err);
const labels = res.data.labels;
if (labels.length) {
console.log('Labels:');
labels.forEach((label) => {
console.log(`- ${label.name}`);
});
} else {
console.log('No labels found.');
}
});
}
(So you don't get the random labels in your console)

Closing a connection in nodemailer

I am using nodemailer to send e-mails in nodejs. I am able to send the mails, but the script doesn't terminate. I don't know how to close the connection.
This is the code:
var nodemailer = require('nodemailer');
nodemailer.SMTP = {
host: 'localhost'
}
nodemailer.send_mail(
{
sender: 'me#example.com',
to:'my_gmail_nickname#gmail.com',
subject:'Hello!',
html: 'test',
body:'test'
},
function(error, success){
console.log(error);
console.log(success);
console.log('Message ' + success ? 'sent' : 'failed');
});
I got it working like this:
var nodemailer = require('nodemailer');
var transport = nodemailer.createTransport("SMTP", {
host: 'localhost',
});
var send_email = function (email_content) {
var mailOptions = {
from: 'me#example.com',
to: 'my_gmail_nickname#gmail.com',
subject: 'Hello!',
html: email_content.content
};
transport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log('Message sent: ' + info.message);
transport.close();
}
})
};

Resources