Can't send Emails with attachments with nodemailer - node.js

I have the following function for sending emails with attachments using nodemailer, but sometimes It returns error enoent, the file path can't be found even if it exists. Can you tell me where is my mistake?
function sendEmail(userEmail, htmlString, requestSnap, FIREBASE_WEB) {
fileName ="test.pdf";
folderName = "./" + uuid.v4();
mkdirp(folderName, function(err) {
if (err) console.error(err)
else console.log(folderName + ' folder created!')
});
pdf.create(htmlString + userEmail, options).toFile(folderName + '/' + fileName, function(err, res) { // if the file doesnt exist it will be created
if (err) return console.log(err);
console.log(res);
});
var transporter = nodemailer.createTransport(smtpTransport({
service: 'Gmail',
auth: {
user: '...',
pass: '...'
}
}));
console.log("\nPATH " + folderName + "/" + fileName);
var mailOptions = {
from: 'marija.lukaroska.cw#gmail.com',
to: userEmail,
subject: 'So mail vo pdf-ot',
text: 'Hellow',
attachments: [{
path: folderName + "/" + fileName
}]
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log("ERROR kkkk " + error);
} else {
console.log('Email sent: ' + info.response);
console.log("REQUEST SNAP " + JSON.stringify(requestSnap));
}
deleteFolderRecursive(folderName);
});
}
Error log:
ERROR kkkk Error: ENOENT: no such file or directory, open 'C:\Users\asd\Documents\Projects\asd\asd\010a3e0f-2f16-4227-a886-873a8529737f\asd.pdf'
the path exists

As node Js is single threaded, event driven, this seems to be an issue of chaining your functions appropriately.
Your PDF creation code is taking time to return but by that time your send mail code is already called and it finds the folder is not yet created.
Try this:
function sendEmail(userEmail, htmlString, requestSnap, FIREBASE_WEB) {
fileName = "test.pdf";
folderName = "./" + uuid.v4();
mkdirp(folderName, function (err) {
if (err) console.error(err)
else console.log(folderName + ' folder created!')
});
pdf.create(htmlString + userEmail, options).toFile(folderName + '/' + fileName, function (err, res) { // if the file doesnt exist it will be created
if (err) return console.log(err);
console.log(res);
var transporter = nodemailer.createTransport(smtpTransport({
service: 'Gmail',
auth: {
user: '...',
pass: '...'
}
}));
console.log("\nPATH " + folderName + "/" + fileName);
var mailOptions = {
from: 'marija.lukaroska.cw#gmail.com',
to: userEmail,
subject: 'So mail vo pdf-ot',
text: 'Hellow',
attachments: [{
path: folderName + "/" + fileName
}]
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log("ERROR kkkk " + error);
} else {
console.log('Email sent: ' + info.response);
console.log("REQUEST SNAP " + JSON.stringify(requestSnap));
}
deleteFolderRecursive(folderName);
});
});
}

Related

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.

NodeJs application stopping abrubtly while async file copy

I am trying to write a file copy code in Node.js:
function archiveFolder(site, callback) {
//console.log("Processing Site2 " + site );
var path = srcDir + '/' + site;
var startDate = moment(new Date(), 'YYYYMMDD');
asyncCounter++;
console.log("getting list of files for site " + site);
fse.readdir(path, function(err, files) {
console.log("got list of files for site " + site);
if (err) {
console.log(err);
return callback(err);
}
async.eachLimit(files, 5, function(file, callback2) {
var endDate = moment(file, 'YYYYMMDD');
var diff = startDate.diff(endDate, 'days');
fs.stat(path + '/' + file, function(err, stats) {
try {
if (err) {
return callback2(err);
}
if (/^\d{8}$/i.test(file) && stats.isDirectory() && diff >= 7) {
fse.ensureDir(destDir + '/' + site, function(err) {
fse.copy(path + '/' + file, destDir + '/' + site + '/' + file, function(err) {
try {
if (err) {
//console.log(err);
return callback2(err);
} else {
console.log("copied " + site + '/' + file);
return callback2();
}
} catch (e) {
console.log(e);
return callback2(err);
}
});
});
} else {
// //console.log("failed " + path + '/' + file)
return callback2();
}
} catch (e) {
console.log(e);
return callback2(err);
}
});
}, function(err) {
if (!err) {
//console.timeEnd("site");
console.log("finished Site " + site + " asyncCounter " + asyncCounter);
return callback();
} else {
return callback(err + " test");
}
});
});
}
Still there isn't any error message on console or the system logs /var/log/messages
What could be the reason for this behavior? I can increase the ulimit on the server but I want to confirm that this is due to this "number of files open" limit.

