How to read the latest document created in Firestore using node? - node.js

I wrote a cloud function, to listen for document creation in a collection, in my database
here is the function,
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.myTrigger = functions.firestore.document('FCM/{id}').onCreate(async (snapshot, context) => {
//
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = 'hello';
const deviceIdTokens = await admin
.firestore()
.collection('FCM')
.get();
var tokens = [];
var i=0;
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().ar1[i]);
i++;
}
var payload = {
notification: {
title: 'push title',
body: 'push body',
sound: 'default',
},
data: {
push_key: 'Push Key Value',
key1: newData,
},
};
try {
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
});
This function works weirdly,
For example, sometimes it sends notification, and sometimes it does not.
I don't know how to resolve this issue,
In my arr1 field, i have an array of device tokens, to whom i want to send notifications to,
i want the function to send notifications only to the devices(using tokens) which are just created(in the newly created document ),then delete the document.
I think it's sending notifications to all the documents at once.
I'm pretty new at node..
Is there anyway to only send notifications to the device tokens present in only one document (..the latest created document)?.. I think it's sending notifications to all.
please help me out.
UPDATE:- Here is my document structure

Firstly, in an onCreate handler, you can access the id of just the newly created document by through the snap object, you can access the id via snapshot.id and the body via snapshot.data().
You already have the newly created document fetched, so no need to fetch entire collection. You should replace this entire section:
const deviceIdTokens = await admin
.firestore()
.collection('FCM')
.get();
var tokens = [];
var i=0;
for (var token of deviceIdTokens.docs) {
tokens.push(token.data().ar1[i]);
i++;
}
with this:
const tokens = snapshot.data().ar1;
UPDATE: To delete the new document, you could do
await firestore().collection("FCM").doc(snapshot.id).delete();
since the new document belongs in the FCM collection, not "helpReqs"

Related

Mongoose Node.JS check for duplicate items in database array

I am creating a messaging application and I am looking for a way to check to see if there already exists a conversation between people before a new one is created. Here is the implementation I currently have:
export const createConversation = async (req, res) => {
const conversation = req.body.conversation;
const message = req.body.message;
try {
//Check if conversation between participants exists
const newConversation = await Conversation.find({participants: conversation.participants});
if(newConversation != null) {
newConversation = new Conversation(conversation);
}
message.conversationId = newConversation._id;
const newMessage = new Messages(message);
await newConversation.save();
await newMessage.save();
res.status(201).json({conversation: newConversation, message: newMessage});
} catch (error) {
res.status(409).json({ message: error.message });
}
};
I want to check if there exists a conversation in MongoDB atlas where the participant list contains the same Ids as the conversation that is trying to be created. What is the most efficient way to implement this?

Currently I am facing challenge in sending proactive 1 to 1 messages to a large number of users in parallel without delay

Solution illustration:
Currently, i am facing challenge in sending proactive 1 to 1 messages to a large number of users in parallel without delay.
Right now I am trying these approaches:
For an array of conversationID’s we use .map() to call our async/await function that sends a proactive messages, then Promise.all() to gather them back up again.
I am also trying to create workers using cluster module and distributing split of conversationid’s to each worker.
What is the recommended approach to overcome this issue?
Can I improve the solution by integrating with Microsoft graph API?
// Listen for incoming notifications and send proactive messages to users.
server.get('/api/notify', async (req, res) => {
// map through the agents list
const promises = userslist.map(async agent => {
var sendmsg = sendUserMessage(user.conversationid);
return sendmsg;
})
const results = await Promise.all(promises)
console.log(results);
res.json(results);
res.end();
});
// function to send message
const sendUserMessage = async function (userConversationReference) {
try{
MicrosoftAppCredentials.trustServiceUrl(serviceUrl);
var credentials = new MicrosoftAppCredentials(process.env.BotId, process.env.BotPassword);
var connectorClient = new ConnectorClient(credentials, { baseUri: serviceUrl });
var message = MessageFactory.text("Hello this is a test notification!");
// User Scope
const conversationParameters = {
isGroup: false,
channelData: {
tenant: {
id: process.env.TENANT_ID
}
},
bot: {
id: process.env.BotId,
name: process.env.BotName
},
members: [
{
id: userConversationReference
}
]
};
var conversationResponse = await connectorClient.conversations.createConversation(conversationParameters);
var response = await connectorClient.conversations.sendToConversation(conversationResponse.id, message);
return response;
} catch (error) {
return error.statusCode;
console.error(error);
}
}
Here is documentation on how to Optimize your bot with rate limiting in Teams.
Sharing code snippet from the doc for your reference:
try
{
// Perform Bot Framework operation
// for example, await connector.Conversations.UpdateActivityAsync(reply);
}
catch (HttpOperationException ex)
{
if (ex.Response != null && (uint)ex.Response.StatusCode == 429)
{
//Perform retry of the above operation/Action method
}
}
Sample code references:
Teams Proactive Messaging Samples
Company Communicator App Template

