Notification not being sent via firebase functions. "undefined" logged in console - javascript

I am trying to send a sample notification to all devices according to their token, however the token is being logged as "undefined" and the notification subsequently fails to deliver
The following lines from my code successfully show me the data from the database:
const notificationSnapshot = change.after.val(); //get new value
console.info(notificationSnapshot);
However, the following gives "undefined", despite the above retrieving the data successfully.
const userToken = notificationSnapshot.token;
console.info(userToken);
Is this not the correct way to retrieve the token to send the notification to all the registered devices in my firebase database?
my whole function (index.js)
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendSampleNotification = functions.database.ref('/User')
.onWrite((change, context) => {
const notificationSnapshot = change.after.val(); //get new value
const userToken = notificationSnapshot.token;
const name = notificationSnapshot.name;
const surname = notificationSnapshot.surname;
console.info(notificationSnapshot);
console.info(userToken);
var message = {
notification: {
title: 'test title',
body: 'test message'
},
token: userToken
};
admin.messaging().send(message).then((response) => {
console.log("Message sent successfully:", response);
return response;
})
.catch((error) => {
console.log("Error sending message: ", error);
});
});

I would say that your issue is very similar to this one since you are having a missing token (showed as undefined) due to the executions times, more or less what Doug was pointing out.
Note that the solution relies on considering the execution times and I’ve seen also that the implementation differs in some method executions but I would say the generals point in the same direction.

Related

Cloud functions with Firebase Realtime Database not triggering onCreate and not sending FCM notification

