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

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(){
}

Related

Get the value of a child in firebase using nodejs TypeError

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.

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.

Cannot read property 'val' of undefined

I try to learn how to send a notifications using a firebase functions. So I'm using this example code:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendAdminNotification = functions.database.ref('/message').onWrite(event => {
const news = event.data.val();
const payload = {notification: {
title: 'New news',
body: `${news.title}`
}
};
return admin.messaging().sendToTopic("News",payload)
.then(function(response){
return console.log('Notification sent successfully:',response);
})
.catch(function(error){
console.log('Notification sent failed:',error);
});
});
but it gives me an error:
Cannot read property 'val' of undefined
I've been looking for a solution, tried this, but still no-go...
Any suggestions would be greatly appreciated.
I think you are not using the syntax as specified in the documentation.
Refer this documentation.
Anyways, as I see in the documentation linked above, your code should be :
exports.sendAdminNotification = functions.database.ref('/message')
.onWrite((change, context) => {
console.log(change);
// Do something with change or context
});

How do I call the function with a parameter I set up in firebase functions

I've deployed this code to my firebase functions project:
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp()
export const getEmail = functions.https.onRequest((request, response) => {
var from = request.body.sender;
admin.auth().getUserByEmail(from)
.then(snapshot => {
const data = snapshot.toJSON()
response.send(data)
})
.catch(error => {
//Handle error
console.log(error)
response.status(500).send(error)
})
})
Which takes in a email parameter that it gets from the user's input on my app. My app's code looks like this:
Functions.functions().httpsCallable("https://us-central1-projectname.cloudfunctions.net/getEmail").call(email) { (result, error) in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain {
//email isnt taken
let code = FunctionsErrorCode(rawValue: error.code)
let message = error.localizedDescription
let details = error.userInfo[FunctionsErrorDetailsKey]
print(code, message, details)
}
// ...
}
if let text = (result?.data as? [String: Any])?["text"] as? String {
// email taken
}
}
When I run the app and when that function is called, it seems to do nothing, no error message is shown and no data has been sent back. What am I missing?
Update: I went to the logs and nothing has happened in there as if the function was never called.
You are actually mixing up HTTP Cloud Functions and Callable Cloud Functions:
You Cloud Function code corresponds to an HTTP one but the code in your front-end seems to call a Callable one.
You should adapt one or the other, most probably adapt your Cloud Function to a Callable one, along the following lines:
exports.getEmail = functions.https.onCall((data, context) => {
const from = data.sender;
return admin.auth().getUserByEmail(from)
.then(userRecord => {
const userData = userRecord.toJSON();
return { userData: userData };
})
});
Have a look at the doc for more details, in particular how to handle errors. The doc is quite detailed and very clear.

firebase cloud functions Cannot read property 'ref' of undefined

when i want to update Cloud Firestore from Realtime Database i deployed bellow code and i get error.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const firestore = functions.firestore;
exports.onUserStatusChange = functions.database
.ref('/status/{userId}')
.onUpdate(event => {
var db = admin.firestore();
//const usersRef = firestore.document('/users/' + event.params.userId);
const usersRef = db.collection("users");
var snapShot = event.data;
return event.data.ref.once('value')
.then(statusSnap => snapShot.val())
.then(status => {
if (status === 'offline'){
usersRef
.doc(event.params.userId)
.set({
online: false,
last_active: Date.now()
}, {merge: true});
}
})
});
TypeError: Cannot read property 'ref' of undefined
at exports.onUserStatusChange.functions.database.ref.onUpdate.event (/user_code/index.js:18:20)
at cloudFunctionNewSignature (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:105:23)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:135:20)
at /var/tmp/worker/worker.js:733:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
It looks like you got the code for a beta version of Cloud Functions for Firebase. The syntax has changed in the 1.0 version. From the documentation on upgrading your Cloud Functions:
or onWrite and onUpdate events, the data parameter has before and after fields. Each of these is a DataSnapshot with the same methods available in admin.database.DataSnapshot. For example:
Before (<= v0.9.1)
exports.dbWrite = functions.database.ref('/path').onWrite((event) => {
const beforeData = event.data.previous.val(); // data before the write
const afterData = event.data.val(); // data after the write
});
Now (>= v1.0.0)
exports.dbWrite = functions.database.ref('/path').onWrite((change, context) => {
const beforeData = change.before.val(); // data before the write
const afterData = change.after.val(); // data after the write
});
So you will want to use:
.onUpdate((change, context) => { to declare the funtcion, instead of .onUpdate(event => {
use change.after to refer to the data, instead of event.data
use change.after.ref.once('value'), instead of event.data.ref.once('value')
Since it seems that this code is mostly copied from somewhere, I'd recommend getting an updated version from there. For example, the Firestore documentation that your code is likely based on, contains an up-to-date example here: https://firebase.google.com/docs/firestore/solutions/presence#updating_globally
Try to change below code, as firebase functions on events have two properties any more. So, ref position is:
.onUpdate((event,context) => {
....
return event.ref.once('value')
...
event.data does not exist anymore, instead event.val() for more info and event has properties like

Categories

Resources