Execute push notification after sending message - node.js

I am using sails.socket for message sending. My requirement is that, I have to send a push notification after the success of sent message. How it is possible. Please see the code that I have written.
sendChatMessage(chatMessage, function() {
// calling push notification when a chat message send
var serverKey = req.options.settingsKeyValue.PUSH_SERVER_KEY;
var typeData = { type:1, data:{type:1} };
var pushData = { title:'New Message', body: data };
pusherService.pushFcm(serverKey,typeData,pushData,toId, function(err, result) {
if(err) {
return res.json(200, {status:1, status_type: 'Success', message: 'Error in sending the message'});
}else{
return res.json(200, {status:1, status_type: 'Success', message: 'You have successfully send the message'});
}
});
});
function sendChatMessage(){
var socketRoom = "userRoom_"+toId;
var roomsSubcribers = sails.sockets.subscribers(socketRoom);
console.log("roomsSubcribers");
console.log(roomsSubcribers);
var data = {
text: message,
from_id: fromId,
from_name: userResult[0].firstName+' '+userResult[0].lastName, from_img : userResult[0].profilePhoto,
};
sails.sockets.broadcast(socketRoom,{
type : "chat",
message : data,
});
callback(chatMessage);
}

These blocks of code look ok to me... you just have to position them in the right place within your application. Have you tried this?
You definitely need to name some of your arguments in both the sendChatMessage function definition, and in your callback function.
Maybe you need something like this:
// in some controller
// notice the named arguments 'options' and 'callback'
var sendChatMessage = function(options, callback){
var socketRoom = "userRoom_"+options.toId;
var roomsSubcribers = sails.sockets.subscribers(socketRoom);
console.log("roomsSubcribers");
console.log(roomsSubcribers);
var data = {
text: options.message,
from_id: options.fromId,
from_name: options.fromName,
from_img : options.fromImg,
};
sails.sockets.broadcast(socketRoom,{
type : "chat",
message : data,
});
callback(data);
};
module.exports = {
someMethod: function(req, res) {
// do some work, including defining / getting all options
var options = {
toId: 123,
fromId: 456,
message: 'test message',
fromName: 'test user',
fromImg: 'some/image.jpg' // don't know if you need a source here or what
};
// invoke your function - notice the named argument in the callback
sendChatMessage(options, function(data) {
// calling push notification when a chat message send
var serverKey = req.options.settingsKeyValue.PUSH_SERVER_KEY;
var typeData = { type:1, data:{type:1} };
var pushData = { title:'New Message', body: data };
pusherService.pushFcm(serverKey,typeData,pushData,toId, function(err, result) {
if(err) {
return res.json(200, {status:1, status_type: 'Success', message: 'Error in sending the message'});
}else{
return res.json(200, {status:1, status_type: 'Success', message: 'You have successfully send the message'});
}
});
});
},
};
I can't know if this is the right way to use all your plugins, etc, but putting the pieces together seems to make sense.
Last note, when you define your own callbacks, it's good practice to make the first argument an error object, and check for received errors in the callback body. When successful you can return a null error, like callback(null, data);.
Hope this is helpful.

Related

Accessing Firestore via Cloud Function

