Firebase Functions - Function not being called - firebase

I have created the following functions:
It has been deployed and is there on the firebase hosting, but it just doesn't ever get called (usage is 0)...
It should be called when a user document is changed and then update a field to null if it wasn't already null.
Can anyone see why this is not running?
exports.deleteField = functions.database.ref('/Users/{userID}')
.onUpdate((change, context) => {
const overrideTag = change.after.data().overrideTag
if (overrideTag !== null) {
const db = admin.firestore()
db.collection('Users').doc(userID).set({ overrideTag: null })
}
})
Kind Regards,
Josh

Your function is configured to trigger on changes to a node called "Users" in Realtime Database. Realtime Database doesn't have "documents". However, Firestore does have documents. If you meant to trigger when a document is changed in Firestore, you will have to write a Firestore trigger instead. It will use functions.firestore instead of functions.database.

Related

Unable to fetch data from Firebase Firestore in react native

I am trying to GET data from Firestore but I cannot see data coming. I am building a react-native app. I am using react-native-firebase.
I also tried Transactions method from react-native-firebase documentation. But nothing is working.
Here' my code:
firebase.firestore().collection('users').doc(uid)
.get()
.then((doc)=>{
console.log(doc)
console.log(doc.data().shoppinglist)
})
.catch(e => console.log(e));
Console logging .get() gives me a Promise.
I am getting the Promise like this:
Promise {_40: 0, _65: 0, _55: null, _72: null}
_40: 0
_55: null
_65: 0
_72: null
But .then() doesn't executes as the two console.log() ain't logging anything.
Please help me out here. Quite new with Firebase.
After some digging in Firebase Documentation, I found out a solution.
Collection references and document references are two distinct types of references and let you perform different operations. For example, you could use a collection reference for querying the documents in the collection, and you could use a document reference to read or write an individual document.
Therefore, Replacing firebase.firestore().collection('users').doc(uid) with firebase.firestore().doc(`users/${uid}`) solved my problem.
For dummies in firebase like me, if you want a custom document id, you have to specify it when writing data to firestore
import firestore from '#react-native-firebase/firestore';
firestore()
.collection('Users')
.doc('ABC')
.set({
name: 'Ada Lovelace',
age: 30,
})
.then(() => {
console.log('User added!');
});
Then you can get it by
import firestore from '#react-native-firebase/firestore';
const user = await firestore().collection('Users').doc('ABC').get();
For reference: https://rnfirebase.io/firestore/usage#writing-data

How to add a document to a collection in cloud firestore

