Getting undefined error when printing a variable - node.js

I am retrieving data from the firestore to get the data. I store the data into a variable. I need to use that variable in another part of the code. when I use that it shows me an undefined code. I retrieved the user details and sending the notification . In datapayload I need to pass the retrieved field . But it shows an error undefined
cloud functions
const functions = require('firebase-functions');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.splitting = functions.firestore
.document('deyaPayUsers/{authid}/Split/{authid1}/SentInvitations/{autoid}')// Here the path is is triggereing
.onWrite(event =>{
var id = event.params.authid;//here we get the authid of the user who triggeres.
var dbref = db.collection('deyaPayUsers').doc(id);
var sendername ;
var getDoc = dbref.get()
.then(doc =>{
if(!doc.exists){
console.log("No Such Document");
}else{
console.log('Document data of the firstname', doc.data().FirstName);
sendername = doc.data().FirstName;
}
console.log("sent"+sendername);
})
.catch(err => {
console.log("error getting document",err);
});
console.log(id);
var id1 = event.params.authid1;
var splitid = event.params.autoid;
console.log("laha"+sendername);
var document = event.data.data();
var phoneNumber = [];
//In loop
for(var k in document){ // this line says about looping the document to get the phonenumber field in all the invites
phoneNumber.push(document[k].PhoneNumber);// All the phonenumbers are stored in the phoneNumber array.
}
console.log("The json data is " +phoneNumber);
var ph1 = document.Invite1.PhoneNumber;//It gets the single user phoneumber in Invite1
var amount = document.Invite1.Amount;//It gets the amount of the single user in invite1 in integer
var a = amount.toString();// It is used to convert the Integer amount into String
console.log(a);
console.log(document);
console.log(ph1);
var deyaPay = db.collection("deyaPayUsers");// This path is user profile path to query with phonenumber
for(var k in document){ // here It is the loop in a document to get the phonenumber
var p = document[k].PhoneNumber;// here we get the phonenumber for every loop in invite and stored in p variable
var am = document[k].Amount;// we get the amount of that invite
var ams = am.toString();// converting integer amount into string
console.log("AMount of the user"+ams);
let phQuery = deyaPay.where('PhoneNumber','==',p)// This line says that checking the user proile PhoneNumber field and the document phonenumber which is stored in p variable.
.get()
.then(snapshot => {
snapshot.forEach(doc=>{ // It loops all the documnets whether the PhoneNumbers are matching with user profiles phonenumber
console.log(doc.id, " => ", doc.data());// If matches it prints the doc id and the user data
var userData = doc.data();//Here we get the doc data of that matched phonenumber
var userId = doc.id;// here it we get the id of the Matched profile
var FirstName = userData.FirstName;
console.log(FirstName);
var LastName = userData.LastName;
console.log(FirstName);
var FullName = FirstName + LastName;
console.log(FullName);
var Token = userData.FCMToken; // we need to take that fcm token to send the notification
console.log(userId);
console.log("FCM Token for that phoneNumber" + Token);
console.log(userData.PhoneNumber);
console.log(ph1 + "Exist In DB");
var msg = FirstName + " "+ "requested you to pay $" +ams;
console.log("total notification is" +msg);
let payload = { //This is for sending notification message
notification: {
title: "Message",
body: msg,
sound: "default",
},
'data':{// these is the data it calls in the messagereceived method
'Name':sendername,
'Amount':ams
}
};
console.log(payload);
return admin.messaging().sendToDevice(Token, payload).then((response)=> { // This method is used for returning the notification to a specific device
console.log(Token);
console.info("Successfully sent notification")
}).catch(function(error) {
console.warn("Error sending notification " , error)
});
});
}) .catch(err => {
console.log('Error getting documents', err);
});
}
});

