Converting AWS Lambda function to use promises? - node.js

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();
});
}

Related

OCPP Soap Client Issue

I wrote a SOAP Server and Client as this:
index.js
var soap = require('soap');
var express = require('express');
var bodyParser = require('body-parser');
var service = {
CentralSystemService: {
CentralSystemServiceSoap12: {
Authorize: (args) => { return args; },
/*function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.Authorize.handle(args).then(function(data) {
callback(data);
});
},*/
BootNotification: function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
console.log('[SOAPWrapper] BootNotification endpoint: ' + args.endpoint);
handlers.BootNotification.handle(args).then(function(data) {
console.log('[SOAPWrapper] BootNotification result: ' + JSON.stringify(data));
callback(null, data);
});
},
StartTransaction: function(args, callback, headers, req) {
console.log('Headers: ' + JSON.stringify(headers));
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.StartTransaction.handle(args).then(function(data) {
callback(data);
});
},
StopTransaction: function(args, callback, headers, req) {
// TODO: store the correct port [Issue #29]
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getRemoteAddress(req.connection.remoteAddress);
handlers.StopTransaction.handle(args).then(function(data) {
callback(data);
});
},
Heartbeat: function(args, callback, headers, req) {
handlers.Heartbeat.handle().then(function(data) {
callback(data);
});
},
MeterValues: function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.MeterValues.handle(args).then(function(data) {
callback(data);
});
},
StatusNotification: function(args, callback, headers, req) {
console.log(`Recieved StatusNotification ${JSON.stringify(args)}`);
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.StatusNotification.handle(args).then(function(data) {
data = data || {}
console.log('[SOAPWrapper] StatusNotification result: ' + JSON.stringify(data));
callback(data);
});
},
FirmwareStatusNotification: function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.FirmwareStatusNotification.handle(args).then(function(data) {
callback(data);
});
},
DiagnosticsStatusNotification: function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.DiagnosticsStatusNotification.handle(args).then(function(data) {
callback(data);
});
},
DataTransfer: function(args, callback, headers, req) {
args.chargeBoxIdentity = headers.chargeBoxIdentity;
args.endpoint = Utils.getEndpoint(headers.From.Address, req.connection.remoteAddress);
handlers.DataTransfer.handle(args).then(function(data) {
callback(data);
});
}
}
}
};
var url = require('fs').readFileSync("centralsystemservice_1.5.wsdl", "utf8");
var path = '/client1';
var port = 9330;
var app = express();
app.use(bodyParser.text({type:'text/*'}));
let options = {
ignoredNamespaces: {
namespaces: [],
override: true
}}
soap.createClient("chargingpointservice_1.5.wsdl",options, (err,soapClient) => {
if(err)
console.log(err);
var headerStr = '<h:chargeBoxIdentity xmlns:h="urn://Ocpp/Cp/2012/06/" >'+ 'clientId' + '</h:chargeBoxIdentity>'+
'<a:MessageID>urn:uuid:' + 'uuid4' + '</a:MessageID>' +
'<a:From><a:Address>http://localhost:9330</a:Address></a:From>'+
'<a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>'+
'<a:To>'+ 'http://localhost:9330/client1' + '</a:To>'+
'<a:Action soap:mustUnderstand="1">'+ '/Reset' +'</a:Action>';
var cleaned = headerStr.replace("\ufeff", "");
soapClient.setEndpoint('http://localhost:9330/client1');
//soapClient.clearSoapHeaders();
soapClient.addSoapHeader(cleaned);
var request = {
resetRequest: "data"
};
soapClient.Reset(request, (error, result) => {
if(error)
console.log(error);
else
console.log(result);
})
})
app.listen(port, (req, res, next) => {
console.log(`App is listening on port ${port}`);
soap.listen(app, path, service, url);
});
I want to perform some actions on client. Bu when i run relevant piece of code i get this error:
Fault: {
faultcode: 500,
faultstring: 'Invalid XML',
detail: 'Error: Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: T',
statusCode: 500
},
...
body: "TypeError: Cannot read properties of undefined (reading 'description')\n" +
' at SAXParser.p.onopentag (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\wsdl\\index.js:238:37)\n' +
' at emitNode (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\sax\\lib\\sax.js:629:5)\n' +
' at openTag (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\sax\\lib\\sax.js:825:5)\n' +
' at SAXParser.write (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\sax\\lib\\sax.js:1391:13)\n' +
' at WSDL.xmlToObject (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\wsdl\\index.js:443:11)\n' +
' at Server._process (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\server.js:253:29)\n' +
' at Server._processRequestXml (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\server.js:166:18)\n' +
' at Server._requestListener (C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\server.js:219:29)\n' +
' at C:\\Users\\Public\\Projects\\toDrive\\node_modules\\soap\\lib\\server.js:80:29'
I am aware of that, client has no service object. Is the cause of that error absence of service object? Can i perform actions only for server? By the way, i am fairly new to SOAP. I couldn't find the source of error.

AWS NodeJS Lambda email function not working correctly