So i have 2 Cloud Functions within the same file:
exports.Auth = functions.region('europe-west1').https.onRequest((req, res) =>
and
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) =>
When adding the following code right at the start of my Auth function it adds a new document to the Firestore as expected, however, when i add the same code at the start of my IPN function, which is currently being called via Paypal's IPN Simulator, it does nothing, no errors.
let pin = RandomPIN(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var userRef = db.collection('Users').doc(pin);
var setWithOptions = userRef.set({ Activated: false }, { merge: true });
console.log("PIN: "+pin);
What on earth is going on, i must be missing something?
Thanks in advance.
Update:
Here are the logs, first with the 2 middle lines commented and then uncommented It seems to be silently failing, i'm just not sure what is causing it.
Update with Complete function:
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) =>
{
console.log("IPN Notification Event Received");
let pin = RandomPIN(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var userRef = db.collection('Users').doc(pin);
var setWithOptions = userRef.set({ Activated: false }, { merge: true });
console.log("PIN: "+pin);
if (req.method !== "POST")
{
console.error("Request method not allowed.");
res.status(405).send("Method Not Allowed");
}
else
{
console.log("IPN Notification Event received successfully.");
res.status(200).end();
}
let ipnTransactionMessage = req.body;
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = querystring.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`Verifying IPN: ${verificationBody}`);
let options = {
method: "POST",
uri: getPaypalURI(),
body: verificationBody,
};
// POST verification IPN data to paypal to validate.
request(options, function callback(error, response, body)
{
if(!error && response.statusCode === 200)
{
if(body === "VERIFIED")
{
console.log(`Verified IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is verified.`);
SendPIN(ipnTransactionMessage.payer_email, pin);
}
else if(body === "INVALID")
console.error(`Invalid IPN: IPN message for Transaction ID: ${ipnTransactionMessage.txn_id} is invalid.`);
else
console.error("Unexpected reponse body.");
}
else
{
console.error(error);
console.log(body);
}
});
});
Indeed it is a problem of Promises chaining and also a problem due to the request library: request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
I would suggest that you watch these official Firebase videos from Doug : https://www.youtube.com/watch?v=7IkUgCLr5oA&t=28s and https://www.youtube.com/watch?v=652XeeKNHSk which explain this key concept.
You can use request-promise (https://github.com/request/request-promise) and the rp() method which "returns a regular Promises/A+ compliant promise".
It is not clear what SendPIN() is doing. Let's make the assumption it returns a Promise. If this is true, you could adapt your code along the following lines:
//....
const rp = require('request-promise');
//....
exports.IPN = functions.region('europe-west1').https.onRequest((req, res) => {
console.log('IPN Notification Event Received');
let pin = RandomPIN(
10,
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
);
var userRef = db.collection('Users').doc(pin);
if (req.method !== 'POST') {
console.error('Request method not allowed.');
res.status(405).send('Method Not Allowed');
} else {
let ipnTransactionMessage;
userRef
.set({ Activated: false }, { merge: true })
.then(() => {
console.log('PIN: ' + pin);
ipnTransactionMessage = req.body;
// Convert JSON ipn data to a query string since Google Cloud Function does not expose raw request data.
let formUrlEncodedBody = querystring.stringify(ipnTransactionMessage);
// Build the body of the verification post message by prefixing 'cmd=_notify-validate'.
let verificationBody = `cmd=_notify-validate&${formUrlEncodedBody}`;
console.log(`Verifying IPN: ${verificationBody}`);
let options = {
method: 'POST',
uri: getPaypalURI(),
body: verificationBody
};
// POST verification IPN data to paypal to validate.
return rp(options);
})
.then(response => {
//Not sure what you will get within the response object...
console.log(
`Verified IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is verified.`
);
return SendPIN(ipnTransactionMessage.payer_email, pin); //It is not clear what SendPIN is doing, let's make the assumption it returns a Promise...
})
.then(() => {
res.send('Success');
return null;
})
.catch(err => {
console.error(
`Invalid IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is invalid.`
);
res
.status(500)
.send(
'Error: ' +
err +
` - Invalid IPN: IPN message for Transaction ID: ${
ipnTransactionMessage.txn_id
} is invalid.`
);
return null;
});
}
});

Keystonejs get insterted id value

I am using updateItem method to save new record.
Following is the code example :
var vv = new vvmodel.model();
vvmodel.updateItem(vv, obj, function (error) {
if(error)
console.log('error :: ', error);
});
This is not sending any object from which we can have inserted id record. How can we get the inserted record id ?
Thanks
you should use update handler in keystoneJs
once updateHandler.process runs without error, you can access .id field on vv
var vv = new vvmodel.model({
// .... initial fields
}),
var updater = vv.getUpdateHandler(req, res, {
errorMessage: 'There was an error creating your new model:'
});
updater.process(req.body, {
flashErrors: true,
logErrors: true,
fields: 'field1, field2, field3'
}, function(err) {
if (err) {
locals.validationErrors = err.errors;
} else {
req.flash('success', 'Your model has been added');
return res.redirect('/vv/detail/' + vv.id); // here you can access the id if request was successful.
}
next();
});
see more detailed example use with sydjs site source

Why can't Restful pass body into database

I'm creating a RESTful API.
I wanna use GET method to check if lastName exists. If it can find lastName, return "YES", otherwise, call a POST method to create a data with lastName entered.
The problem is that it can create a new data, but the body is empty. Ideally, it should contain a value with lastName, like "lastName": "James",
{
"_id": "58a22c3c3f07b1fc455333a5",
"__v": 0
}
Here is my code.
router.route("/findLastName/:id")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.id}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
res.json(response);
}
if (result) {
response = "YES";
res.send(response);
} else {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
var POSTurl = "http://localhost:6002/users";
var params = "lastName=" + req.params.id;
xhr.open("POST", POSTurl, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(params);
}
});
})
PS: GET method works well, not a issue.
Let me modify a bit of your code and add comments as pointers:
// changed findLastName to find-last-name. It's a common convention,
// urls need to be case insensitive. It doesn't concern lastName, as
// that's a parameter, internal to your app so it's fine.
// even better if you name the route `find-or-create` or something, to better
// reflect what you're doing.
router.route("/find-last-name/:lastName")
.get(function(req,res){
var response;
mongoOp.findOne({deviceID: req.params.lastName}, function(err, result){
if (err) {
response = {"error" : true,"message" : "Error fetching data"};
// Adding a `return statement here. If you don't return, you'll tell
// the user that there was an error, but your code continues running
// potentially calling that res.json twice.
// Also, since it's an internal error, it's common to tell the client
// about it, by setting the status to 500
return res.status(500).json(response);
}
if (result) {
// turning the message to JSON instead. You started that above,
// and for the sake of your clients (your frontend), it's
// better to stick to JSON. Also you can pass useful info, such as
// _id of the document.
// Again adding a `return` here, and then the rest of the code
// is nested one level less. not required, but some people like that.
response = {
message: "Last name exists."
};
return res.json(response);
}
// Here begins the new code. I'm typing what I can infer from your code,
// I don't know if your MongoDB driver looks like that exactly.
mongoOp.insert({
deviceId: req.params.lastName
// add other optional properties here.
}, function (err, response) {
if (err) {
var message = {
error: true,
message: 'Cannot save new entry.'
}
return res.status(500).json(message);
}
// if we're here, everything went ok. You can probably return
// the _id of the given user.
return res.json({
message: 'New user created.',
_id: response._id
});
});
});
})

