I want to send a notification to a specific device so I write this function and its work right but I got undefined in the username
Logs output:
Get this
after: { '-LhjfwZeu0Ryr6jYRq5r': { Price: '888', date: '2019-6-19', description: 'Ghh', id: 50, nameOfProblem: 'Vbh', providerName: 'Loy', providerService: 'Carpenter', statusInfo: 'Incomplete', time: '15:22', username:"devas" }}
And the username is undefined
Here is the function
exports.sendPushR = functions.database.ref('/request/{pid}/{uid}/orders')
.onWrite(async (snapshot, context) => {
const registrationTokens = "------";
const providerId = context.params.pid;
const userId = context.params.uid;
const event = context.params;
console.log("event", event);
console.log(`New Order from ${userId} to ${providerId}`);
const afterData = snapshot.after.val(); // data after the write
const username = snapshot.after.val().username;
console.log(afterData);
console.log(username);
const payload = {
notification: {
title: 'Message received',
body: `You received a new order from ${username} check it now! `,
sound: "default",
icon: "default",
}
};
try {
const response = await admin.messaging().sendToDevice(registrationTokens, payload);
console.log('Successfully sent message:', response);
}
catch (error) {
console.log('Error sending message:', error);
}
return null;
});
It looks like the code you wrote is meant to run when a new order is added to the database. But you've declared it to trigger like this:
exports.sendPushR = functions.database.ref('/request/{pid}/{uid}/orders')
.onWrite(async (snapshot, context) => {
This means that the code instead triggers whenever anything is written under the orders node for a user. To trigger only when an order is written under that orders node, define your trigger as:
exports.sendPushR = functions.database.ref('/request/{pid}/{uid}/orders/{orderid}')
.onWrite(async (snapshot, context) => {
The difference above is that the path now includes {orderid} meaning that it triggers one level lower in the tree, and your snapshot.after will no longer contain the -L level.
Since you actually only seem to care about when an order gets created, you can also only trigger on that (meaning your function won't get called when an order gets updated or deleted). That'd be something like this:
exports.sendPushR = functions.database.ref('/request/{pid}/{uid}/orders/{orderid}')
.onCreate(async (snapshot, context) => {
...
const afterData = snapshot.val();
const username = snapshot.val().username;
console.log(afterData);
console.log(username);
...
});
Here we again trigger on the lower-level in the JSON. But since we now trigger onCreate, we no longer have a before and after snapshot, and instead just do snapshot.val() to get the data that was just created.
Since the object you are retrieving has a generated member you could use a for-in loop to retrieve the value.
const object = snapshot.after.val()
for(const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
if(element.username) {
console.log(element.username);
break;
}
}
}
Related
I am trying to configure the index.js file in javascript for push notifications for firebase iOS. here is my code in question:
exports.newsFRA = functions.database
.ref('news/FRA/{uid}')
.onCreate((snapshot, context) => {
let newsItemFRA = snapshot.val()
sendNotificationFRA(newsItemFRA)
})
function sendNotificationFRA(newsItemFRA) {
let title = newsItemFRA.title
let message = newsItemFRA.message
let payload = {
notification: {
title: 'FRA News',
body: title,
sound: 'default'
}
}
console.log(payload)
let topic = 'FRA'
admin.messaging().sendToTopic(topic, payload)
}
exports.newsLHR = functions.database
.ref('news/LHR/{uid}')
.onCreate((snapshot, context) => {
let newsItemLHR = snapshot.val()
sendNotificationLHR(newsItemLHR)
})
function sendNotificationLHR(newsItemLHR) {
let title = newsItemLHR.title
let message = newsItemLHR.message
let payload = {
notification: {
title: 'LHR News',
body: title,
sound: 'default'
}
}
console.log(payload)
let topic = 'LHR'
admin.messaging().sendToTopic(topic, payload)
}
the 2 function are the same , what change is the topic and the matching name. I have 21 of the same.
I am sure there is a better way to do it by detecting in firebase the content of the authenticated current user 's child called "base": and using the value (FRA , CDG , ORD etc) to define the topic. so I dont have to repeat the same function for each available base.
Hope I am not too confusing and I greatly appreciate any help. thank you
You can use multiple wildcards and take the value of the parameter to determine what to do. For example, this is valid:
exports.news = functions.database
.ref('news/{x}/{uid}')
.onCreate((snapshot, context) => {
const x = context.params.x
let newsItem = snapshot.val()
sendNotification(x, newsItem)
})
Firebase Event Listener. I want to execute if data says: "friend request" and not just on data change.
I've tried to search for listeners. I found:
https://firebase.google.com/docs/database/admin/retrieve-data
I feel like I'm close, but I need help with the specifics.
exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}').onWrite((change, context) => {
const user_id = context.params.user_id;
const notification_id = context.params.notification_id;
const deviceToken = admin.database().ref('/' + user_id +'/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! <3",
icon: "default"
}
};
});
});
My code executes(as expected), both when I write but also when I delete. However: Is there a way to instead, check if something specific is written to the database?
Bear with me, coming from java. But something like:
functions.database.ref('/Notifications/{user_id}/{notification_id}/type("friend request")')
.onWrite((write, context) => {.....
I want a listener, to check what data/notification type, I'm writing to the database. And then execute accordingly.
Best regards.
If I understand correctly, you need to do as follows, checking the value of type:
exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}').onWrite((change, context) => {
const user_id = context.params.user_id;
const notification_id = context.params.notification_id;
const afterData = change.after.val();
if (afterData.type === "friend request") {
const deviceToken = admin.database().ref('/' + user_id + '/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! <3",
icon: "default"
}
};
//Here you need to return the promise returned by an asynchronous operation, probably return admin.messaging()....
});
} else {
console.log("Not a friend request");
return null;
}
});
You can limit when a Cloud Function gets triggered:
by the path that data is written to, and
by the type of write operation (create, update, delete) that is being performed.
There is no way to limit the Cloud Functions trigger on the content that is being written.
If you must filter on the content, you will have to do that inside of the code of your Cloud Function, as Renaud's answer shows.
Alternatively, you can consider moving the relevant value into the path of the write operation. For example, you could put all friend requests into a separate path in your database (e.g. "friend_requests") and then have the Cloud Function trigger only on that path.
If you only want to trigger your Cloud Function when a new request is written, and not on a delete, you can change your declaration to onCreate:
exports.sendNotification = functions.database.ref('/Notifications/{user_id}/{notification_id}')
.onCreate((snapshot, context) => {
I want to send notification to users when they receive new messages with the below JavaScript code
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.pushNotification = functions.database.ref('/messages/{user_id}/{message_id}').onWrite( (change, context) => {
const user_id = context.params.user_id;
const message_id = context.params.message_id;
console.log('We Have A Notification for :', user_id);
if (!change.after.val()){
return console.log("A Notification Has Been Deleted From The Database: ", message_id)
}
const fromUser = admin.database().ref(`/messages/${user_id}/${message_id}`).once('value');
return fromUser.then(fromUserResult => {
const from_user_id = fromUserResult.val().from;
console.log("You have new notification from : ", from_user_id)
const userQuery = admin.database().ref(`/Users/${from_user_id}/name`).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 payload = {
notification: {
title: "Chat+",
body: `You have a new notification from ${userName}`,
icon: "default",
click_action: "com.mani.eric.quickch_TARGET_NOTIFICATION"
},
};
return admin.messaging().sendToDevice(token_id, payload ).then(Response =>{
console.log('this is the notification')
});
});
});
});
the notification actually gets delivered but on both devices(sender and receiver gets same notification) with the user name of the sender as null.
my question now is, how can i retrieve the sender user name and display the notification only on the receivers device?
You have a type on the path that triggers the function:
functions.database.ref('/messages/{user_id/{message_id}')
Should be:
functions.database.ref('/messages/{user_id}/{message_id}')
So with a closing parenthesis after user_id.
Please read how to create a minimal, complete, verifiable example, as the code you shared is quite a bit more complex than needed to reproduce the problem. For example, your console.log('We Have A Notification for :', user_id); already should show that user_id is null, so the code after that can't work, and is irrelevant to the problem. Reducing the scope of the problem this way increases the chances that you'll find the cause yourself. Or at worst, it reduces the code we need to look at, which increases the chance that somebody will spot the problem and answer.
How to get the push ID?
I have a JSON which looks like this:
purchaseTransaction: {
xxxxxx: {
amount: 1000,
user: yyyyyyy
}
}
and when this purchaseTransaction document is updated, I would like to get the pushID (this is generated by firebase)?
exports.addPurchaseTransactionToUser = functions.database.ref('/purchaseTransaction/{pushId}').onWrite((change, context) => {
const snapshot = change.after;
const val = snapshot.val();
console.log('val',val);
// How to get the pushID?
});
From the documentation on handling event data:
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
console.log('Uppercasing', context.params.pushId, original);
So in your case the push ID will be available as context.params.pushId.
For your code, use context.params.pushId.
I'm trying to add a Function in my Firebase Database, that creates/updates a combined property of two others, whenever they change.
The model is like this:
database/
sessions/
id/
day = "1"
room = "A100"
day_room = "1_A100"
And my function so far:
exports.combineOnDayChange = functions.database
.ref('/sessions/{sessionId}/day')
.onWrite(event => {
if (!event.data.exists()) {
return;
}
const day = event.data.val();
const room = event.data.ref.parent.child('room').data.val();
console.log(`Day: ${day}, Room: ${room}`)
return event.data.ref.parent.child("day_room").set(`${day}_${room}`)
});
exports.combineOnRoomChange = functions.database
.ref('/sessions/{sessionId}/room')
.onWrite(event => {
if (!event.data.exists()) {
return;
}
const room = event.data.val();
const day = event.data.ref.parent.child('day').data.val();
console.log(`Day: ${day}, Room: ${room}`)
return event.data.ref.parent.child("day_room").set(`${day}_${room}`)
});
But it's throwing this error:
TypeError: Cannot read property 'val' of undefined
I'm following the very first example in the Firebase Functions Get Started (Add the makeUppercase() function) and this is what it does in order to reach the entity reference:
event.data.ref.parent
Am I using the child() function wrongly? Any ideas?
When Cloud Functions triggers your code, it passes in a snapshot of the data that triggered the change.
In your code:
exports.combineOnDayChange = functions.database
.ref('/sessions/{sessionId}/day')
This means you event.data has the data for /sessions/{sessionId}/day. It does not contain any data from higher in the tree.
So when you call event.data.ref.parent, this points to a location in the database for which the data hasn't been loaded yet. If you want to load the additional data, you'll have to load it explicitly in your code:
exports.combineOnDayChange = functions.database
.ref('/sessions/{sessionId}/day')
.onWrite(event => {
if (!event.data.exists()) {
return;
}
const day = event.data.val();
const roomRef = event.data.ref.parent.child('room');
return roomRef.once("value").then(function(snapshot) {
const room = snapshot.val();
console.log(`Day: ${day}, Room: ${room}`)
return event.data.ref.parent.child("day_room").set(`${day}_${room}`)
});
});
Alternatively, consider triggering higher in your tree /sessions/{sessionId}. Doing so means that you get all the necessary data in event.data already, and also means you only need a single function:
exports.updateSyntheticSessionProperties = functions.database
.ref('/sessions/{sessionId}')
.onWrite(event => {
if (!event.data.exists()) {
return; // the session was deleted
}
const session = event.data.val();
const day_room = `${session.day}_${session.room}`;
if (day_room !== session.day_room) {
return event.data.ref.parent.child("day_room").set(`${day}_${room}`)
});
});