Get the value of a child in firebase using nodejs TypeError - javascript

I was wondering if anyone could tell me what I am doing wrong. I am trying to write a firebase function to send a notification. That part works, but I am trying to get the value of a particular child. Yet I keep getting the following error message: "TypeError: Cannot read property 'child' of undefined"
This is my server-side code:
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/notifications/{receiver_user_id}/{notification_key}').onWrite((data, context)=>{
const receiver_user_id = context.params.receiver_user_id;
const notification_key = context.params.notification_key;
console.log('We have a notification to send to : ', receiver_user_id);
// Grab the current value of what was written to the Realtime Database.
const original = data.after.val();
console.log('Uppercasing', context.params.notification_key, original);
const sender_fullname = snapshot.child('notifying_user_fullname').val();
console.log('fullname value: ', sender_fullname);
if(!data.after.val()){
console.log('A notification has been deleted: ', notification_key);
return null;
}
const DeviceToken = admin.database().ref(`/tokens/${receiver_user_id}/device_token`).once('value');
return DeviceToken.then(result=>{
const token_id = result.val();
const payload = {
notification: {
title: sender_fullname,
body: "You have a new message!",
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response=>{
console.log('Message has been sent');
});
});
});
The snapshot prints out just fine. And when I remove the snapshot and sender_fullname constants the notification is delivered just fine. But, like I said, I keep getting a TypeError message when the code is executed this way. Does anyone know how to get the value of a child at a certain location in realtime database. If it helps, this is what the data snapshot looks like:
If anyone can help that would be great. Thank you in advance

You have a variable snapshot that was never defined, at least not that you're showing here. Here's where you're using it:
const sender_fullname = snapshot.child('notifying_user_fullname').val();
Trying to call method on something that's not defined would give you that error message.

Related

How to fix firebase database initialised multiple times due to React SSR initialised database and cloud function firebase initialised database?

I have updated the question as found the root cause of the issue.
As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?
ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'?
So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why?
If I look at the console I get results till console.log('deviceToken', deviceToken);
so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
.onCreate(async(snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const adminApp = admin.initializeApp(appOptions, 'app');
const { message, senderId, receiverUid } = snap.val();
console.log(message, senderId, receiverUid);
console.log('------------------------');
const deleteApp = () => adminApp.delete().catch(() => null);
try {
const db = adminApp.database();
const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
console.log('reciverUserRef', reciverUserRef);
const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
const deviceToken = await deviceTokenSnapshot.val();
console.log('deviceToken', deviceToken);
const payload = {
notification: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
}
};
const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
console.log('Successfully sent message: ', JSON.stringify(messageDone));
return deleteApp().then(() => res);
} catch (err) {
console.log('error', err);
return deleteApp().then(() => Promise.reject(err));
}
});
Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK?
So switched to
const payload = {
data: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
},
token: deviceToken
};
const messageDone = await admin.messaging().send(payload);
Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.
EDIT: Finally got the function working.
My index.js is exporting to functions, follwoing
exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);
exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance.
When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?
You can initialise db outside export function.
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code
Update:
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
try {
const defaultApp = adminApp.name
if(defaultApp) {
const adminApp1 = admin.initializeApp(apps);
}else {
const adminApp1 = admin.initializeApp(options, apps);
}
}catch(err) {
console.error(err);
}
}
Modify this snippet as per your need and try it out
It abstracts initialize of app in another function. Just call this function at appropriate place in your code.

How to fix Cloud Function error admin.database.ref is not a function at exports