Obtain data from a GET in superagent function

I'm trying to do something like this:
var countElemnts = function() {
superagent
.get('/someOtherUrl')
.query({
type: value.type
})
.end(function(err, res) {
console.log(JSON.stringify(res.body));
if (res.ok) {
reply(JSON.stringify(res.body));
} else {
console.log(err);
}
})
};
superagent
.post('/someUrl')
.type('json')
.send({
name: 'value.update',
data: {
id: request.params.id,
type: value.type,
count: countElemnts()
}
})
.end(function() {
reply({
message: 'ok'
});
});
In data option of the send function I'm trying to call a function to obtain some value.
What I want is to get the value that comes in the body of a reply, ie the res.body. Upon console.log get this [{ "count ": 3 } ], but if I do a console.log of res.body.count tells me undefined, what could I do to get the value 3.
Thanks.
Since the return doesn't have the extra space in "count " (as mentioned in the comments) the problem was that you were trying to access the count attribute of the array and not the object (first element of the array), so to access it you should do it like:
res.body[0].count
As for the problem of not being able to get the count in your POST, the problem is that countElemnts uses an asynchronous function. The end method of superagent takes a function as parameter and it is only called when it receives a response. By that time, your function had already returned (with undefined, since you didn't return anything).
You should first make the GET call and then send it to a function that will handle the POST. For example:
superagent
.get('/someOtherUrl')
.query({
type: value.type
})
.end(function(err, res) {
if (err) {
console.log(err);
} else {
console.log(JSON.stringify(res.body));
sendCount(res.body[0].count)
}
});
function sendCount(count) {
superagent
.post('/someUrl')
.type('json')
.send({
name: 'value.update',
data: {
//id: request.params.id, // not sure where you are getting these values from,
//type: value.type, // but you should adapt it to your code
count: count
}
})
.end(function() {
reply({
message: 'ok'
});
});
}

Faye PubSub Extension adding extra data field in messages

I am using Faye with Node.js (javascript) for a chat server and I am trying to implement 'notices' so that when one use subscribes, the server will send a message to the channel with a property __messageType = 'subscribe'
The server code looks like so (the ext field is present and working just fine)
var chat = new Faye.NodeAdapter()... //etc
chat.addExtension({
incoming: function(message, callback) {
if (message.channel === '/meta/subscribe') {
console.log('A new user subscribed');
chat.getClient().publish(message.subscription, {
ext: message.ext,
__messageType: 'subscription'});
}
callback(message);
}
});
On my client side I have attached an 'incoming' extension so that people can easily determine if this is a subscription notification and not an actual 'data message'
clientChat.addExtension({
incoming: function(message, callback) {
console.log('Incoming message', message);
message.getType = function() {
return message.__messageType;
};
messge.getData = function(key) {
return message.ext[key];
};
callback(message);
}
});
I am structuring my messages this way that way people can do something like this:
var sub = new Faye.Client(url).subscribe('/messages', function(message) {
if (message.getType() === 'subscribe') console.log('Someone subscribed');
if (message.getType() === 'unsubscribe') console.log('Someone left');
else console.log('Ext data: ', message.ext);
The problem is that my messages are coming through with the ext field wrapped in a 'data' field and I have no idea where it's coming from. My getType and getData methods have been successfully added, but they obviously don't work because the ext field is no longer present at the top level, they are instead embedded in the message's data field.
Expected: Actual:
channel: "/messages" channel: "/messages/"
ext: { variousData } data: {ext: variousData}
getData: function (key) { getData: function(key) {
getType: function () { getType: function() {
id: "4" id: "4"
Does anyone have any idea why I'm getting that 'data' field in my messages?

Resources