try this, Problem is whenever you perform any async operation in node.js, it does not wait for it to complete execute next line.
const functions = require('firebase-functions');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.splitting = functions.firestore
.document('deyaPayUsers/{authid}/Split/{authid1}/SentInvitations/{autoid}') // Here the path is is triggereing
.onWrite(async (event) => {
try {
const responses = [];
var id = event.params.authid; //here we get the authid of the user who triggeres.
var dbref = db.collection('deyaPayUsers').doc(id);
var sendername;
var doc = await dbref.get();
if (!doc.exists) {
console.log("No Such Document");
} else {
console.log('Document data of the firstname', doc.data().FirstName);
sendername = doc.data().FirstName;
}
console.log(id);
var id1 = event.params.authid1;
var splitid = event.params.autoid;
console.log("laha" + sendername);
var document = event.data.data();
var phoneNumber = [];
//In loop
for (var k in document) { // this line says about looping the document to get the phonenumber field in all the invites
phoneNumber.push(document[k].PhoneNumber); // All the phonenumbers are stored in the phoneNumber array.
}
console.log("The json data is " + phoneNumber);
var ph1 = document.Invite1.PhoneNumber; //It gets the single user phoneumber in Invite1
var amount = document.Invite1.Amount; //It gets the amount of the single user in invite1 in integer
var a = amount.toString(); // It is used to convert the Integer amount into String
console.log(a);
console.log(document);
console.log(ph1);
var deyaPay = db.collection("deyaPayUsers"); // This path is user profile path to query with phonenumber
for (var k in document) { // here It is the loop in a document to get the phonenumber
var p = document[k].PhoneNumber; // here we get the phonenumber for every loop in invite and stored in p variable
var am = document[k].Amount; // we get the amount of that invite
var ams = am.toString(); // converting integer amount into string
console.log("AMount of the user" + ams);
let snapshot = await deyaPay.where('PhoneNumber', '==', p) // This line says that checking the user proile PhoneNumber field and the document phonenumber which is stored in p variable.
.get();
snapshot.forEach(doc => { // It loops all the documnets whether the PhoneNumbers are matching with user profiles phonenumber
console.log(doc.id, " => ", doc.data()); // If matches it prints the doc id and the user data
var userData = doc.data(); //Here we get the doc data of that matched phonenumber
var userId = doc.id; // here it we get the id of the Matched profile
var FirstName = userData.FirstName;
console.log(FirstName);
var LastName = userData.LastName;
console.log(FirstName);
var FullName = FirstName + LastName;
console.log(FullName);
var Token = userData.FCMToken; // we need to take that fcm token to send the notification
console.log(userId);
console.log("FCM Token for that phoneNumber" + Token);
console.log(userData.PhoneNumber);
console.log(ph1 + "Exist In DB");
var msg = FirstName + " " + "requested you to pay $" + ams;
console.log("total notification is" + msg);
let payload = { //This is for sending notification message
notification: {
title: "Message",
body: msg,
sound: "default",
},
'data': { // these is the data it calls in the messagereceived method
'Name': sendername,
'Amount': ams
}
};
console.log(payload);
const response = await admin.messaging().sendToDevice(Token, payload)
console.info("Successfully sent notification")
responses.push(response);
});
};
return responses; //here
} catch (error) {
console.info("error", error)
}
});

Related

Retrieve data from collection/document inside of onCreate in firebase firestore with cloud function onCreate

I am trying to retrieve the device token from my users collection in cloud firestore, inside of an onCreate. d.get("token"); below returns an undefined.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const fcm = admin.messaging();
exports.senddevices = functions.firestore
.document("notification/{id}")
.onCreate((snap, context) => {
const name = snap.get("name");
const subject = snap.get("subject");
return admin.firestore()
.doc("users/{id}")
.get()
.then((d) => {
if (!d.empty) {
const payload = {
notification: {
title: "from " + name,
body: "subject " + subject,
sound: "default",
},
};
d.
const token = d.get("token");
return fcm.sendToDevice(token, payload);
} else {
console.log("User not found");
}
});
});
You need to read the value of ID from the path provided in .document() method and then use it in .doc() method to fetch the document as shown:
exports.senddevices = functions.firestore
.document("notification/{id}")
.onCreate((snap, context) => {
const name = snap.get("name");
const subject = snap.get("subject");
const id = context.params.id
// ^^ reading value of ID
return admin.firestore()
.doc("users/"+id) // <-- Using ID,
//or .doc(`users/${id}`)
.get()
.then((d) => {
if (d.exists) {
const payload = {
notification: {
title: "from " + name,
body: "subject " + subject,
sound: "default",
},
};
const token = d.data().token;
return fcm.sendToDevice(token, payload);
} else {
console.log("User not found");
}
});
});
Do notice you are fetching a single document so you should use d.exists to check if document exists instead of .empty which is used on a QuerySnapshot. Also to read value of token field you should first use .data() method to get contents of that document as an object.