Nodemailer not sending response

I'm working on my first express project. I'm using Nodemailer and it's sending emails just fine but I'm not getting a response. Any insights? Here is the function I'm using to send the emails. It console logs message sent just fine, but doesn't seem to respond.
exports.email = function(req, res, options, tokens) {
receiver_id= options.email.receiver_id;
message= options.email.message;
sender_email = options.email.sender_email;
sender_name = options.email.sender_name;
slug=options.info.slug;
org_name=options.info.org_name;
dir_url=options.info.dir_url;
var https = require('https');
str = '';
//getting user email by id
path = '/api/v1/people/' + receiver_id +'?__proto__=&access_token=' + tokens[slug];
var options = {
host: slug + '.nationbuilder.com',
path: path,
method: "GET",
json: true,
headers: {
"content-type": "application/json",
"accept": "application/json"
},
}
var nb_req = https.get(options, req_callback);
function req_callback(response, res) {
response.on('data', function(chunk) {
str += chunk;
});
response.on('end', function() {
object = JSON.parse(str);
receiver_email = object.person.email1;
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
port:465,
host:"smtp.gmail.com",
auth: {
user: 'connect#alumninations.com', // Your email id
pass: '69$6FK$b6PQu' // Your password
}
});
var html = '<p>From: ' + sender_email + '<br>';
html += 'To: ' + receiver_email + '</p>';
html += "<p>"+message + "</p>"
html += "<p>This message was delivered by the " + org_name + " alumni directory website, " + "<a href='" + dir_url
+ "'>" + dir_url + "</p>";
var mailOptions = {
from: 'connect#alumninations.com', // sender address
to: 'petervankoughnett#gmail.com', // list of receivers
subject: "A message from " + sender_name, // Subject line
/*text: "Message sent to", // plaintext body*/
html: html
};
transporter.sendMail(mailOptions, function(error, info){
if(error){
console.log(error);
res.json({yo: 'error'});
}else{
console.log('Message sent: ' + info.response);
res.sendStatus(200);
};
return res.sendStatus(200);
});
});
}
}
You are missing res.end() after res.sendStatus(200);.
transporter.sendMail(mailOptions, function(error, info){
if (error){
console.log(error);
res.json({yo: 'error'});
res.sendStatus(500);
}else{
console.log('Message sent: ' + info.response);
res.sendStatus(200);
};
return res.end();
}) ;
The following code worked for me in the transporter verify and sendMail functions.
transporter.verify(function (error, success) {
if (error) {
console.log(error);
} else {
console.log("Server is ready to take our messages");
res.send(success);
res.status(200);
}
});
transporter.sendMail(mailData, function (err, info) {
if (err) {
console.log("error: ", err);
} else {
console.log("all good", info);
res.sendStatus(200);
}
return res.status(200).end();
});
}
This is the asynchronous problem of JavaScript. I also have the same problem. When you call the email function outside it will not return anything so you will get 'undefined', because you returned a response within a callback function.
router.post('/"your api path"',async(req,res)=>{
//remaining codes
transporter.sendMail(mailOptions, function(error, info){
if(error){
console.log(error);
res.status(400).json({yo: 'error'});
}else{
console.log('Message sent: ' + info.response);
res.sendStatus(200);
};
return res.sendStatus(200);
});
}
use res.status(200).json() instead:
transporter.sendMail(mailOptions, function(error, info){
if (error){
console.log(error);
res.json({yo: 'error'});
res.sendStatus(500);
}else{
console.log('Message sent: ' + info.response);
res.status(200).json({"msg": "mesage has been sent"})
};});

Nodejs Immediately delete generated file

I'm trying to delete a pdf immediately after is has been generated in node.js. The generated pdf is sent as an email attachment, uploaded to dropbox and then deleted from the local file system. But as i try to delete it , it does not delete it and it does not send the email either. The pdf is created using html-pdf. Here is my code :
if (result) {
var filename = user.number+ ".pdf";
var path = './public/files/'+filename ;
var options = { filename: path, format: 'Legal', orientation: 'portrait', directory: './public/files/',type: "pdf" };
html = result;
pdf.create(html, options).toFile(function(err, res) {
if (err) return console.log(err);
console.log(res);
});
var dbx = new dropbox({ accessToken: mytoken });
fs.readFile( path,function (err, contents) {
if (err) {
console.log('Error: ', err);
}
dbx.filesUpload({ path: "/"+filename ,contents: contents })
.then(function (response) {
console.log("done")
console.log(response);
})
.catch(function (err) {
console.log(err);
});
});
var mailOptions = {
from: 'xyz', // sender address
to: user.email, // list of receivers
subject: 'Confirmation received', // Subject line
attachments : [{
filename: filename,
path : path
}]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
});
fs.unlinkSync(path); // even tried fs.unlink , does not delete file
// fs.unlinkSync(someother file); this one works
}
So when i do fs.unlink' orfs.unlinkSync`, if the file is already there it works but the files that is generated as in path doesn't get deleted.
NodeJs is asynchronous so you need to handle every blocks properly. your code shows that some times before completely creating the PDF itself the file upload to dropbox will start if the PDF creation is slow.
And deletion of the PDF file happens before the mail sending, so you get some err but you did not logged you error in fs.unlink(). Divide you code as blocks and use callback for better performance and flow.
Your code should be like this to work properly..
if (result) {
var filename = user.number+ ".pdf";
var path = './public/files/'+filename ;
var options = { filename: path, format: 'Legal', orientation: 'portrait', directory: './public/files/',type: "pdf" };
html = result;
//Generate the PDF first
pdf.create(html, options).toFile(function(err, res) {
if (err){
return console.log(err);
} else {
//If success then read the PDF file and then upload to dropbox
var dbx = new dropbox({ accessToken: mytoken });
fs.readFile( path,function (err, contents) {
if (err) {
console.log('Error: ', err);
} else {
dbx.filesUpload({path: "/"+filename ,contents: contents }).then(function (response) {
// Once the file upload is done then send mail
console.log("done")
sendMail('xyz', user.email, 'Confirmation received', filename, path, function(err, result){
// once mail is successful then delete the file finally
fs.unlinkSync(path); //if you need you can use callback with this for confirmation of deletion
});
}).catch(function(err) {
console.log(err);
});
}
});
}
});
function sendMail(sender, receiver, subject, filename, path, callback){
var mailOptions = {
from: sender, // sender address
to: receiver, // list of receivers
subject: subject, // Subject line
attachments : [{
filename: filename,
path : path
}]
};
transporter.sendMail(mailOptions, (err, info) => {
if (error) {
callback(err, null);
} else {
console.log('Message %s sent: %s', info.messageId, info.response);
callback(null, info)
}
});
}
}

