Cloud Functions for Firebase and FCM - javascript

I recently developed an application for a school and one of its features is principal's updates. For this, I'm using the Firebase database for this (on Android studio and Xcode). I heard that there is a new Firebase feature that calls cloud functions and I heard that I can integrate Database and FCM. For now, I have this code in index.js:
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.sendNotifications = functions.database.ref('/messageS/{id}').onWrite(event => {
const snapshot = event.data;
// Only send a notification when a message has been created.
if (snapshot.previous.val()) {
return;
}
// Notification details.
const text = snapshot.val().text;
const payload = {
notification: {
title: 'new message recived',
body: text ? (text.length <= 100 ? text : text.substring(0, 97) + '...') : '',
icon: '/app/src/main/res/drawable/logo_he_digita_homepage.png'
}
};
// Get the list of device tokens.
return admin.database().ref('fcmTokens').once('value').then(allTokens => {
if (allTokens.val()) {
// Listing all tokens.
const tokens = Object.keys(allTokens.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(allTokens.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
}
});
});
I receive the log into Firebase console, but I don't see fcmTokens in the database and the device doesn't get the notification. what should I do?
Thanks for the help.

Related

firebase cloud functions for push notifications are not working

in my flutter app, i have saved every devices token to a collection in firebase database and i wrote the code for firebase cloud functions so it sends a message(notification) for every user that subscribed to a topic and have their token is in the tokens collection but it doesnt send anything when i add something to the topic i subscribed them to, heres my cloud functions code using javascrpit in the index file:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().functions);
var newData;
exports.myTrigger = functions.firestore.document('messages/{messageId}').onCreate(async (snapshot, context) => {
//
if (snapshot.empty) {
console.log('No Devices');
return;
}
newData = snapshot.data();
const deviceIdTokens = await admin
.firestore()
.collection('messages')
.get();
var tokens = [];
for (var tokened of deviceIdTokens.docs) {
tokens.push(tokened.data().token);
}
var payload = {
notification: {
title: 'Push Title',
body: 'Push Body',
sound: 'default',
},
data: {
message: newData.message,
click_action: 'FLUTTER_NOTIFICATION_CLICK',
},
};
try {
const response = await admin.messaging().sendToDevice(tokens, payload);
console.log('Notification sent successfully');
} catch (err) {
console.log(err);
}
});
and heres my database structure
and the tokens collection :
what am i doing wrong?
trying his instead :
i should have written :
'''
const deviceIdTokens = await admin
.firestore()
.collection('tokens')
.get()
'''
also for sending a message through the database i should have written "message" in the field because i named the "data"'s key "message" :)

Calling a Firebase Cloud Function 'ForEach' child of a Snapshot

I'm trying to deploy a Firebase Cloud Function that sends a text message to its associated recipient for x number of text messages. The function is triggered in my iOS app when an update is made to the 'send' Realtime Database reference, indicating that the user has pressed the 'send' button.
My Firebase structure is
{
"user1uid": {
"send": false
"messagesToSend": {
"messageuid1": {
"messageText": "What's for dinner?",
"recipientNumber": "+18017378888",
}
"messageuid2:
"messageText": "Who won the Cowboys game?",
"recipientNumber": "+18017377787",
}
}
"user2uid": {
"send": false
"messagesToSend": {
"messageuid1": {
"messageText": "What's for dinner?",
"recipientNumber": "+18017378888",
}
"messageuid2:
"messageText": "Who won the Cowboys game?",
"recipientNumber": "+18017377787",
}
}
}
My code currently only sends one message, and I'm not sure how I can properly iterate through the messagesToSend node for each user and send all the messages in it.
I've been trying to follow the tutorial located here. I have looked at the following Stack Overflow responses but am unable to decipher or derive a solution from them:
Firebase cloud function promises
Am I using ForEach correctly?
My index.js code that sends one message is as follows:
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
const twilio = require('twilio')
const accountSid = functions.config().twilio.sid;
const authToken = functions.config().twilio.token;
const client = new twilio(accountSid, authToken);
const twilioNumber = functions.config().twilio.number;
// Start cloud function
exports.sendSecrets = functions.database
.ref('/{uid}/send')
.onUpdate((change,context) => {
const uid = context.params.uid;
return admin.database().ref(uid+'/messagesToSend').once('value').then(snapshot => {
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key;
var messageData = childSnapshot.val();
**if (messageData.sanitized) return true;**
var message = messageData.messageText;
var phoneNumber = messageData.recipientNumber;
const textMessage = {
body: `From My App - ${message}`,
from: twilioNumber, // From Twilio number
to: phoneNumber // Text to this number
}
return client.messages.create(textMessage)
})
**return snapshot.ref.toString();**
});
});
Please note that the lines marked with ** at either end indicate that I know I need to return something based on error messages I received indicating that 'Each then() should return a value or throw'.
I make the assumption that you are using the twilio-node library that use promises: https://www.npmjs.com/package/twilio.
Since you want to send several messages in parallel, you have to use Promise.all(), as follows:
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
const twilio = require('twilio')
const accountSid = functions.config().twilio.sid;
const authToken = functions.config().twilio.token;
const client = new twilio(accountSid, authToken);
const twilioNumber = functions.config().twilio.number;
// Start cloud function
exports.sendSecrets = functions.database
.ref('/{uid}/send')
.onUpdate((change,context) => {
const uid = context.params.uid;
return admin.database().ref(uid+'/messagesToSend').once('value')
.then(snapshot => {
const promises = [];
snapshot.forEach(function(childSnapshot) {
var key = childSnapshot.key;
var messageData = childSnapshot.val();
//**if (messageData.sanitized) return true;**
var message = messageData.messageText;
var phoneNumber = messageData.recipientNumber;
const textMessage = {
body: `From My App - ${message}`,
from: twilioNumber, // From Twilio number
to: phoneNumber // Text to this number
}
promises.push(client.messages.create(textMessage));
})
return Promise.all(promises);
})
// Edits made below to parentheses/brackets
.then(results => {
//Do whatever you want !!
// e.g. print the results which will be an array of messages
// (see https://www.twilio.com/docs/libraries/node#testing-your-installation)
})
});
You can also simply return Promise.all() as follows:
....
return Promise.all(promises);
})
});