The change data of the updated firestore document in firebase cloud function returns undefined

I'm using firestore onUpdate trigger and trying to get the data of the
updated document (field name, new value).
const newValue = change.after.data();
const name = newValue.name;
I expect to notify the user with the new mark added to his profile for example:
the new attend mark is 50
But when i display them in the body of the notification it shows:
Here is the full cloud function snippet:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//functions.config().firebase
exports.updateUser = functions.firestore
.document('stuThird/{userId}/stuMarks/{markId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const name = newValue.name;
var body = ' the new mark of' + name + 'is'+ newValue;
if(newValue){
var message = {
notification: {
title: 'new mark changed',
body: body,
},
topic: 'bebo'
};
}
// Send the message.
return admin.messaging().send(message)
.then((message) => {
return console.log('Successfully sent message:', message);
})
.catch((error) => {
console.error('Error sending message:', error);
});
// perform desired operations ...
});
**
here is the database:
**

Getting document not a function

I am attempting to retrieve the boolean child (notificationsOn) of an object stored as a Firestore document to see if the rest of a function should be executed.
The overall function works to completion without this portion, but adding the portion from let threadDoc to the if statement presents a "threadDoc.get is not a function" error. I think my syntax is wrong but I don't know how, as a similar function works in a later part of the function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendDMNotification =functions.firestore.document('/dm_threads/{thread_id}/messages/{message_id}').onCreate((snapshot, context) => {
const newMessage = snapshot.data();
const senderName = newMessage.authorName;
const senderID = newMessage.authorUID;
const messageText = newMessage.message;
const recipientID = newMessage.recipientUID;
var notificationsOn = null;
let deviceTokenQuery = admin.firestore().collection(`/users/${recipientID}/device_tokens/`);
var idsToBeSorted = [senderID, recipientID];
idsToBeSorted.sort();
var threadID = idsToBeSorted[0] + idsToBeSorted[1];
console.log(recipientID);
console.log(threadID);
let threadDoc = admin.firestore().document(`users/${recipientID}/threads/${threadID}/`);
return threadDoc.get().then(doc => {
let notificationsOn = doc.data.notificationsOn;
console.log(notificationsOn);
if (notificationsOn !== false){
return deviceTokenQuery.get().then(querySnapshot => {
let tokenShapshot = querySnapshot.docs;
const notificationPromises = tokenShapshot.map(doc => {
let token_id = doc.data().tokenID;
const payload = {
data: {
title: senderName,
body: messageText,
senderID: senderID,
senderName: senderName
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log("Notification sent: ", response);
})
.catch(error => {
console.log("Error sending message: ", error);
});
});
return Promise.all(notificationPromises);
});
}
return;
});
});
admin.firestore().document() was supposed to be admin.firestore().collection(...).doc(...)
This fixed my problem
I think you meant to say admin.firestore() instead of functions.firestore.

Use async forEach loop while fetching data from firestore