I'm trying to automatically send notification per onCreate event of RealtimeDatabase, however the function never triggers and never logs anything.
code:
// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.notificationOnArticleCreate = functions.database.ref().onCreate((snapshot, context) => {
const payload = {
notification: {
title: 'cloud function demo',
body: 'this is a test notification'
}
};
admin.messaging().send(payload).then((response) => {
console.log('Successfully sent message:', response);
return {success: true};
}).catch((error) => {
return {error: error.code};
});
console.log('test test');
})
I'm surprised this even compiles/deployed: functions.database.ref().onCreate.
You'll want to indicate the path where the node is created, e.g.
functions.database.ref("/messages/{message}").onCreate(...

Retrieve an array from a Firestore document and store it to Node.Js then use it as tokens to send notifications

I've been trying to figure this out for hours and I just can't. I'm still a beginner with Node.js and Firebase. I need your help to be able to retrieve the tokens array in my "userdata" collection to Node.js and be able to use it to send notifications in the Cloud Function. So far this is what I've been working on. Here is what my database looks like:
The receiverId is gathered from when I have an onCreate function whenever a user sends a new message. Then I used it to access the userdata of a specific user which uses the receiverId as their uid.
In the cloud function, I was able to start the function and retrieve the receiverId and print the userToken[key]. However, when I try to push the token it doesnt go through and it results in an error that says that the token is empty. See the image:
Your help would mean a lot. Thank you!
newData = snapshot.data();
console.log("Retrieving Receiver Id");
console.log(newData.receiverId); //uid of the user
const tokens = [];
const docRef = db.collection('userdata').doc(newData.receiverId);
docRef.get().then((doc) => {
if (doc.exists) {
console.log("DocRef exist");
const userToken = doc.data().tokens;
for(var key in userToken){
console.log(userToken[key]);
tokens.push(userToken[key]);
}
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch((error) => {
console.log("Error getting document:", error);
});
//Notification Payload
var payload = {
notification: {
title: newData.sendBy,
body: 'Sent you a message',
sound: 'default',
},
data: {
click_action : 'FLUTTER_NOTIFICATION_CLICK',
route: '/telconsultinbox',
}
};
console.log("Sending Notification now.");
console.log(tokens);
try{
//send to device
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
console.log(newData.sendBy);
}catch(err){
console.log(err);
}
I think you should avoid using for..in to iterate through an array (you can read more about it in this answer). Try one of these 2 options:
You could use forEach(), which is more elegant:
userToken.forEach((token) => {
console.log(token);
tokens.push(token);
});
for-of statement:
for(const token of userToken){
console.log(token);
tokens.push(token);
}
Also, I would consider renaming userToken to userTokens, since it should contain multiple values. Makes the code a bit more readable.

How to program Firebase Cloud Functions to iterate through array of references?

I'm pretty new to Cloud Functions on Firebase and I'm struggling to program some code to iterate through an array of document references that have been downloaded from the Firestore.
The array is stored in my Firestore and contains references to each admin user in my users collection. Each of these users has a field in their document with their messaging token, which I need to send the message. I've manage to get the code to send a notification to a token that I define as a constant in the code however haven't had any luck sending to the tokens stored in the database.
Here is my code so far;
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// // Create and Deploy Your First Cloud Functions
// // https://firebase.google.com/docs/functions/write-firebase-functions
//
// exports.helloWorld = functions.https.onRequest((request, response) => {
// response.send("Hello from Firebase!");
// });
exports.notifyNewReport = functions.firestore
.document('admin/reportsToReview')
.onUpdate((change, context) => {
console.log('Change to doc function registered');
// Get an object representing the document
const newValueReports = change.after.data().reports;
// ...or the previous value before this update
const previousValueReports = change.before.data().reports;
if (newValueReports.length > previousValueReports.length) {
console.log('Report added to review list');
var adminsArray = ""
admin.firestore()
.collection('admin')
.doc('admins')
.get()
.then(doc => {
adminsArray = doc.data().admins
return console.log('Found admin UID: ' + adminsArray);
})
.catch(error => {
console.error(error);
res.error(500);
});
//Code to get send notification to each device
console.log("Construct the notification message.");
var message = {
notification: {
body: 'There are new reports to review!',
},
token: token
};
admin.messaging().send(message)
}
});
If anyone can point me in the right direction that would be greatly appreciated! :)

Extract specific node value via Firebase Cloud functions

Ok so I'm going to start with some background (skip to MY ISSUE for tl;dr):
I have an application in development that passes data from a Google Sheet into a Firebase Realtime Database in the form of a 2d Array. The data layout of the Google sheet is as shown below:
This data is passed into a Firebase Realtime Database under the node masterSheet via an Apps Script function result shown below:
Which is used as the live database for my mobile web application I am developing using the Ionic Framework (preview below):
I have functions which deal with the setting of "Y" and "N" flags at the correct positions for the sub tasks of each job and a function which sets the overall job completion status flag to "Y" when all sub tasks are done working as intended.
I am trying to add in an automatic email service via Firebase Cloud Functions that sends off a "job completion notification" whenever a job's overall "Completed" status is set to "Y" (i.e the value at ref: 'masterSheet/0/1' is equal to "Y").
So far I have managed to get it to successfully send off the emails via a Firebase Cloud Function using nodemailer and the Firebase Admin SDK to all registered users of the Firebase app whenever a job's overall completed status is changed from an "N" to a "Y" via the onUpdate() method and the .ref() of the location to listen at.
Below is my Index.js file containing the cloud function I am using:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
// The mail service used
const nodemailer = require('nodemailer');
// Cloud Fucntion to export:
exports.onMessageUpdate = functions.database.ref('/masterSheet/{subArray}/1')
.onUpdate((change) => {
var changeRef = change.after.ref.path;
console.log('changeRef: ' + changeRef);
var newVal = change.after.val();
if (newVal == "Y"){
getUsers();
}
})
// Fucntion to get all registers users of the Firebase Project
function getUsers(){
var userEmails = [];
admin.auth().listUsers()
.then(function(listUsersResult) {
listUsersResult.users.forEach(function(userRecord) {
console.log(userRecord);
userEmails.push(userRecord.email);
sendCompletionEmail(userRecord.email)
});
})
.catch(function(error) {
console.log("Error listing users:", error);
});
}
// Function to send automatic emails
function sendCompletionEmail(email){
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: 'xxxxxxxx#gmail.com',
clientId: 'xxxxxxxx.apps.googleusercontent.com',
clientSecret: 'xxxxxxxxxxxxxxx',
refreshToken: 'xxxxxxxxxxxxxx'
}
})
// Email details:
var mailOptions = {
from: 'xxxxxxx',
to: email,
subject: 'Job completion notification',
text: 'This is an automated message to inform you that a job has been fully completed ' +
'with all of its tasks marked as done. \n\nYou can view this (along with any others) from the Completed ' +
'Jobs page within the app.'
}
transporter.sendMail(mailOptions, function (err, res) {
if(err){
console.log('Error');
} else {
console.log('Email Sent');
}
})
}
MY ISSUE:
I want to be able to include the job title in this automatic email that is sent.
logging the result of change.after.ref.path used in the snippet below:
// Cloud Fucntion to export:
exports.onMessageUpdate = functions.database.ref('/masterSheet/{subArray}/1')
.onUpdate((change) => {
var changeRef = change.after.ref.path;
console.log('changeRef: ' + changeRef);
var newVal = change.after.val();
if (newVal == "Y"){
getUsers();
}
})
Produces this log output:
which contains exactly what I want within it... But I don't know how to get it out...
How can I retrieve the second value from the changeRef variable so that I can pass this onto the sendCompletionEmail() function and use it to refer to the item at position [0] for that node?
something like:
var subArray = changeRef[1]
to get the value: 0 out of masterSheet/0/1
which i can store as a variable and use to refer to the job title of the job that has just been completed in the sent off email.
Thanks for any help!
If you're looking for the 0 from the request, that is available from the second parameter that is passed into your Cloud Function (but that you're not declaring).
exports.onMessageUpdate = functions.database.ref('/masterSheet/{subArray}/1')
.onUpdate((change, context) => {
console.log(context.params.subArray);
})
See the Firebase documentation on handling event data and the reference docs for onUpdate.

