API Gateway websockets - Function is sending two messages - node.js

I'm using the serverless apigateway websockets and I can successfully get messages sent back and forth between the lambda function and client.
However I can't figure out how to get my function to send only a single message. It's currently sending two messages due to the callback at the end of the function. This is more of a nodejs issue, but I've been trying for the past couple of hours to figure out how, but can't seem to.
var params2 = {
TableName: "UserConnections",
FilterExpression: "cameraId = :val",
ExpressionAttributeValues: { ":val": {"S" : JSON.parse(event.body).data.camera_id}}
};
DDB.scan(params2, function(err, data) {
if (err) throw err;
console.log("DATA: " + JSON.stringify(data));
for(var i = 0; i<data['Items'].length; i++){
var id = data['Items'][i]['connectionId'].S;
console.log("List of connection ids: " + id);
var params3 = {
ConnectionId: id,
Data: JSON.stringify(message)
};
apigatewaymanagementapi.postToConnection(params3, function(err, data) {
if (err){
throw err; // an error occurred
}else{
console.log("Success sending message to clients: " + JSON.stringify(data));
}
});
}
});
callback(null, {
statusCode: 200,
body: "Message Processed in Lambda!"
});
In postToConnection method, it sends a message back to multiple users, and the callback function sends the body to the same users. How can I just send the params3 back to the users and not use the callback to end the function
Edit1:______________________________________________
Adding
callback(null, {});
Still sends two messages except the second one is now empty. How can I get it to strictly send only one message

Return with a empty object, the return value is ignored when this function is invoked from WebSocket gateway
return {}; // callback(null, {});

Related

Cannot set headers after they are sent to the client NodeJS Redirect

I have some IF/Else stataments, in IF statament I could make an redirect, but in else statament I couldn't.
Code that I use:
app.use(express.static(__dirname + '/public'));
router.get('/',function(req,res) {
res.sendFile(path.join(__dirname + '/public/html/index.html'));
var email = req.query.email;
conn.query("SELECT * FROM users WHERE email = ?", [email], function(err, result){
if (err) throw err;
var resultLength = result.length;
if (email != null && resultLength == 0) {
conn.query('INSERT INTO users (email) VALUES (?)', [email], function(err, result) {
if (err) throw err;
console.log('1 record inserted')
res.write('<p>You are now subscribed to our newsletter!</p>');
})
res.redirect('/succes')
}else{
console.log("You are already subscribed to our newsletter!")
res.redirect('/error')
}
})
});
You can send ONE and only one response to a given request. When you do:
res.sendFile(...)
that sends a response to the incoming request. Attempting to send ANY other request will cause the error about "cannot send headers after they are already sent".
Yet, in your code, you then try to do both res.write(...) and res.redirect(...). You simply can't do that. You get to send one and only one response to a given incoming request. You already sent one with res.sendFile(...).
Then, even further, you can't do a res.write(...), then followed by res.redirect(...). Doing a res.write(...) means that the http library has to send the headers so that it can now start sending the body. So, this forces the headers to get sent. But, now when you do a res.redirect(...), you're telling the Express library that you want to set a 302 status code and you want to set the Location header. Hmmm, but the status code has already been sent and the headers have already been sent, forced out by the res.write(...). So, at this point, res.redirect(...) cannot do its job.
It's unclear what this code is supposed to do. You're either serving index.html or you're doing something else that redirects to a different URL. The same route can't do both of those operations. For a route like this, you either send an HTML response OR you redirect to another URL. Pick one.
Here's one possible solution that contains only res.redirect():
router.get('/', function(req, res) {
const email = req.query.email;
if (!email) {
console.log("req.query.email not present");
res.redirect('/error')
return;
}
conn.query("SELECT * FROM users WHERE email = ?", [email], function(err, result) {
if (err) {
console.log(err);
res.redirect('/error')
return;
}
const resultLength = result.length;
if (resultLength == 0) {
conn.query('INSERT INTO users (email) VALUES (?)', [email], function(err, result) {
if (err) {
console.log(err);
res.redirect('/error')
return;
}
console.log('1 record inserted')
res.redirect('/succes');
return;
})
} else {
console.log("You are already subscribed to our newsletter!")
res.redirect('/error')
return;
}
});
});

Can you publish multiple messages to an SNS topic using an AWS Lambda function backed by node.js 8.10?