I have a collection called 'users'. I'm trying to add a user to the collection after Google authentication but I keep getting the following error:
FirebaseError: [code=invalid-argument]: Invalid document reference. Document references must have an even number of segments, but users has 1.
Here is the code
this.googlePlus.login({
'scopes': '',
'webClientId': environment.googleWebClientId,
'offline': true,
})
.then(user => {
// save user data on the native storage
const userRef: AngularFirestoreCollection<User> = this.afs.collection<User>(`users/`);
const data: User = {
email: user.email,
displayName: user.displayName,
uid: user.uid
};
userRef.set(data)
.then(() => {
this.router.navigate(['/home']);
Google+ is being discontinued so you should look at Firebase Authentication, or GCP's new Cloud Identity Platform.
In the case of Firebase Authentication, you must listen to the .onAuthStateChanged observer. Once it fires off your user object, you then take that and write a new user document to a users collection in Firestore. Best practise is to use the uid of the firebase.auth().currentUser.uid as the user document ID in your users collection.
Your userRef refers to a collection, and the type of object is called a CollectionReference. You're attempting to call set() on it with some object that should become a new document in that collection. But that's not the way it works. Instead, it looks like you want to call add() to add a new document with a new random ID.
If you somehow already know the ID of the new user document, you should build a DocumentReference with that id, then use set() on that DocumentReference to create the document.

minimize time operation in firebase/firestore

I build react native app with firebase & firestore.
what I'm looking to do is, when user open app, to insert/update his status to 'online' (kind of presence system), when user close app, his status 'offline'.
I did it with firebase.database.onDisconnect(), it works fine.
this is the function
async signupAnonymous() {
const user = await firebase.auth().signInAnonymouslyAndRetrieveData();
this.uid = firebase.auth().currentUser.uid
this.userStatusDatabaseRef = firebase.database().ref(`UserStatus/${this.uid}`);
this.userStatusFirestoreRef = firebase.firestore().doc(`UserStatus/${this.uid}`);
firebase.database().ref('.info/connected').on('value', async connected => {
if (connected.val() === false) {
// this.userStatusFirestoreRef.set({ state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp()},{merge:true});
return;
}
await firebase.database().ref(`UserStatus/${this.uid}`).onDisconnect().set({ state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
this.userStatusDatabaseRef.set({ state: 'online', last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
// this.userStatusFirestoreRef.set({ state: 'online',last_changed: firebase.firestore.FieldValue.serverTimestamp() },{merge:true});
});
}
after that, I did trigger to insert data into firestore(because I want to work with firestore), this is the function(works fine, BUT it takes 3-4 sec)
module.exports.onUserStatusChanged = functions.database
.ref('/UserStatus/{uid}').onUpdate((change,context) => {
const eventStatus = change.after.val();
const userStatusFirestoreRef = firestore.doc(`UserStatus/${context.params.uid}`);
return change.after.ref.once("value").then((statusSnapshot) => {
return statusSnapshot.val();
}).then((status) => {
console.log(status, eventStatus);
if (status.last_changed > eventStatus.last_changed) return status;
eventStatus.last_changed = new Date(eventStatus.last_changed);
//return userStatusFirestoreRef.set(eventStatus);
return userStatusFirestoreRef.set(eventStatus,{merge:true});
});
});
then after that, I want to calculate the online users in app, so I did trigger when write new data to node of firestore so it calculate the size of online users by query.(it works fine but takes 4-7 sec)
module.exports.countOnlineUsers = functions.firestore.document('/UserStatus/{uid}').onWrite((change,context) => {
console.log('userStatus')
const userOnlineCounterRef = firestore.doc('Counters/onlineUsersCounter');
const docRef = firestore.collection('UserStatus').where('state','==','online').get().then(e=>{
let count = e.size;
console.log('count',count)
return userOnlineCounterRef.update({count})
})
return Promise.resolve({success:'added'})
})
then into my react native app
I get the count of online users
this.unsubscribe = firebase.firestore().doc(`Counters/onlineUsersCounter`).onSnapshot(doc=>{
console.log('count',doc.data().count)
})
All the operations takes about 12 sec. it's too much for me, it's online app
my firebase structure
what I'm doing wrong? maybe there is unnecessary function or something?
My final goals:
minimize time operation.
get online users count (with listener-each
change, it will update in app)
update user status.
if there are other way to do that, I would love to know.
Cloud Functions go into a 'cold start' mode, where they take some time to boot up. This is the only reason I can think of that it would take that long. Stack Overflow: Firebase Cloud Functions Is Very Slow
But your cloud function only needs to write to Firestore on log out to
catch the case where your user closes the app. You can write to it directly on log in from your client
with auth().onAuthStateChange().
You could also just always read who is logged in or out directly from the
realtime database and use Firestore for the rest of your data.
You can rearrange your data so that instead of a 'UserStatus' collection you have an 'OnlineUsers' collection containing only online users, kept in sync by deleting the documents on log out. Then it won't take a query operation to get them. The query's impact on your performance is likely minimal, but this would perform better with a large number of users.
The documentation also has a guide that may be useful: Firebase Docs: Build Presence in Cloud Firestore

Firebase Functions triggered by database change in my simple case

In my firebase database, I have a "users" node.
I use Firebase Functions for backend functions. My javascript code (node.js) running in Functions needs to do something when a new user is added to database, here is the backend function code:
// required modules
const functions = require('firebase-functions');
const admin = require('firebase-admin');
// my backend function
exports.onUserCreated = functions.auth.user().onCreate(event => {
// Do something when a new user is created
}
I successfully deployed my backend function to firebase Functions.
Then, I manually added a user under users node of firebase database. Then, I go to Firebase Functions, I see the onUserCreated Function listed there in console, but the number of executions is 0.
What could be the reason why my function is not triggered when I manually added a user in database?
Your function is an authentication trigger, not a database trigger:
functions.auth.user().onCreate(...)
It will trigger when a new user is added via Firebase Authentication, not when there is a change in your database.
If you want to write a database trigger, follow the instructions here instead.
What's happening is that you've created a function that is triggered by Firebase Auth events. Which means that it will only be triggered when a new User signs up to use your application. Not when he writes to the database.
If you want it to be triggered when you create a new user under the "users" node, you should use a Realtime Database trigger:
exports.onUserCreated = functions.database.ref('/user/{userId}')
.onWrite(event => {
// Do something when a new user is created
}
EDIT: Accessing the user's email stored in the database:
exports.onUserCreated = functions.database.ref('/user/{userId}')
.onWrite(event => {
// Do something when a new user is created
var email = event.data.val().email;
}

Access a cloud firestore document within a cloud function

Within my Google Firebase Firstore database I would like to gather aggregative data such as how many documents a collection has. Since Firestore does not provide aggregative queries I'm attempting to write a cloud function that will increment a field every time a document is added to the database which will contain the number of documents a collection has.
The problem I'm having is I cannot for the life of me figure out how to grab documents from Firestore within a cloud function using nodejs.
Here's what I'm doing:
At the top of my index.js file I configure the admin SDK and what have you like this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
Then for my cloud function I do this:
exports.createPost = functions.firestore
.document('posts/{post_id}')
.onCreate(event => {
// Get the post object
var post = event.data.data();
var senderID = post["sender_id"]; // This is not null
// Access to user's document and the main ledger document
const userDocRef = admin.database().ref('/users').orderByChild("user_id").equalTo(senderID).once('value')
const ledgerDocRef = admin.database().ref('/posts/ledger').once('value');
return Promise.all([userDocRef, ledgerDocRef]).then(snapshot => {
const user = snapshot[0].val();
const ledger = snapshot[1].val();
console.log("user => "+user); // Logs: user => null
console.log("ledger => "+ledger); // Logs: ledger => null
const userPostCount = user["user_name"];
const globalPostCount = ledger["global_post_count"] + 1;
const userUpdate = user.update({"post_count" : userPostCount});
const ledgerUpdate = ledger.update({"global_post_count" : globalPostCount});
return Promise.all([userUpdate, ledgerUpdate]);
});
});
I end up with the error:
TypeError: Cannot read property 'global_post_count' of null
at Promise.all.then.snapshot
Which I figure means something is wrong with my query but I don't know what. Both the users and posts are root level collections.
Im also getting a warning that says:
Billing account not configured. External network is not accessible and
quotas are severely limited.
From what I've read online, I don't think that effects it but I thought it was worth noting.
Please help.
Looks like you've written a Firestore trigger, but are then reaching into Realtime Database for queries:
const userDocRef = admin.database().ref('/users').orderByChild("user_id").equalTo(senderID).once('value')
const ledgerDocRef = admin.database().ref('/posts/ledger').once('value');
If your RTDB is empty, these queries will also end up empty.
To query Firestore, you need to be using admin.firestore() instead of admin.database(). Firestore has a mostly different API (via the Cloud SDK I just linked) than RTDB, but they are similar in some ways.

Resources