How do you access a DocumentReference object stored in Firestore from a Cloud Function?

In my Firestore database I store DocumentReferences to users so that I am always using up-to-date user data such as username, profile pictures, and auth tokens.
I am also implementing Cloud Functions to listen for database triggers so that I can send notifications to those specific users about activity related to their posts.
This is where I run into trouble, because I do not know how to use the stored reference object properly inside the Node.js function when I access it like all other database information.
The following is my function code:
exports.countNameChanges = functions.firestore
.document('posts/{postId}')
.onUpdate((change, context) => {
// Retrieve the current and previous value
const data = change.after.data();
const previousData = change.before.data();
var registrationToken = '';
var notification = '';
var postTitle = data.statement;
var userRef = data.userRef; //This is my `DocumentReference` object
if (data.interactionCount > previousData.interactionCount && data.postTypeId == 2131165321) notification = 'You recieved a new comment!';
if (data.interactionCount > previousData.interactionCount && data.postTypeId == 2131165335) notification = 'You recieved a new vote!';
if (data.likes > previousData.likes) notification = 'You have a new post like!' ;
if (data.dislikes > previousData.dislikes) notification = 'You have a new post dislike!' ;
admin.firestore()
.doc(userRef) //This is my `DocumentReference` object
.get()
.then(doc => {
registrationToken = doc.data().token;
var payload = {
data: {
title: postTitle,
body: notification
},
token: registrationToken
};
admin.messaging().send(payload)
.then((response) => {
console.log('Successfully sent message:', response);
})
.catch((error) => {
console.log('Error sending message:', error);
})
});
});
});
My Function Log
I would assume that the DocumentReference object would be easy to work with
inside a Cloud Function since the object is supported for direct storage into Firestore, but I can't figure it out.
If userRef is a DocumentReference type object, then just call get() on it directly. Don't pass it to doc(). You're only supposed to pass string type objects to doc().
userRef.get().then(...)

Categories