Converting AWS Lambda function to use promises?

I am writing a simple HTTP 'ping' function that is being periodically executed using AWS Lambda. It uses four asynchronous functions: http.get, S3.getObject, S3.putObject, and nodemailer.sendMail. Each seems to have a slightly different callback model.
After reading about promises, I spent way too much time trying to convert the following code to use Q promises and failed miserably.
For my own education and hopefully that of others, I was hoping someone could help me convert this to using promises (doesn't have to be Q):
'use strict';
var http = require('http');
var nodemailer = require('nodemailer');
var AWS = require('aws-sdk');
var s3 = new AWS.S3( { params: { Bucket: 'my-bucket' } } );
exports.handler = (event, context, callback) => {
var lastStatus;
var options = {
host: event.server.host,
port: event.server.port ? event.server.port : 80,
path: event.server.path ? event.server.path : '',
method: event.server.method ? event.server.method : 'HEAD',
timeout: 5000
};
var transporter = nodemailer.createTransport({
host: event.mail.host,
port: event.mail.port ? event.mail.port : 587,
auth: {
user: event.mail.user,
pass: event.mail.pass
}
});
var d = new Date();
var UTCstring = d.toUTCString();
// email templates
var downMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda DOWN alert: SITE (' + event.server.host + ') is DOWN',
text: 'LambdaAlert DOWN:\r\nSITE (' + event.server.host + ') is DOWN as at ' + UTCstring + '.'
};
var upMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda UP alert: SITE (' + event.server.host + ') is UP',
text: 'LambdaAlert UP:\r\nSITE (' + event.server.host + ') is UP as at ' + UTCstring + '.'
};
// Run async chain to ensure that S3 calls execute in proper order
s3.getObject( { Key: 'lastPingStatus' }, (err, data) => {
// get last status from S3
if (err) { lastStatus = "UP"; } else {
lastStatus = data.Body.toString();
console.log("Last observed status: " + lastStatus);
}
http_request(options, lastStatus);
});
function http_request(requestOptions, lastStatus) {
var req = http.request(requestOptions, function(res) {
if (res.statusCode == 200) {
if (lastStatus == "DOWN") {
console.log('Email up notice sending...');
transporter.sendMail(upMail, function(error, info) {
if (error) {
console.log("ERROR: " + error);
callback(null, "ERROR: " + error);
} else {
console.log('No further details available.');
callback(null, 'Up message sent');
}
});
}
s3.putObject({ Key: 'lastPingStatus', Body: 'UP', ContentType: 'text/plain' }, (error, data) => { console.log("Saved last state as UP"); });
callback(null, 'Website is OK.');
}
});
req.on('error', function(e) {
if (lastStatus == "UP") {
console.log('Email down notice sending...');
transporter.sendMail(downMail, function(error, info) {
if (error) {
console.log("ERROR: " + error);
callback(null, "ERROR: " + error);
} else {
console.log('No further details available.');
callback(null, 'Down message sent');
}
});
s3.putObject({ Key: 'lastPingStatus', Body: 'DOWN', ContentType: 'text/plain' }, (error, data) => { console.log("Saved last state as DOWN"); });
callback(null, 'Website is DOWN.');
}
});
req.end();
}
};
EDIT: First attempt at writing using promises:
'use strict';
var http = require('http');
var nodemailer = require('nodemailer');
var AWS = require('aws-sdk');
var s3 = new AWS.S3( { params: { Bucket: 'lambda-key-storage' } } );
exports.handler = (event, context, callback) => {
var lastStatus;
var options = {
host: event.server.host,
port: event.server.port ? event.server.port : 80,
path: event.server.path ? event.server.path : '',
method: event.server.method ? event.server.method : 'HEAD',
timeout: 5000
};
var transporter = nodemailer.createTransport({
host: event.mail.host,
port: event.mail.port ? event.mail.port : 587,
auth: {
user: event.mail.user,
pass: event.mail.pass
}
});
var d = new Date();
var UTCstring = d.toUTCString();
// email templates
var downMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda DOWN alert: SITE (' + event.server.host + ') is DOWN',
text: 'LambdaAlert DOWN:\r\nSITE (' + event.server.host + ') is DOWN as at ' + UTCstring + '.'
};
var upMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda UP alert: SITE (' + event.server.host + ') is UP',
text: 'LambdaAlert UP:\r\nSITE (' + event.server.host + ') is UP as at ' + UTCstring + '.'
};
var myProm = new Promise(function(resolve, reject) {
console.log("called 1");
s3.getObject( { Key: 'lastPingStatus' }, (err, data) => {
// get last status from S3
if (err) {
resolve("UP");
} else {
resolve(data.Body.toString());
}
});
})
.then(function(lastStatus) {
console.log("called 2");
console.log("Last observed status: " + lastStatus);
var req = http.request(options, function(res) {
resolve(res.statusCode);
});
req.on('error', function(e) {
reject(e);
});
req.end();
return "??";
})
.then(function(statusCode) {
console.log("called 3");
if (statusCode == 200) {
if (lastStatus == "DOWN") {
console.log('Email up notice sending...');
resolve("upTrigger");
} else {
resolve("upNoTrigger");
}
s3.putObject({ Key: 'lastPingStatus', Body: 'UP', ContentType: 'text/plain' }, (err, data) => { console.log("Saved last state as UP"); });
callback(null, 'Website is OK.');
}
})
.catch(function(err){
console.log("called 3 - error");
// Send mail notifying of error
if (lastStatus == "UP") {
console.log('Email down notice sending...');
resolve("downTrigger");
s3.putObject({ Key: 'lastPingStatus', Body: 'DOWN', ContentType: 'text/plain' }, (error, data) => { console.log("Saved last state as DOWN"); });
callback(null, 'Website is DOWN.');
return("downTrigger");
} else {
return "downNoTrigger";
}
})
.then(function(trigger) {
console.log("called 4");
if (trigger == "upTrigger") {
transporter.sendMail(upMail, (error, info) => {
if (error) {
console.log("ERROR: " + error);
callback(null, "ERROR: " + error);
} else {
console.log('Up message sent.');
callback(null, 'Up message sent');
}
});
} else if (trigger == "downTrigger") {
transporter.sendMail(downMail, (error, info) => {
if (error) {
console.log("ERROR: " + error);
callback(null, "ERROR: " + error);
} else {
console.log('Down message sent.');
callback(null, 'Down message sent');
}
});
}
console.log("Outcome of ping was: ", trigger);
});
};
This doesn't quite work. The result logs are:
called 1
called 2
Last observed status: UP
called 3
called 4
Outcome of ping was: undefined
ReferenceError: resolve is not defined
Converting your typical async function to a promise is pretty straight forward. I'd rather try and demonstrate how to convert it than write the code as you don't learn anything from that.
Usually with node you'll have something that looks similar to this:
doSomethingAsync(callback);
function doSomethingAsync(callback){
var err, result;
// Do some work
...
callback(err, result);
}
function callback(err, result){
if(err){
// Handle error
} else{
// Success so do something with result
}
}
A promise wrapping an async function generally looks something like this:
var myProm = new Promise(function(resolve, reject){
doSomethingAsync(function(err, result){
if(err){
reject(err);
} else{
resolve(result)
}
});
})
.then(function(result){
// Success so do something with result
console.log("Success:", result)
})
.catch(function(err){
// Handle error
console.log("Error: ", err);
})
.then(function(result){
// Where's my result? - result == undefined as we didn't return anything up the chain
console.log("I always execute but result is gone", result)
})
To pass the result down the chain to our "always then" method we need to return a promise or a value:
var myProm = new Promise(function(resolve, reject){
doSomethingAsync(function(err, result){
if(err){
reject(err);
} else{
resolve(result)
}
});
})
.then(function(result){
// Success so do something with result
console.log("Success:", result)
return result;
})
.catch(function(err){
// Handle error
console.log("Error: ", err);
return err;
})
.then(function(result){
// The err/result now gets passed down the chain :)
console.log("Oh there it is", result)
})
I think that using the above patterns should cater to most of the async methods and events in your code example if any particular ones are giving you trouble drop a comment in and I'll try to cover those specific examples.
Here's an attempt at converting it over to promises - I'm pretty tired so apologies about any mess or mistakes - also there's still plenty of cleanup that could be done.
Essentially what I've done is try to break down the code into tasks and wrap each of those tasks in a promise. That way we can resolve/reject and chain them as needed.
'use strict';
var http = require('http');
var nodemailer = require('nodemailer');
var AWS = require('aws-sdk');
var s3 = new AWS.S3( { params: { Bucket: 'my-bucket' } } );
exports.handler = function (event, context, callback) {
var lastStatus;
var options = {
host: event.server.host,
port: event.server.port ? event.server.port : 80,
path: event.server.path ? event.server.path : '',
method: event.server.method ? event.server.method : 'HEAD',
timeout: 5000
};
var transporter = nodemailer.createTransport({
host: event.mail.host,
port: event.mail.port ? event.mail.port : 587,
auth: {
user: event.mail.user,
pass: event.mail.pass
}
});
var d = new Date();
var UTCstring = d.toUTCString();
// email templates
var downMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda DOWN alert: SITE (' + event.server.host + ') is DOWN',
text: 'LambdaAlert DOWN:\r\nSITE (' + event.server.host + ') is DOWN as at ' + UTCstring + '.'
};
var upMail = {
from: event.mail.from,
to: event.mail.to,
subject: 'Lambda UP alert: SITE (' + event.server.host + ') is UP',
text: 'LambdaAlert UP:\r\nSITE (' + event.server.host + ') is UP as at ' + UTCstring + '.'
};
// Run async chain to ensure that S3 calls execute in proper order
function getLastPingStatus(){
return new Promise(function(resolve, reject){
s3.getObject( { Key: 'lastPingStatus' }, function(err, data) {
// get last status from S3
if (err) {
lastStatus = "UP";
reject(lastStatus)
} else {
lastStatus = data.Body.toString();
resolve(lastStatus);
console.log("Last observed status: " + lastStatus);
}
});
})
}
getLastPingStatus()
.then(httpRequest)
.catch(httpRequest); // Otherwise a reject will throw an error
function sendMail(mail, status){ // status = "up" or "down" -
return new Promise(function(resolve, reject){
transporter.sendMail(mail, function(error, info) {
if (error) {
console.log("ERROR: " + error);
reject(null, "ERROR: " + error);
} else {
console.log('No further details available.');
resolve(null, status + ' message sent');
}
});
});
}
function saveStatus(up) {
return new Promise(function (resolve, reject) {
var saveOptions,
message;
// I didn't bother refactoring these as promises at they do the same thing regardless of outcome
if(up){
saveOptions = [{ Key: 'lastPingStatus', Body: 'UP', ContentType: 'text/plain' }, function(error, data) { console.log("Saved last state as UP"); }];
message = 'Website is OK.';
} else{
saveOptions = [{ Key: 'lastPingStatus', Body: 'DOWN', ContentType: 'text/plain' }, function(error, data) { console.log("Saved last state as DOWN"); }];
message = 'Website is DOWN.';
}
s3.putObject.apply(this, saveOptions);
callback(null, message);
});
}
function httpRequest(lastStatus) {
var requestOptions = options;
return new Promise (function (resolve, reject){
var req = http.request(requestOptions, function(res) {
if (res.statusCode == 200) {
if (lastStatus == "DOWN") {
console.log('Email up notice sending...');
sendMail(upMail, "Up")
.then(resolve, reject)
.then(saveStatus(true))
.then(callback)
}
}
});
req.on('error', function(e) {
if (lastStatus == "UP") {
console.log('Email down notice sending...');
sendmail(downMail, "Down")
.then(resolve, reject)
.then(saveStatus(false))
.then(callback)
}
});
req.end();
});
}
};
The AWS-SDK supports native promises, for all services. Some need additional parameters to return properly, like Lambda.invoke().
You would essentially do
s3.putObject({ Key: 'key', Bucket: 'bucket' }).promise()
.then(data => {
// this is the same as the data callback parameter
})
.catch(error => {
// handle your error
})
Or, you could use async/await:
const file = await s3.getObject(params).promise()
// do things with the result
For quick access to the actual file (and not metadata):
const file = JSON.parse(await s3.getObject(params).promise().then(res => res.Body));
https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-promises.html
In order to "promisify" callback function, imho, the easiest and cleaner way is to use bluebird. You just don't want to write glue code in order to simplify your code, it's counter productive (and it's error prone).
From the doc :
var Promise = require("bluebird");
var readFile = Promise.promisify(require("fs").readFile);
readFile("myfile.js", "utf8").then(function(contents) {
return eval(contents);
}).then(function(result) {
console.log("The result of evaluating myfile.js", result);
}).catch(SyntaxError, function(e) {
console.log("File had syntax error", e);
//Catch any other error
}).catch(function(e) {
console.log("Error reading file", e);
});
After reading slaughtr's answer I decided to do it like this, for doing some data saving when pressing the AWS IoT button:
var AWS = require("aws-sdk");
var iot = new AWS.Iot();
exports.handler = (event, context, callback) => {
iot.listThings({
attributeName: 'dsn',
attributeValue: event.serialNumber,
maxResults: 1
})
.promise()
.then(response => {
return iot.listThingGroupsForThing({thingName: response.things[0].thingName}).promise();
})
.then(groupsList => insertRecordIntoDDB(date, serialNumber, groupsList.thingGroups[0].groupName))
.catch(err => console.log(err))
};
and shortly after I decided to compress it even further with async/await
exports.handler = async (event, context, callback) => {
var eventText = JSON.stringify(event, null, 2);
var thingsList = await iot.listThings({
attributeName: 'dsn',
attributeValue: event.serialNumber,
maxResults: 1
}).promise()
var groupsList = await iot.listThingGroupsForThing({
'thingName': thingsList.things[0].thingName
}).promise();
insertRecordIntoDDB(date, serialNumber, groupsList.thingGroups[0].groupName)
};
I'm still fairly new at this async programming stuff so I'm not sure what I should like the most. Promise chaining can get a bit spaghetti-like, while async await only helps mask all that into something that's easier to comprehend
Using node http promisified to call external api in aws lambda.
exports.handler = async (event) => {
return httprequest().then((data) => {
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
return response;
});
};
function httprequest() {
return new Promise((resolve, reject) => {
const options = {
host: 'jsonplaceholder.typicode.com',
path: '/todos',
port: 443,
method: 'GET'
};
const req = http.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.end();
});
}

Resources