I'm currently using Firebase Functions to send automatic push notifications when the database is uploaded. It's working perfectly, I'm just wondering how I can get a specific value from my database, for example PostTitle and display it on, for example title.
In Firebase my database is /post/(postId)/PostTitle
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// database tree
exports.sendPushNotification = functions.database.ref('/posts/{id}').onWrite(event =>{
const payload = {
notification: {
title: 'This is the title.',
body: 'There is a new post available.',
badge: '0',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()){
const token = Object.keys(allToken.val());
console.log(`token? ${token}`);
return admin.messaging().sendToDevice(token, payload).then(response =>{
return null;
});
}
return null;
});
});
If I understand correctly that you want to get the PostTitle from the node that triggers the Cloud Function, the following should do the trick:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// database tree
exports.sendPushNotification = functions.database.ref('/posts/{id}').onWrite(event =>{
const afterData = event.data.val();
const postTitle = afterData.PostTitle; //You get the value of PostTitle
const payload = {
notification: {
title: postTitle, //You then use this value in your payload
body: 'There is a new post available.',
badge: '0',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()){
const token = Object.keys(allToken.val());
console.log(`token? ${token}`);
return admin.messaging().sendToDevice(token, payload)
} else {
throw new Error('error message to adapt');
}
})
.catch(err => {
console.error('ERROR:', err);
return false;
});
});
Note the following points:
You are using the old syntax for Cloud Functions, i.e. the one of versions <= v0.9.1. You should migrate to the new version and syntax, as explained here: https://firebase.google.com/docs/functions/beta-v1-diff#realtime-database
I have re-organised your promise chaining and also added a catch() at the end of the chain.
I'd use ...
var postTitle = event.data.child("PostTitle").val;
while possibly checking, it the title even has a value
... before sending out any notifications.
Related
I am writing a cloud function for firebase for my android app. I can't resolve this error. I am a complete newbie.
29:73 error Each then() should return a value or throw promise/always-return
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/comment')
.onWrite((change, context) => {
// get user ids
const reciever_id = context.params.reciever_id;
const sender_id = context.params.sender_id;
const comment = context.params.comment;
const object_id = context.params.object_id;
const objecttype = context.params.objecttype;
//get the token of reciever
return admin.database().ref("/users/" + reciever_id).once('value').then(snap => {
const token = snap.child("token").val();
// Create a payload
var payload = {
data: {
data_type: "direct_message",
title: "Comment from" + sender_id,
comment: comment,
object_id: object_id,
objecttype: objecttype,
}
};
// Sent To device with token Id : THIS IS LINE 29
return admin.messaging().sendToDevice(token, payload).then(response => {
console.log("Successfully sent message:", response);})
.catch(error => {console.log("Error:", error); });
}); // token
}); // onWrite
IT worked I just changed this
// Sent To device with token Id
return admin.messaging().sendToDevice(token, payload).then(result => {
return console.log("Successfully sent message:", result);
})
I don't know how to send different types of notification to the device from Firebase cloud function in index.js I want to send (comments notification)(like notification).
I am using this code to get following notification to device but I don't know how to get other.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/notification/{user_id}/{notification_id}').onWrite((change, context) => {
const user_id = context.params.user_id;
const notification_id = context.params.notification_id;
console.log('We have a notification to send to : ', user_id);
const fromUser = admin.database().ref(`/notification/${user_id}/${notification_id}`).once('value');
return fromUser.then(fromUserResult => {
const from_user_id = fromUserResult.val().from;
const from_message = fromUserResult.val().message;
console.log('You have new notification from : ', from_user_id);
const userQuery = admin.database().ref(`users/${from_user_id}/username`).once('value');
const deviceToken = admin.database().ref(`users/${user_id}/device_token`).once('value');
return Promise.all([userQuery,deviceToken]).then(result =>{
const userName = result[0].val();
const token_id = result[1].val();
const payload1 = {
notification:{
title: "some is following you",
body: `${userName} is following you`,
icon: "default",
click_action : "alpha.noname_TARGET_NOTFICATION"
},
data:{
from_user_id:from_user_id
}
};
return admin.messaging().sendToDevice(token_id, payload1).then(result=>{
console.log("notification sent");
});
})
.then(response => {
console.log('This was the notification Feature');
return true;
}).catch(error => {
console.log(error);
//response.status(500).send(error);
//any other error treatment
});
});
});
You can change what you are sending to the /notification/${user_id}/${notification_id} node to include fields that will let you identify and create different notifications in the cloud function.
For example, you could add a type field and then:
return fromUser.then(fromUserResult => {
const from_user_id = fromUserResult.val().from;
const from_message = fromUserResult.val().message;
const from_type = fromUserResult.val().type;
Then you could build your notification based on type:
if(from_type === NOTIFICATION_FOLLOW){
payload1 = {
notification:{
title: "some is following you",
body: `${userName} is following you`,
icon: "default",
click_action : "alpha.noname_TARGET_NOTFICATION"
},
data:{
from_user_id:from_user_id
}
};
}else{
//set payload1 for a different notification
}
Add whatever fields are necessary for your payload and extend the control structure as needed.
I have tried Firebase cloud function for sending a notification.My project structure
and this is the index.js,
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.pushNotification = functions.database.ref('/messages').onWrite( event => {
console.log('Push notification event triggered');
const message = event.data.val();
const user = event.data.val();
console.log(message);
console.log(user);
const topic = "myTopic";
const payload = {
"data": {
"title": "New Message from " + user,
"detail":message,
}
};
return admin.messaging().sendToTopic(topic, payload);
});
The above code is misconfigured, when I deploy in Node.js, LOG in Function shows:
"TypeError: Cannot read property 'val' of undefined".
What Actually I am trying to do :
I am trying to extract info from snapshot load into that index.js so that when a new child gets added to Real-time database, it should trigger a notification payload with a title and body.
In Android, I use a child listener, for listening when a new record is added
FirebaseDatabase.getInstance().getReference().child("messages")
OnChildAdded(.....){
if (dataSnapshot != null) {
MessageModel messageModel = dataSnapshot.getValue(MessageModel.class);
if (messageModel != null) {
// do whatever
}
}
But in index.js, I could not able to parse that.
A bit guidance how to fixate index.js according to my database structure would be immensely appreciated.
PS- I have never done coding in JS
If you want more context, I'd be happy to provide it.
Change this:
exports.pushNotification = functions.database.ref('/messages').onWrite( event => {
const message = event.data.val();
const user = event.data.val();
});
to this:
exports.pushNotification = functions.database.ref('/messages').onWrite(( change,context) => {
const message = change.after.val();
});
Please check this:
https://firebase.google.com/docs/functions/beta-v1-diff#realtime-database
The cloud functions were changed and now onWrite has two parameters change and context
The change has two properties before and after and each of these is a DataSnapshot with the methods listed here:
https://firebase.google.com/docs/reference/admin/node/admin.database.DataSnapshot
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/NOTIFICATIONS/{UserId}/{{notification_id}').onWrite((change, context) =>
{
const UserId = context.params.UserId;
const notification = context.params.notification;
console.log('The user Id is : ', UserId);
if(!change.after.exists())
{
return console.log('A Notification has been deleted from the database : ', notification_id);
}
if (!change.after.exists())
{
return console.log('A notification has been deleted from the database:', notification);
return null;
}
const deviceToken = admin.database().ref(`/USER/${UserId}/device_token`).once('value');
return deviceToken.then(result =>
{
const token_id = result.val();
const payload = {
notification : {
title : "Friend Request",
body : "You've received a new Friend Request",
icon : "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log('This was the notification Feature');
});
});
});
I am following a tutorial to deploy a function to firebase. when I deploy I get an unknown error whereas the tutor doesn't. I have looked through this line for line and it's exact. Can anyone else shed any light on this?
// 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(functions.config().firebase);
const ref = admin.database().ref
exports.sendPushNotification = functions.database.ref('/posts/{postId}/question').onWrite(event => {
const payload = {
notification: {
title: 'A Question has been posted',
body: 'Check out the question posted ',
badge: '1',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
};
});
});
Cloud Functions expects a returned Promise or value. Add a return for the case where there is no token value found in the database:
exports.sendPushNotification = functions.database.ref('/posts/{postId}/question').onWrite(event => {
const payload = {
notification: {
title: 'A Question has been posted',
body: 'Check out the question posted ',
badge: '1',
sound: 'default',
}
};
return admin.database().ref('fcmToken').once('value').then(allToken => {
if (allToken.val()) {
const token = Object.keys(allToken.val());
return admin.messaging().sendToDevice(token, payload).then(response => {
});
} else {
return null; // <= ADDED
}
});
});
For my magazine app,I am using Firebase service.One function of this android app is whenever new article is published;notification of new article is sent to all the devices.
I am saving all the device tokens in db like this:
FCMToken
{
userid:deviceToken
}
So whenever new node is added in "published" key in firebase db,FCM function is triggered and messages is sent to all the devices:
Below is my code in javascript for FCM function:
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/published/{msg_id}').onWrite(event => {
const snapshot = event.data;
// Only send a notification when a new message has been created.
if (snapshot.previous.val()) {
return;
}
const msg_id = event.params.msg_id;
const msg_val=admin.database().ref(`messages/${msg_id}`).once('value');
return msg_val.then(msgResult =>{
const msg_title=msgResult.val().title;
const user_id=msgResult.val().userId;
console.log('msg title is',msg_title);
console.log('We have a new article : ', msg_id);
const payload={
data : {
title:"New Article",
body: msg_title,
msgid : msg_id,
userid : user_id
}
};
// const deviceToken = admin.database().ref('/FCMToken/{user_id}').once('value');
admin.database().ref('/FCMToken').on("value", function(dbsnapshot)
{
dbsnapshot.forEach(function(childSnapshot) {
//var childKey = childSnapshot.key;
const childData = childSnapshot.val();
const deviceToken=console.log("device token" + childSnapshot.val());
return admin.messaging().sendToDevice(childData,payload).then(response=>{
console.log("This was notification feature")
console.log("response: ", response);
})
.catch(function(error)
{
console.log("error sending message",error)
});
});
});
});
});
For some reason,notification is only sent to only 1 device(the first token in FCM node).
Update:
I have updated my code and using promise,but for some reason it is still not working,just sending notification to first device token.
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/published/{msg_id}').onWrite(event => {
const snapshot = event.data;
// Only send a notification when a new message has been created.
if (snapshot.previous.val()) {
return;
}
const msg_id = event.params.msg_id;
const msg_val=admin.database().ref(`messages/${msg_id}`).once('value');
return msg_val.then(msgResult =>{
const msg_title=msgResult.val().title;
const user_id=msgResult.val().userId;
console.log('msg title is',msg_title);
console.log('We have a new article : ', msg_id);
const payload={
data : {
title:"New Article",
body: msg_title,
msgid : msg_id,
userid : user_id
}
};
const promises=[];
// const deviceToken = admin.database().ref('/FCMToken/{user_id}').once('value');
admin.database().ref('/FCMToken').once('value').then(function(dbsnapshot)
{
dbsnapshot.forEach(function(childSnapshot) {
//var childKey = childSnapshot.key;
const childData = childSnapshot.val();
const deviceToken=console.log("device token" + childSnapshot.val());
const promise = admin.messaging().sendToDevice(childData,payload).then(response=>{
promises.push(promise)
console.log("This was notification feature")
console.log("response: ", response);
})
return Promise.all(promises)
.catch(function(error)
{
console.log("error sending message",error)
});
});
});
});
});
Response object is giving this output: response: { results: [ { error: [Object] } ],
canonicalRegistrationTokenCount: 0,
failureCount: 1,
successCount: 0,
multicastId: 6411440389982586000 }
You're not using promises correctly throughout your function. There are two things wrong.
First, you should be querying the database using once() instead of on(), and using the promise returned from it in order to proceed to the next item of work:
admin.database().ref('/FCMToken').on("value")
.then(result => /* continue your work here */)
Also, you can't return a promise out of the forEach loop. Instead, you need to return a promise at the top level of the function, as the very last step in the function. This promise needs to resolve when all of the work is done in this function. For your function, this means when all of the messages are sent. You'll have to collect all the promises for all of the messages in an array, then return a single promise that resolves when they all resolve. The general form of that looks like this:
const promises = []
dbsnapshot.forEach(function(childSnapshot) {
// remember each promise for each message sent
const promise = return admin.messaging().sendToDevice(...)
promises.push(promise)
})
// return a single promise that resolves when everything is done
return Promise.all(promises)
Please take care to learn how promises work in JavaScript. You won't be able to write effective functions without dealing with promises correctly.
So I figured out another method to get values.
const tokens= Object.keys(tokensSnapshot.val()).map(e => tokensSnapshot.val()[e]);
Below is my complete method:
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
//Object.values = require('object.values');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/published/{msg_id}').onWrite(event => {
const snapshot = event.data;
// Only send a notification when a new message has been created.
if (snapshot.previous.val()) {
return;
}
const msg_id = event.params.msg_id;
const msg_val=admin.database().ref(`messages/${msg_id}`).once('value');
return msg_val.then(msgResult =>{
const msg_title=msgResult.val().title;
const user_id=msgResult.val().userId;
console.log('msg title is',msg_title);
console.log('We have a new article : ', msg_id);
const payload={
data : {
title:"New Article",
body: msg_title,
msgid : msg_id,
userid : user_id
}
};
const getDeviceTokensPromise = admin.database().ref('/FCMToken').once('value');
return Promise.all([getDeviceTokensPromise, msg_title]).then(results => {
const tokensSnapshot = results[0];
const msgi = 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.');
console.log("tokenslist",tokensSnapshot.val());
const tokens= Object.keys(tokensSnapshot.val()).map(e => tokensSnapshot.val()[e]);
//var values = Object.keys(o).map(e => obj[e])
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.
}
});
return Promise.all(tokensToRemove);
});
});
});
});