I have firestore data somewhat like this:
"Support": {
"userid":"abcdxyz",
"message": "hello"
}
I am using nodejs to fetch my data and I also want to show the email address and name of the person who sent this message. So I am using following function:
database.collection("support").get().then(async function (collections) {
var data = [];
console.log("data collected");
collections.forEach(async function (collection) {
var temp = {};
var collectionData = collection.data()
var userInfo = await getUserDetails(collectionData.userId)
temp.name = userInfo.name
temp.supportMessage = collectionData.supportMessage
data.push(temp)
console.log("data pushed")
});
console.log("data posted")
return res.status(200).end(JSON.stringify({ status: 200, message: "Support Message fetched successfully.", data: data }))
}).catch(error => {
return res.status(500).end(JSON.stringify({ status: 500, message: "Error: " + error }))
});
Here the sequence of logs is following: data collected, data posted, data pushed
I want the sequence like this: data collected, data pushed (x times), data posted
Use following code:
database.collection("support").get().then(async function (collections) {
var data = [];
console.log("data collected");
for await(let collection of collections){
var temp = {};
var collectionData = collection.data()
var userInfo = await getUserDetails(collectionData.userId)
temp.name = userInfo.name
temp.supportMessage = collectionData.supportMessage
data.push(temp)
console.log("data pushed")
}
console.log("data posted")
return res.status(200).end(JSON.stringify({ status: 200, message: "Support Message fetched successfully.", data: data }))
}).catch(error => {
return res.status(500).end(JSON.stringify({ status: 500, message: "Error: " + error }))
});
OR
Use can use
var promise = Promise.all(collections.map((collection) =>{
...
return await ... //or a promise
}));
promise.then(() => {
console.log("posted");
return res.status(200).end(...);
})
I solved my answer with the help of #estus comment.
Credit: #estus
var data = [];
var tempCollection = [];
collections.forEach(collection => {
tempCollection.push(collection.data());
});
for (collection of tempCollection) {
var temp = {};
var userInfo = await getUserDetails(collection.userId)
temp.name = userInfo.name
temp.supportMessage = collection.supportMessage
data.push(temp)
}
It solved my problem very easily.
I know this might not the OP's exact use case but if you're interested in compiling the results of a collection query into an array, you can still use the .docs property of a QuerySnapshot to obtain the list of items:
...
const userId = <some-id>;
const usersRef = db.collection(`users`)
const usersSnapshot = await usersRef.where("id", "==", userId).get()
if (usersSnapshot.empty) {
console.log('found no matching user ', userId);
return;
}
console.log(`found ${usersSnapshot.size} user records`);
const userResults = []
// this block here doesn't construct the results array before the return statement
// because .foreach enumerator doesn't await: https://stackoverflow.com/q/37576685/1145905
// usersSnapshot.forEach((doc) => {
// // doc.data() is never undefined for query doc snapshots
// //console.log(doc.id, " => ", doc.data());
// userResults.push[doc.data()];
// });
for (user of usersSnapshot.docs) {
// console.log(user.id, " => ", user.data());
userResults.push(user.data());
}
...
A more detailed example is here

Firebase functions how to get an array of values from Firebase Database

I want to obtain an array of all the users of my database in the second function that I use ""return admin.database().ref("/Usuarios").once('value')
" so that we can send a notification to all these users.How could all users get in the second function? Thank you
I have this code:
let functions = require('firebase-functions');
let admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotificationNewAd = functions.database.ref('/Alertas/{notiId}').onWrite((change, context) => {
// Only edit data when it is first created.
if (change.before.exists()) {
return null;
}
// Exit when the
data is deleted.
if (!change.after.exists()) {
return null;
}
//te escribe el json de el mensaje nuevo
const afterData = change.after.val();
console.log("afterData: ", afterData);
//get lat and lng of Ad
const name = afterData.name;
console.log("Name: "+name);
//get lat and lng of Ad
const lat = afterData.lat;
const lng = afterData.lng;
console.log("Lat y Lng", "lat: "+lat+" lng: "+lng);
//get lat and lng of Ad
const adType = afterData.typeAd;
console.log("Tipo: "+adType);
//get the user id of the ad
const notiId = context.params.notiId;
console.log("notiId: ", notiId);
const userId = afterData.userId;
console.log("userId: ", userId);
return admin.database().ref("/Usuarios").once('value')
.then(snap => {
const userName = snap.child("name").val();
return console.log("userName: ", userName);
});
});
It looks like this is what you are asking:
exports.sendNotificationNewAd =
functions.database.ref('/Alertas/{notiId}')
.onCreate((noti_snap, context) => {
//te escribe el json de el mensaje nuevo
const notif = noti_snap.val();
console.log("notif: ", notif);
//get lat and lng of Ad
const name = notif.name;
console.log("Name: " + name);
//get lat and lng of Ad
const lat = notif.lat;
const lng = notif.lng;
console.log("Lat y Lng", "lat: " + lat + " lng: " + lng);
//get lat and lng of Ad
const adType = notif.typeAd;
console.log("Tipo: " + adType);
//get the user id of the ad
const notiId = context.params.notiId;
console.log("notiId: ", notiId);
const userId = notif.userId;
console.log("userId: ", userId);
return admin.database().ref("/Usuarios").once('value')
.then(snap => {
let children = [];
snap.forEach(child_snap => {
children.push(child_snap.val()); // build children
});
return children;
})
.then(children => {
children.map(child => {
let message = {
notification: {
title: "message",
body: "body"
},
token: child.device_token
}
admin.messaging().send(message).catch(console.log);
});
return null;
})
.then( () => {
return notif.ref.remove(); // consume send request
})
.catch(console.log);
});

Resources