I have a AWS Lambda that first gets data from an external API endpoint and then will loop the json data and will send email to each of the records. Here is the code:
var aws = require("aws-sdk");
var ses = new aws.SES({ region: "us-east-1" });
const https = require('https');
exports.handler = async function(event, context) {
return httprequest().then((data) => {
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
data.forEach(function(item) {
// send email
sendEmailAsync(item);
});
return response;
});
};
function sendEmailAsync(item) {
return new Promise((resolve, reject) => {
console.log(`Sending email to: ${item.fullName} `);
let emailFrom = process.env.emailFrom;
const htmlBody = `
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Hi ${item.fullName},</p>
<p>...</p>
</body>
</html>
`;
const textBody = `
Hi ${item.fullName},
...
`;
var params = {
Destination: {
ToAddresses: [item.email],
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data: htmlBody
},
Text: {
Charset: "UTF-8",
Data: textBody
}
},
Subject: {
Charset: "UTF-8",
Data: "Test Email"
}
},
Source: emailFrom,
};
const sendPromise = ses
.sendEmail(params)
.promise();
sendPromise.then(data => {
console.log(data.MessageId);
context.done(null, "Success");
resolve(data.MessageId);
})
.catch(err => {
console.error(err, err.stack);
context.done(null, "Failed");
reject(err.message);
});
});
}
function httprequest() {
let host = process.env.endPointHost;
let path = process.env.endPointPath;
let apiKey = process.env.apiKey;
return new Promise((resolve, reject) => {
const options = {
host: host,
path: path + '?APIKey=' + apiKey,
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
const req = https.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();
});
}
Everything works fine except that when the sendEmailAsync(item); function is being called I get the console log but never get any result about the email being sent or not. Of course, I don't get any email but I'm pretty sure that the problem is that I have to await maybe the function inside the foreach statement but have tried some code and didn't work.
Any clue?
As the forEach won't give the expected result with the await function, we shall use Promise.all or for..of way of looping.
Let me give you the example by taking only the loop from your snippet.
Promise.all
await Promise.all(data.map(async (item) => {
try {
let res = await sendEmailAsync(item);
console.log(res)
} catch (error) {
console.log('Error: '+ error);
}
}))
for..of
for(let item of data) {
try {
let res = await sendEmailAsync(item);
console.log(res);
} catch (error) {
console.log('Error: ' + error);
}
}

How to handle large file processing through AJAX and express

I'm uploading a PDF file using AJAX and express and sending the data to external REST API for some file processing.
Whenever I upload more than 3MB file I will get an error in AJAX before the REST API response.
I get correct results from the external REST API, but processing takes more time so AJAX is not receiving the success msg.
How do I handle this?
Tried with AXIOS and other promise handler but same result
module.exports = (req, res) =>
{
var pdf = [],
form = new formidable.IncomingForm(),
pdfFile,
dirname = process.cwd();
form.multiples = false;
// Upload directory for the images
form.uploadDir = path.join(dirname, 'tmp_uploads');
form.on('file', function(name, file) {
let token;
console.log('FORM ON')
if (req.cookies.userData.token) {
token = req.cookies.userData.token;
let buffer = null,
type = null,
filename = '',
renameFile = '';
// Read a chunk of the file.
buffer = readChunk.sync(file.path, 0, 262);
// filename = Date.now() + '-' + file.name;
filename = file.name;
renameFile = path.join(dirname, 'uploads/' + filename);
fs.rename(file.path, renameFile, function(err) {
if (err) throw err;
console.log('renamed complete');
pdfFile = path.join(dirname, 'uploads/' + filename);
let readFileStream = fs.createReadStream(pdfFile),
data = '',
formData = {};
formData = {
name: filename,
my_file: readFileStream,
Width: 1024,
Height: 768
};
function postData() {
// Setting URL and headers for request
var options = {
url: EXTERNALURL,
headers: {
"Authorization": 'bearer ' + token,
"content-type": "multipart/form-data"
},
formData: formData
};
// Return new promise
return new Promise(function(resolve, reject) {
// Do async job
request.post(options, function(err, resp, body) {
if (err) {
reject(err);
} else {
resolve(body);
}
})
})
}
function getData(url) {
// Return new promise
return new Promise(function(resolve, reject) {
// Do async job
request.get({ url: url, encoding: null }, function(err, resp, body) {
if (err) {
reject(err);
} else {
resolve(body);
}
})
})
}
var errHandler = function(err) {
console.log(err);
}
var filePath;
function main() {
var dataPromise = postData();
// Get user details after that get followers from URL
dataPromise.then(JSON.parse, errHandler)
.then(function(result) {
fData = result;
var fileName = fData.Id,
file_url = fData.PresentationZipUrl;
filePath = path.join(dirname, fData.HtmlPath);
// Do one more async operation here
var downloadPromise = getData(file_url).then();
return downloadPromise;
}, errHandler)
.then(function(data) {
//console.log(data);
var zip = new AdmZip(data);
if (!fs.existsSync(filePath)) {
fs.mkdirSync(filePath);
zip.extractAllTo(filePath, true);
}
}, errHandler)
.then(function(data) {
console.log('Done');
res.status(200).json({ 'status': "success" });
}, errHandler);
}
// console.log('before');
main();
}); // END RENAME FILE
} else { //
console.log('ERROR')
res.redirect('/');
}
});
form.on('error', function(err) {
console.log('Error occurred during processing - ' + err);
});
// Invoked when all the fields have been processed.
form.on('end', function() {
console.log('All the request fields have been processed.');
});
// Parse the incoming form fields.
form.parse(req, function(err, fields, files) {})}
AJAX:
$.ajax({
enter code here
url: '/presentation/uploadFiles',
method: 'post',
data: formData,
processData: false,
contentType: false,
timeout: 0}).done(function(d) {
console.log(d);
if (d.status == 'success') {
location.reload()
}}).fail(function(e, t) {
console.log(e);
console.log(t)})
.always(function() {
});

AWS lambda http post request failing

const http = require('http');
exports.handler = function (event, context, callback) {
console.log(event);
var data = {
'name': 'test'
};
var url = "http://**************.com";
function sendFileToUrl(url, data, context, callback) {
console.log('Sending File to the URL:' + url);
if (url && data && context) {
if (context) {
setInterval(function () { }, 1000);
context.callbackWaitsForEmptyEventLoop = false;
}
return http.post(url, JSON.stringify(data)).then(function (res) {
console.log("Data Sent & response: " + res.statusCode);
callback(null, 'success msg');
}).on('error', function (e) {
console.log("Could not send the data & error: " + e.message);
callback(new Error('failure'));
});
}
}
return sendFileToUrl(url, data, context, callback);
};
I am trying to make a http post request from lambda. Sending the data to the specified URL. But it is being asynchronous and gives a response message null
It is not printing the message "Data sent".
How to get this done? Thanks in advance
Try to change your code like this:
const http = require('http');
exports.handler = function (event, context, callback) {
console.log(event);
var data = JSON.stringify({
'name': 'test'
});
const options = {
hostname: 'example.com',
path: '/path-to-resource',
port: 80,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
function sendFileToUrl(options, data, callback) {
console.log('Sending File to the URL:' + url);
if (url && data) {
const req = http.request(url, JSON.stringify(data)).then(function (res) {
console.log("Data Sent & response: " + res.statusCode);
callback(null, 'success msg');
});
req.on('error', function (e) {
console.log("Could not send the data & error: " + e.message);
callback(new Error('failure'));
});
req.write(data);
req.end();
}
}
sendFileToUrl(options, data, callback);
};

Node.js: Why not saved session?

Here's the code:
app.get('/vklogin', function(request, response) {
console.log('Авторизация через соц.сеть "Вконтакте"'.green);
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
var data = querystring.stringify({
client_id: '4836170',
client_secret: 'cPkR53zhon0lU7TAiz9f',
code: query.code,
redirect_uri: 'http://' + request.headers.host + '/vklogin'
});
var options = {
host: 'oauth.vk.com',
port: 443,
path: '/access_token?' + data,
method: 'GET'
};
var httpsreq = https.request(options, function(response) {
response.setEncoding('utf8');
response.on('data', function(chunk) {
var chunk = JSON.parse(chunk);
pg.connect(dbconfig, function(err, client, done) {
if (err) {
return console.error('Ошибка подключения к БД',err);
}
client.query('select * from users where vk = $1', [chunk.user_id], function(err, result) {
done();
if (err) {
console.error('Ошибка получения данных',err);
} else {
if (result.rows[0]) {
console.log(result.rows[0]);
request.session.authorized = true;
request.session.userid = result.rows[0].id;
} else {
console.log('Попытка создания нового пользователя. ');
client.query("insert into users (email, vk) values ('" + chunk.email + "', " + chunk.user_id + ") returning id", function(err, result) {
done();
if (err) {
console.error('Ошибка записи данных в БД', err);
} else {
request.session.authorized = true;
request.session.userid = result.rows[0].id;
console.log('Добавлен новый пользователь # ' + result.rows[0].id);
}
});
}
}
client.end();
});
console.log("№ пользователья: " + request.session.userid);
});
});
});
httpsreq.end();
if (request.session.authorized) {
response.writeHead(301, {
Location: 'http://' + request.headers.host + '/cabinet'
});
} else {
response.writeHead(301, {
Location: 'http://' + request.headers.host
});
}
response.end();
});
That is why outside functions session is not saved? What is wrong in my code?
Inside the function, everything is fine, outside functions - undefined.
After this session, the logic must be maintained and be available everywhere, too, everywhere, or is not it?
Tried to declare a variable with the session, but it also did not work, and no error does not give, do not even know where to dig.
var sess;
app.get('/vklogin', function(request, response) {
sess = request.session;
// other code...
});
UPD:
My problem is related to the lack of understanding of the control of asynchronous processes. I can not understand how to perform the originally one - database queries, information preservation in the session, and then check the session variables and forwarding to the desired page.
If you know how to make the correct execution order for me, write the answer.
Ok, I find need async pattern. Look here: http://book.mixu.net/node/ch7.html

Resources