Receiving default push notification Firebase Cloud messaging For Web

I am receiving default push notification instead of my own payload data
in notification.
Here you can see the notification in this Picture below
I am getting no errors on firebase log, also receiving data which I want to send through push notification
Here you can see
and here is the code of index.js file of firebase cloud function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotifications = functions.database.ref(`messages/{notificationId}`).onCreate((event) => {
const receiverId = event.val().recId;
const payload = {
notification: {
title: `New Message from ${event.val().sndrName}!`,
body: event.val().message,
status: "New message",
icon: 'icon-192x192.png'
}
}
console.info(payload);
let tokensList = [];
return admin.database().ref('fcmtokens').orderByValue().equalTo(receiverId).once('value').then((token) => {
console.info(token.val());
if(token.val()) {
tokensList = (Object.keys(token.val()));
console.info(tokensList);
return admin.messaging().sendToDevice(tokensList,payload);
}
})
})
I am very new to firebase cloud functions please tell if I am doing something wrong,TIA

Unable to send notifications using firebase functions after update

So Today I updated the firebase cli and after that deployed a new function. Although the firebase log shows that notifications has been sent to this many tokens, no notification occurs. An error shows in the log
Function returned undefined, expected Promise or value
I searched for answers in stack overflow but nothing helped.
Also I would like to add that before it was showing some different error
TypeError: Cannot read property 'description' of null
and now suddenly it is showing function returned undefined.
Not sure what is wrong. Any help is appreciated.
Index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
function token_send(admin,title_input,body_input,getBody,getDeviceTokensPromise,change){
// Only edit data when it is first created.
if (change.before.val()) {
return 0;
}
// Exit when the data is deleted.
if (!change.after.val()) {
return 0;
}
return Promise.all([getDeviceTokensPromise,getBody]).then(results => {
const tokensSnapshot = results[0];
const notify=results[1];
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
var contentAlert = change.after.val();
// Notification details.
const payload = {
'data': {
'title': title_input,
'body': body_input
}
};
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
console.log("Successfully sent message:", response);
console.log("content alert",contentAlert);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
}
exports.sendNotificationCouncil = functions.database.ref(`path/Post/{pushId}`).onWrite((change,context) => {
const getDeviceTokensPromise = admin.database().ref(`/Token/token_no`).once('value');
const getBody=admin.database().ref(`/Post`).once('value');
var title_input='You have new Post';
var contentAlert = change.after.val();
var body_input=contentAlert.description; //showing error here
token_send(admin,title_input,body_input,getBody,getDeviceTokensPromise,change);
});
You should return the promise (returned by token_send()) in the sendNotificationCouncil Cloud Function, as follows:
exports.sendNotificationCouncil = functions.database.ref(`path/Post/{pushId}`).onWrite((change,context) => {
const getDeviceTokensPromise = admin.database().ref(`/Token/token_no`).once('value');
const getBody=admin.database().ref(`/Post`).once('value');
var title_input='You have new Post';
var contentAlert = change.after.val();
var body_input=contentAlert.description; //showing error here
return token_send(admin,title_input,body_input,getBody,getDeviceTokensPromise,change);
});
Note that it is also a best practice to catch the errors in your Function and in this case return false.

Firebase.child failed: First argument was an invalid path

Possible duplicate. Not sure.
connections: {
connectionID : {
userID: true,
anotherUserID: true
},
users: {
userID : {
deviceToken : "tokenID",
name : "Display Name"
},
anotherUserID : {
deviceToken : "tokenID",
name : "Display Name"
}
}
and so on and so forth.
This is my index.js:
exports.sendConnectionNotification = functions.database.ref('/connections/{connectionID}/{userID}').onWrite(event => {
const parentRef = event.data.ref.parent;
const userID = event.params.userID;
const connectionID = event.params.connectionID;
// If un-follow we exit the function.
if (!event.data.val()) {
return console.log('Connection', connectionID, 'was removed.');
}
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
// Get the user profile.
const getUserProfilePromise = admin.auth().getUser(userID);
and it continues. I am getting this error in my logcat:
Error: Firebase.child failed: First argument was an invalid path: "/users/${userID}/deviceToken". Paths must be non-empty strings and can't contain ".", "#", "$", "[", or "]"
at Error (native)
at Ge (/user_code/node_modules/firebase-admin/lib/database/database.js:111:59)
at R.h.n (/user_code/node_modules/firebase-admin/lib/database/database.js:243:178)
at Fd.h.gf (/user_code/node_modules/firebase-admin/lib/database/database.js:91:631)
at exports.sendConnectionNotification.functions.database.ref.onWrite.event (/user_code/index.js:31:51)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
I do not understand why Firebase is not able to reach the node. Clearly, my path is valid. Where am I going wrong? Sorry, I happen to start learning Firebase Functions just today.
**EDIT 1: **
After replacing:
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
with
const getDeviceTokensPromise = admin.database().ref(`/users/${userID}/deviceToken`).once('value');
I have gotten a new error. My console log displays:
There are no notification tokens to send to.
Here is my full index.js:
// // Create and Deploy Your First Cloud Functions
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
/**
* Triggers when a user gets a new follower and sends a notification.
*
* Followers add a flag to `/followers/{followedUid}/{followerUid}`.
* Users save their device notification tokens to `/users/{followedUid}/notificationTokens/{notificationToken}`.
*/
exports.sendConnectionNotification = functions.database.ref('/connections/{connectionID}/{userID}').onWrite(event => {
const parentRef = event.data.ref.parent;
const userID = event.params.userID;
const connectionID = event.params.connectionID;
// If un-follow we exit the function.
if (!event.data.val()) {
return console.log('Connection', connectionID, 'was removed.');
}
// Get the list of device notification tokens.
const getDeviceTokensPromise = admin.database().ref(`/users/${userID}/deviceToken`).once('value');
// Get the user profile.
const getUserProfilePromise = admin.auth().getUser(userID);
return Promise.all([getDeviceTokensPromise, getUserProfilePromise]).then(results => {
const tokensSnapshot = results[0];
const user = results[1];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return console.log('There are no notification tokens to send to.');
}
console.log('There are', tokensSnapshot.numChildren(), 'tokens to send notifications to.');
console.log('Fetched user profile', user);
// Notification details.
const payload = {
notification: {
title: `${user.userNickName} is here!`,
body: 'You can now talk to each other.'
}
};
// Listing all tokens.
const tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
return admin.messaging().sendToDevice(tokens, payload).then(response => {
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to', tokens[index], error);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
});
});
You can do use (`) instead of (') as i was also having same problem and solved by using this.
thanks
Change
const getDeviceTokensPromise = admin.database().ref('/users/${userID}/deviceToken').once('value');
to
const getDeviceTokensPromise = admin.database().ref('/users/' + userID + '${userID}/deviceToken').once('value');
'/users/${userID}/deviceToken' is not a valid path.
but '/users/123456/deviceToken' where 123456 represents the user ID, is.
maybe you are using single quote instead of back-ticks.
https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
so the path is not concatenated in a right way.

Categories

Resources