So, from Firebase functions, I'm listening to this event -
exports.populateVairations_delete =
functions.database.ref('/parentA/parentB/child').onDelete(event =>
{
// I know how to get the previous value for what I'm listening too...
val = event.data.previous.val();
...
}
This function is being invoked also when deleting the parent, which is exactly what I want.
But when deleting a parent, how do I access data from /parentA before it's being deleted?
onDelete triggers are always executed after the delete has occurred. There's no way to prevent a delete from happening with a function. Your onDelete code will be delivered an event that contains only the data that was deleted. The event object itself can't be used to see other parts of the database.
If you need to access other parts of the database inside a database trigger, you can use the Admin SDK to make those queries. There is a lot of official sample code that illustrates how to do this.
With context.resource.name you could get a string containing the data path.
Just use Admin SDK for Firebase, You can have administrator access to the firebase Db.From there, you can do basically anything with the Firebase Db
Related
I am new to firebase and noSQL databases. I've read the docs and watched instructional videos but there is one concept I haven't fully grasped.
Why is it that querying the database requires a listener such as .on("value")?
Since these listeners are triggered whenever there is a change of sorts (nodes created, edited, children created) shouldn't there be a more direct way of getting the data from the db? Such as
ref.orderBy("age"). equalTo(30).get()
A method to just get what's in there at the time he instruction is executed, without having to listen to some sort of event?
In SQL it's not like you have to wait for something to change in your db to make this query work:
SELECT * FROM TABLE WHERE X == Y
PS: I know .once() also exists, but my question is more about: if my db never changed, how would I be able to query it and always get the same query result/snapshot?
You didn't define a platform so I will use this Swift pseudo-code example. Suppose we want to get a list of all users, one time.
let usersRef = my_firebase.child("users")
usersRef.observeSingleEvent(by: .value, with: { snapshot in
//all users are returned to the app in the snapshot
// .value means 'give me everything in the specified node'
// so then we can iterate over the snapshot to get each users data
}
You can call the above code any time to get the list of users and it does not add an observer (listener) to the node. It's a one-shot.
Whereas, if we want to be notified of users that are added
let usersRef = my_firebase.child("users")
usersRef.observe(by: .childAdded, with: { snapshot in
//upon first calling this, each user will be sent to the app in the snapshot
// and after that, any user that's added
}
The above code attaches an observer (listener) to the users node and whenever a user is added, it's provided to the app via the snapshot.
Note the use of .observeSingleEvent vs .observe and .value to get everything in the node vs .childAdded to get a specific node.
Not sure if this is even possible with firebase cloud functions.
Let's assume, I want to trigger a cloud function onCreate on all documents in a specific collection.
After creation, the cloud function should add another document in a different collection.
Passing a value from the manually created document.
Sure, that works!:
export const createAutomaticInvoice = functions.firestore.document('users/{userId}/lessons/{lesson}').onCreate((snap, context) => {
let db = admin.firestore();
let info = snap.ref.data()
db.collection('toAdd').add({
info: info
})
})
But if I create a document within users/{userId}/lessons/ and change the value of info directly afterwards, before the cloud function is triggered, the cloud function takes the old value of info as supposed to the one it was changed to.
Is this expected behaviour? For me it is definetely not as I would assume that it takes the values at runtime.
How can I make my example work as expected?
This is the expected behavior - the function is going to execute as soon as possible after that document is created. The snapshot is always going to contain the contents of the document as it was originally created. It's not going to wait around to see if that document changes at some point in the future, and it's not going to try to query that document in case it might have changed.
If you want to handle updates to a document, you should also be using an onUpdate trigger to know if that happens.
I am using the firebase-tools shell CLI to test Firestore cloud functions.
My functions respond to the onCreate trigger for all documents in a certain collection, by using a wildcard, and then mutate that document with an update call.
firestore
.document(`myCollection/{documentId}`)
.onCreate(event => {
const ref = event.data.ref
return ref.update({ some: "mutation"})
})
In the shell I run something like this, (passing some fake auth data required by my database permissions):
myFunction({some: "data"}, { auth: { variable: { uid: "jj5BpbX2PxU7fQn87z10d4Ks6oA3" } } } )
Hoever this results in an error, because the update tries to mutate a document that is not in the database.
Error: no entity to update
In the documentation about unit testing it is explained how you would create mocks for event.data in order to execute the function without touching the actual database.
However I am trying to invoke a real function which should operate on the database. A mock would not make sense, otherwise this is nothing more then a unit test.
I'm wondering what the strategy should be for invoking a function like this?
By using an existing id of a document the function can execute successfully, but this seems cumbersome because you need look it up in the database for every test, and it might not be there anymore at some point.
I think it would be very helpful if the shell would somehow create a new document from the data you pass in, and run the trigger from that. Would this be possible maybe, or is there another way?
The Cloud Functions emulator can only emulate events that could happen within your project. It doesn't emulate the actual change to the database that would have triggered it.
As you're discovering, when your function depends on that actual change previously occurring, you can run into problems. The fact of the matter is that it's entirely possible that the created document may have already been deleted by the time you're handling the event in the function (imagine a user acts quickly to delete, but the event is delayed for whatever reason).
All that said, perhaps you want to use set() with SetOptions that indicate you want to merge instead of overwrite. Bear in mind that if the document was previously deleted (with good reason) before the event triggered, you'll unconditionally recreate the document, which may not be what the user wanted.
I am in a situation where I need to read the Firebase Realtime Database with a "cron" job.
I analysed and played with this repository, but I can't seem to understand how I can simply retrieve the list of ALL of the users or all of the data in the Realtime Database. The only function of the functions.database.ref is onWrite, which doesn't help me.
I know that the Cloud Functions are event-oriented and they were written to respond to some triggers etc, but as far as I here, this is not impossible and we can apparently access the data.
In their 'delete accounts' example, they used this:
https://www.googleapis.com/identitytoolkit/v3/relyingparty/downloadAccount?fields=users/localId,users/lastLoginAt,nextPageToken&access_token=
And it seems quite undocumented and not at all explained. Why do we have to use the identity toolkit? How could I simply READ data? What I've tried to do until the moment:
Use functions.database.ref('users').once('value', function(snapshot){})and it didn't work
Use their cron job example code (identity toolkit), but I get a highly strange response in the logs from Firebase: Missing required header: Metadata-Flavor and the rest of the tracking below:
You can't use functions.database.ref() for reading data. That's only for specifying the path where you want to changes to the database to trigger your function. To write a cron-like function, you're not going to write a database trigger. You probably want an HTTP trigger instead, as shown in the sample code. You trigger than HTTP function with a third party scheduler as described in the readme.
To read data from the database in that function, you should use the admin SDK, as shown in that code sample. It's initialized here, and appears in lots of other code samples as well:
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
Then you can get a reference to the database:
const ref = admin.database().ref(`/path/to/data`);
ref here is a Reference object. You can read data with that. Be sure to look at the other code samples too - many of them read a database with the admin SDK like this.
Simple question:
Will all obersvers automatically removed when I use goOffline (disconnect to firebase) ?
If not, is there another way to do it, because removeAllOberserves doesn't seem to work or must I keep an array of single handles?
UPDATE
I answer myself.
removeAllOberserves works well, if you call it with the reference you used to set the observer!
Example:
Firebase *userThreadRef;
userThreadRef = [userRef appendPathComponent: ThreadsPath];
[userThreadRef observeEventType: FEventTypeChildAdded withBlock: ^(FDataSnapshot *snapshot) {
...
}];
....
[userThreadRef removeAllObservers];
Do not use a new reference like this:
Firebase *newUserThreadRef = [userRef appendPathComponent: ThreadsPath];
[newUserThreadRef removeAllObservers];
Will all observers automatically removed when I use goOffline (disconnect to firebase) ?
No. Calling goOffline() will not automatically remove observers/listeners.
is there another way to do it, because removeAllOberserves doesn't seem to work or must I keep an array of single handles?
It's hard to say without seeing your code, but likely your expectations are just wrong.
You'll need to call removeAllObservers() on each reference. The All in the method name is for the fact that it removes the observers for all event types, not for all references.