How to keep track of users by identifier using Node.JS

I am listening to a small socket.io chat application. Every time a user joins, the server emits a message with the user's name and an id generated on the spot. However, when the user sends a message, the server only broadcasts the message with the userid, and without the username. This snippet of code shows the events that are triggered when a user connects and sends a message.
socket.on('chat_new_user', (data) => {
var json = JSON.parse(data);
console.log('new user!')
console.log(json.userid)
console.log(json.username)
});
socket.on('chat_new_message', (data) => {
var json = JSON.parse(data);
console.log(`${json.userid} - new message!`)
console.log(json.msg)
My issue is, how can I log the user's name to my console when he sends a new message, even though I only have the userID in the message json?
You may need to store the users data in an array
const users = []
socket.on('chat_new_user', (data) => {
var json = JSON.parse(data);
console.log('new user!')
console.log(json.userid)
console.log(json.username)
users.push({ id: json.userid, username: json.username })
});
socket.on('chat_new_message', (data) => {
var json = JSON.parse(data);
console.log(`${json.userid} - new message!`)
const user = users.find((user) => user.id === json.userid)
console.log(json.msg)
// check whether `user` exit
if (user) {
console.log(user.username)
}

Problem with cloud function push notification

In my flutter firebase app, I am trying to configure push notification but I am finding it very challenging since I am very new to javascript,. The below codes, I am trying to send a notification based on a user activity item field(comment). If the comment field of the activity item in empty, it means the post of that activity item is being liked and if it is not empty, it means the post of that activity item is being commented on. Most of the code works from getting the user device token, saving it, and even console login snapshot of the data and sending the message to the device. But the problem is when the cloud function triggers and the message is being sent to the user device, the message body appears to be null, I am getting the hint that I am not writing the switch statement on the right value of the activity item(comment). I have been reviewing this some months now, Thank you
//This part of the cloud function code to listens to new document on
// the activity path
exports.onCreateActivityNotification = functions.firestore
.document('/activities/{userId}/userActivities/{userActivitiesId}')
.onCreate(async (snapshot, context) => {
console.log('activity notification created', snapshot.data());
//getting user connected to the activity
const userId = context.params.userId;
const createdActivityItem = snapshot.data();
const usersRef = admin.firestore().doc(`users/${userId}`);
const doc = await usersRef.get();
const androidNotificationToken =
doc.data().androidNotificationToken;
//checks if user has an androidnotification token
if(androidNotificationToken){
//sennds notification if users has a token
sendNotification(androidNotificationToken, createdActivityItem )
} else {
console.log('no notification token');
}
//function for sending the notification, I am very sure my problem is coming from this
//part of the code. I am not writing the switch statement on the right value. I think I
//need to get the exact fields of the activity item
function sendNotification(androidNotificationToken, userActivities)
{
let body;
switch (userActivities){
case userActivities.comment !== null:
body = `${userActivities.fromUserId} commented :
${userActivities.comment}`
break;
case userActivities.comment === null:
body = `${userActivities.fromUserId} liked your punch`
break;
default:
break;
}
//creates message for push notification
const message = {
notification: {body: body},
token: androidNotificationToken,
data: {recipient: userId},
};
//sends message with admin.messaging()
admin
.messaging()
.send(message)
.then(response => {
return console.log('message sent', response);
}).catch(error =>{
console.log('error sending message', error);
})
}
});
// This is the code to create the activity item for which triggers the cloud function
// just for reference
static void addActivityItem(
{String currentUserId, Post post, String comment}) {
if (currentUserId != post.authorId) {
activitiesRef.document(post.authorId).collection('userActivities').add({
'fromUserId': currentUserId,
'postId': post.id,
'seen': '',
'postImageUrl': post.imageUrl,
'comment': comment,
'timestamp': Timestamp.fromDate(DateTime.now()),
});
}
}
Try changing your switch like this:
switch (userActivities.comment){
case null:
body = `${userActivities.fromUserId} commented : ${userActivities.comment}`
break;
default: body = `${userActivities.fromUserId} liked your punch`
}
or consider using if else

How to update the user object in back4app?

I use Node.js and back4app.com
I try to update the user object. Therefore I have read a lot and found this promissing documentation:
let progressId = "xyz";
let userId = "12354"; //aka objectId
const User = new Parse.User();
const query = new Parse.Query(User);
// Finds the user by its ID
query.get(userId).then((user) => {
// Updates the data we want
user.set('progressId', progressId);
// Saves the user with the updated data
user.save()
.then((response) => {
console.log('Updated user', response);
})
.catch((error) => {
console.error('Error while updating user', error);
});
});
But there also is a warning. It states:
The Parse.User class is secured by default, you are not able to invoke save method unless the Parse.User was obtained using an authenticated method, like logIn, signUp or current
How would this look like in code?
My solution
Well, I got it to work. While I figured it out, I have found some small show stoppers. I list it for anyone it may concern.
Thanks #RamosCharles I added the Master Key in Parse._initialize. Only with that .save(null, {useMasterKey: true}) works. Take notice, without null it also won't work.
That's my working code:
let progressId = "xyz";
const User = Parse.Object.extend('User'); //instead of const User = new Parse.User();
const query = new Parse.Query(User);
query.equalTo("objectId", '123xyz');
query.get(userId).then((userObj) => {
// Updates the data we want
userObj.set('progressId', progressId);
// Saves the user with the updated data
userObj.save(null, {useMasterKey: true}).then((response) => {
console.log('Updated user', response);
}).catch((error) => {
console.error('Error while updating user', error);
});
});
Now I'm wondering
why my working code is different from documentation?
how secure is my code? And what is to do to get it more secure?
Yes, their API Reference is very helpful! On this section, there's a "try on JSFiddle" button, have you already seen that?
To update a user object, you must use the Master Key. On the frontend, it's not recommended, and it's better to create a cloud code function and call it on your frontend. However, for test purposes, you can keep using the API Reference, but on JSFiddle, you need to do some changes, here is their sample code, but with the adjustments:
Parse.serverURL = 'https://parseapi.back4app.com';
Parse._initialize('<your-appID-here>', '<your-JSKey-here>', '<Your-MasterKey-here>');
const MyCustomClass = Parse.Object.extend('User');
const query = new Parse.Query(MyCustomClass);
query.equalTo("objectId", "<object-ID-here>");
query.find({useMasterKey: true}).then((results) => {
if (typeof document !== 'undefined') document.write(`ParseObjects found: ${JSON.stringify(results)}`);
console.log('ParseObjects found:', results);
}, (error) => {
if (typeof document !== 'undefined') document.write(`Error while fetching ParseObjects: ${JSON.stringify(error)}`);
console.error('Error while fetching ParseObjects', error);
});
You'll need to insert the "_" before the "initialize" in your "Parse._initialize" and insert the Master Key in your query as I did on the query.find.

Resources