There is a related question regarding how to publish s single message: Can you publish a message to an SNS topic using an AWS Lambda function backed by node.js?
However, my question is related to publish more than one message.
I am using node 8.10 and my handler is asynchronous.
You can use the Promise.all() feature to encapsulate multiple calls to sns.publish.
Create a one-notification-publish function that returns Promise:
.
function onePublishPromise(notificationParams){
return new Promise((resolve, reject) => {
sns.publish(notificationParams, function(err, data) {
if (err) {
console.error("Unable to send notification message. Error JSON:", JSON.stringify(err, null, 2));
reject(err);
} else {
console.log("Results from sending notification message: ", JSON.stringify(data, null, 2));
resolve(null);
}
});
});
}
Create and send notifications in parallel:
// Create notifications params
const notifications = [
{
Subject: 'A new notification',
Message: 'Some message',
TopicArn: 'arn:aws:sns:us-west-1:000000000:SomeTopic'
}
// , ...
];
// Send all notifications
const notificationsDelivery = notifications.map(onePublishPromise);
// Wait for delivery results
Promise.all(notificationsDelivery).then(callback).catch(callback);
callback will be called after all messages published (successfully or not)
The related question uses context.done, which would end the lambda before making a second call. Using
sns.publish(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
you can make more calls, e.g.
sns.publish(params2, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Whether you want to use async.waterfall, nest the calls or let them go asynchronously is up to you.

Nodejs Firebase reference to database keeps on checking value

I tried creating one program which fetch data from firebase database from node js server.
function FCM()
{
var payload = {
data: {
//Some data
}
};
var db = admin.database();
var ref = db.ref("PATH_TO_DATABASE");
ref.on("value", function(snapshot) {
registrationToken = REGISTRATION TOKEN
admin.messaging().sendToDevice(registrationToken, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
But when this function gets completed then the server keeps on running as usual which is fine but the problem is that when any data gets deleted from PATH_TO_DATABASE then it gives error:
TypeError: Cannot convert undefined or null to object
Now I don't want firebase to keep on checking the value to that path. I just want to free that thing. So how I can do it ?
ref.on("value") is going to trigger whenever that value changes, like when the data is deleted. Instead use
ref.once("value", function(snapshot)
Then admin.messaging().sendToDevice(registrationToken, payload) won't get triggered again.

nodeJS blocking all requests until it calls back

I've developed a nodeJS API (using express) which allow users to login and get a list of files that they have stored in a remote server. And as you understand, the code must be non-blocking so the webserver can still responds to logging in requests, even if there are some users fetching theirs files lists.
Every time a user make a request to get his files list, the listOfFiles function is called.
This is the code:
exports.listOfFiles = function(req,res){
db.Account.find({where: {id:1}}).then(function(newAcc){
console.log("encontrou a account");
getFiles('/', newAcc.accessToken, '0', newAcc, function(error){
if (error) {
log.error('Error getting files');
}else{
console.log("callback!")
}
});
});
}
getFiles function: this function is responsible for fetching the file list from the remote server, and store them in a postgres database
function getFiles(path, accessToken, parentID, newAcc, callback){
var client = new ExternalAPI.Client({
key: config.get("default:clientId"),
secret: config.get("default:clientSecret")
});
client._oauth._token = accessToken;
var options = {
removed : false,
deleted : false,
readDir: true
}
//this is the instruction that fetch an array of items
//(metadata only) from a remote server
client.stat(path, options, function(error, entries) {
if (error) {
if (error.status == 429) {
console.log(accessToken + 'timeout')
setTimeout(
getFiles(path, accessToken, parentID, callback),
60000);
}else{
log.error(error);
callback(error,null);
}
}
else {
//When the array os items arrives:
console.log("RECEIVED FILES")
var inserted = 0;
var items = entries._json.contents;
for(var file in items){
var skyItemID = uuid.v1();
var name = items[file].path.split('/').pop();
var itemType;
if (items[file].is_dir) {
itemType = 'folder';
}else{
itemType = 'file';
}
newAcc.createItem({
name : name,
lastModified: items[file].modified,
skyItemID: skyItemID,
parentID: parentID,
itemSize: items[file].bytes,
itemType : itemType,
readOnly: items[file].read_only,
mimeType: items[file].mime_type
}).then(function(item){
console.log(item.name)
if (++inserted == items.length) {
console.log(inserted)
console.log(items.length)
console.log("callsback")
callback();
}
}).catch(function(error){
log.error('[DROPBOX] - Filename with special characters');
callback(new Error);
return;
});
}
}
});
}
The problem here is, the moment that webserver prints console.log("RECEIVED FILES") in our console, it stops responding to all other requests, such as log in or fetch files requests from other users.
And it starts responding again when it prints console.log("callback!"). So, i'm assuming that somehow nodeJS is blocking itself until getFiles function is finished and called back.
I think that this is not a normal behaviour. Shouldn't nodeJS be responding to responds to other requests even if there are some operations running in background? Shouldn't getFiles function being run in background and not affecting/blocking all other requests? What am I doing wrong?
Thanks!
I am facing the same kind of problem for long time server http request blocks the service for response other client requests. This is my topic. What is the correct behavior for Node.js while Node.js is requesting a long time http request for other servers Currently, I got no answer for that. If you got the answer, please reply me. Thanks.

need to send error to unauthorized user in socket.io

as the title is obvoius i need to send back some error message for unauthorized user and i need to know how to achive this for example i need to send this message to user
you dont have any username to begin chat
and print it in users browser how should i do that? the client side code is something like this
//this is the client side code
var socket = io.connect('http://localhost', { resource: '/chat/app.js' });
// on connection to server, ask for user's name with an anonymous callback
socket.on('connect', function(){
// call the server-side function 'adduser' and send one parameter (value of prompt)
socket.emit('adduser')
});
socket.socket.on('error', function (reason){
console.log('Unable to connect Socket.IO', reason);
});
but the reason which i get in console is
Unable to connect Socket.IO handshake error
how should i print the message which is the cause of user is nothing get authorized?
this is the server side code
var io = require('socket.io').listen(80);
io.configure(function (){
io.set('authorization', function (handshakeData, callback) {
// findDatabyip is an async example function
findDatabyIP(handshakeData.address.address, function (err, data) {
if (err) return callback(err);
if (data.authorized) {
handshakeData.foo = 'bar';
for(var prop in data) handshakeData[prop] = data[prop];
callback(null, true);
} else {
//THIS IS THE MESSAGE *********************************************
callback('you dont have any username to begin chat', false);
}
})
});
});
To send the error back to the user you must modify the error function on manager.js (socket.io\lib\manager.js; line 768 approx) from this
function error (err) {
writeErr(500, 'handshake error');
self.log.warn('handshake error ' + err);
};
to this
function error (err) {
writeErr(500, /*'handshake error'*/ err);
self.log.warn('handshake error ' + err);
};

Resources