I'm currently trying to modify my Cloud Functions and move in under https.onRequest so that i can call use it to schedule a cron job. How it i'm getting the following error in the logs.
TypeError: admin.database.ref is not a function
at exports.scheduleSendNotificationMessageJob.functions.https.onRequest (/user_code/index.js:30:20)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/providers/https.js:57:9)
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
admin.database.ref('/notifications/{studentId}/notifications/{notificationCode}')
.onCreate((dataSnapshot, context) => {
const dbPath = '/notifications/' + context.params.pHumanId + '/fcmCode';
const promise = admin.database().ref(dbPath).once('value').then(function(tokenSnapshot) {
const theToken = tokenSnapshot.val();
res.status(200).send(theToken);
const notificationCode = context.params.pNotificationCode;
const messageData = {notificationCode: notificationCode};
const theMessage = { data: messageData,
notification: { title: 'You have a new job reminder' }
};
const options = { contentAvailable: true,
collapseKey: notificationCode };
const notificationPath = '/notifications/' + context.params.pHumanId + '/notifications/' + notificationCode;
admin.database().ref(notificationPath).remove();
return admin.messaging().sendToDevice(theToken, theMessage, options);
});
return null;
});
});
You cannot use the definition of an onCreate() Realtime Database trigger within the definition of an HTTP Cloud Function.
If you switch to an HTTP Cloud Function "so that (you) can call use it to schedule a cron job" it means the trigger will be the call to the HTTP Cloud Function. In other words you will not be anymore able to trigger an action (or the Cloud Function) when new data is created in the Realtime Database.
What you can very well do is to read the data of the Realtime Database, as follows, for example (simplified scenario of sending a notification):
exports.scheduleSendNotificationMessageJob = functions.https.onRequest((req, res) => {
//get the desired values from the request
const studentId = req.body.studentId;
const notificationCode = req.body.notificationCode;
//Read data with the once() method
admin.database.ref('/notifications/' + studentId + '/notifications/' + notificationCode)
.once('value')
.then(snapshot => {
//Here just an example on how you would get the desired values
//for your notification
const theToken = snapshot.val();
const theMessage = ....
//......
// return the promise returned by the sendToDevice() asynchronous task
return admin.messaging().sendToDevice(theToken, theMessage, options)
})
.then(() => {
//And then send back the result (see video referred to below)
res.send("{ result : 'message sent'}") ;
})
.catch(err => {
//........
});
});
You may watch the following official Firebase video about HTTP Cloud Functions: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3. It shows how to read data from Firestore but the concept of reading and sending back the response (or an error) is the same for the Realtime Database. Together with the 2 other videos of the series (https://firebase.google.com/docs/functions/video-series/?authuser=0), it also explains how it is important to correctly chain promises and to indicate to the platform that the work of the Cloud Function is finished.
For me, this error happened when writing admin.database instead of admin.database().

Resolving a "TypeError: Cannot read property 'data' of undefined" in Cloud Functions

Sorry if this seems like a really basic question, the concept of cloud functions is extremely new to me and i'm still highly in the learning process.
However, whilst trying to execute this cloud function i get the following error
TypeError: Cannot read property 'data' of undefined
Full log can be seen here
For reference as well, I didnt make this function, im just trying to get it working, i used this video.
The actual cloud function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const firestore = admin.firestore();
const settings = { timestampInSnapshots: true };
firestore.settings(settings);
const stripe = require('stripe')(functions.config().stripe.token);
exports.addStripeSource =
functions.firestore.document('cards/{userid}/tokens/{tokenid}')
.onCreate(async (tokenSnap, context) => {
var customer;
const data = tokenSnap.after.data();
if (data === null) {
return null
}
const token = data.tokenId;
const snapchat = await
firestore.collection('cards').doc(context.params.userId).get();
const customerId = snapshot.data().custId;
const customerEmail = snpashot.data().email;
if (customerId === 'new') {
customer = await stripe.customers.create({
email: customerEmail,
source: token
});
firestore.collection('cards').doc(context.params.userId).update({
custId: customer.id
});
}
else {
customer = await stripe.customers.retrieve(customerId)
}
const customerSource = customer.sources.data[0];
return firestore.collection('cards').doc(context.params.userId).collection('sources').doc(customerSource.card.fingerprint).set(customersource, { merge: true });})
The dart code im using for writing a payment service:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class PaymentService {
addCard(token) {
FirebaseAuth.instance.currentUser().then((user) {
print("Found User");
Firestore.instance
.collection('cards')
.document(user.uid)
.collection('tokens')
.add({'tokenId': token}).then((val) {
print('saved');
});
});
}
}
And finally, what executes when i push the button:
StripeSource.addSource().then((String token) {
print("Stripe!");
PaymentService().addCard(token);
});
As you can see the code is clearly being triggered, but i guess there is some sort of error with the data var, JavaScript is brand new to me so im sure its some sort of very dumb syntax issue.
From the log image attached the error is context is not defined
functions.firestore.document('cards/{userid}/tokens/{tokenid}')
.onCreate(async (tokenSnap, conetxt) => {
In the above function, you have passed parameter as conetxt and later in the function context is used, because of which it is giving undefined error.
Change the parameter name conetxt to context.
As your provided log output explains : you need to define a reference for your firestore.document function :
functions.firestore.document('cards/{userid}/tokens/{tokenid}')
modify it to :
functions.firestore.documentReference(){
}

Firebase functions trying to send a notification fails

I'm trying to do the following thing. I have my database like this
clients
|___clientnumber: 4
|___name1
|_device_token: kJ-aguwn7sSHsjKSL....
|_notificationtrigger: 8
What I want to do is that when a client puts a number in my app inside his node "name1" it will trigger a notification when clientnumber is 8 , so, client number will be 4, then 5, then 6 and so on till 8, when it hits 8 I wrote this function in order to send the user a notification telling him that the clientnumber 8 is ready.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendClientesNotification = functions.database.ref('/clients/clientnumber')
.onWrite((snapshot, context) => {
const newClient = snapshot.after.val();
const oldClient = snapshot.before.val();
console.log('New client: '+newClient);
console.log('Old client: '+oldClient);
// get the user device token
const getDeviceTokensPromise = admin.database()
.ref(`/clients/name1/device_token`).once('value');
const getClientNotificationTrigger = admin.database()
.ref(`/clients/name1/notificationtrigger`).once('value');
//I use a promise to get the values above and return, and then I create the notification
return Promise.all([getDeviceTokensPromise, getClientNotificationTrigger]).then(results => {
const devicetoken = results[0].val();
const clientNumberTrigger = results[1].val();
console.log('New notification for '+devicetoken+' for the number '+clientNumberTrigger);
if(clientNumberTrigger = newClient){
const payload = {
notification: {
title: "Alert",
body: "alert triggered",
icon: "default",
sound:"default",
vibrate:"true"
}
};
}
return admin.messaging().sendToDevice(devicetoken, payload);
});
});
Now, I will explain what I'm doing right here.
First I'm pointing to the reference clientnumber and successfully getting the value before and after it changes here
const newClient = snapshot.after.val();
const oldClient = snapshot.before.val();
console.log('New client: '+newClient);
console.log('Turno anterior: '+oldClient);
then I'm querying the values inside my client in order to have device_token and then notificationtrigger and using a promise to get the two values, comparing if they are identically and then returning a notification with the current user device_token
The errors I'm facing are this two
The first one I think is from this line
const clientNumberTrigger = results[1].val();
and the second one I don't know why is happening, I think that I need to add a conditional expression before my if statment
I'm starting up with firebase functions and slowly learning some javascript in the process.
as the error says: you are trying to compare two values using a single equals symbol (=) while you should use three instead:
switch this:
if(clientNumberTrigger = newClient) { ... }
to this:
if(clientNumberTrigger === newClient) { ... }
and at least this error should be gone.

Error: Registration token(s) provided to sendToDevice()

Now im working for my final project. I try to send notification using firebase cloud function when its trigger the onUpdate but i got an error. I have follow tutorial on youtube and website but i dont get it. By the way, im new to firebase. below Here is my index.js code :-
const functions = require('firebase-functions');
//Firebase function and handling notification logic
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.pushNotification = functions.database.ref('/Sensor').onWrite(( change,context) => {
const sensor = change.after.val();
const payload = {
notification: {
Title: "Alert",
Body: "Open pipe detect !",
icon: "default"
}
};
return admin.messaging().sendToDevice(sensor.token, payload)
.then((response)=> {
return console.log("Successfully sent message:", response);
});
});
the project structure is like this:
**water-system**
+--Sensor
+---Pipe
+---pipeName
+---solenoid
+---status // trigger on this update
+---User
+---Id1
+---email
+---name
+---token // token store by this user
+---Id2
+---Id3
+---token // also store token
So when the child node of Sensor have been update it will send notification to User who have store the token(user id1 and id3). Glad if anyone could help me to solve this problem
Try storing the tokens in this format:
"tokens" : {
"cXyVF6oUGuo:APA91bHTSUPy31JjMVTYK" : true,
"deL50wnXUZ0:APA91bGAF-kWMNxyP6LGH" : true,
"dknxCjdSQ1M:APA91bGFkKeQxB8KPHz4o" : true,
"eZunoQspodk:APA91bGzG4J302zS7sfUW" : true
}
Whenever you want to write a new token just do a set:
firebase.app().database().ref(`/user/${uid}/tokens/${token}`).set(true);
And to create an array for sendToDevice:
const tokensList = Object.keys(tokens.val());
return admin.messaging().sendToDevice(tokensList, payload);

Categories

Resources