PageInfo in Firestore not usable - firebase

I would like to use pagination in Firestore so I can save a query state and automatically allow users to submit a page token to start a query again. The challenge I am seeing with this is that Firestore does not offer a query token or page token to resume iteration. However, after looking through the docs for GoLang, it looks like there is an exported PageInfo() method that returns a token I am interested in and an unexported fetch method.
Is there a method to use the exported PageInfo() values to allow me to fetch a new set of documents using that existing token?

To use pagination in Firestore Database you can use query cursors with limit() method as mentioned in this document. You can go through this youtube link to know more about it.
You may also consider using pageSize & pageToken query parameters and nextPageToken field with Firestore REST API as mentioned in this document to achieve pagination. There is a similar StackoverFlow thread which may help you.

Related

Is there an equivalent of a for each function on a map in Firestore Security Rules?

Users of our app can add other users as friends. Each friends list is implemented as a map where the key is the id of the users and where the value is some data related to the user. The following map is an example of how it looks like :
{'id_1' : {displayName: 'John Doe', color: 3412445}, 'id_2' : {displayName: 'Bob Alison', color: 84655467}}
We want to add rules to make data validation on the fields in the values of the map. The displayName needs to be a string and the color needs to be a number.
In the firestore rules we can check the new added friends by doing like so :
let addedFriendsKeys = newFriendsList.diff(oldFriendsList).addedKeys()
But is there a way to retrieve the values related to that list of keys?
something like :
let newFriendsList.getAll(addedFriendsKeys).forEach((p0)=>isDataValid(p0))
There are no looping operations in Firestore security rules. You will have to enumerate the keys that you want to validate.
The following answer is not mine (Credits to
Frank van Puffelen). It's coming from this post but answers perfectly to the question :
If you're asking about doing this in server-side security rules, then you've precisely hit the nail on the head: there is no ability to loop in Firebase's server-side security rules. See the reference documentation for the operations that can be performed on a List in a document. This limits what can be accomplished in security rules, and as far as I can see none of the use-cases you mention can be implemented with just security rules.
The simplest approach I can think of is by using Cloud Functions to implement the logic. You could either have the Cloud Function inspect the documents in place in the current collection, or you can have the client write to a different collection (of "pending" documents), have the Cloud Function validate the document, and move it to the actual collection.

Firestore security rules: get() use in hasAny() list method

I was wondering if this security rule would be possible:
function productForUser() {
return resource.data.products.hasAny(get(/databases/$(database)/documents/Users/$(request.auth.uid)).data.products);
}
When I try to test it in the testing plaground on the Firebase website, it is sucessful. However, when I try to run it with Javascript, with this query, I get the read denied, with "missing or insufficient permissions":
query.where("products", "array-contains", productId);
I can confirm that the user has the array of products, containg the specific product that is being looked up in the query.
Thanks in advance.
Your rule works in the console simulator because the simulator only supports "get" type requests for a single document. It doesn't work for queries because security rules are not filters. The rule will not be evaluated for each and every document in the collection, as that would not scale well at all for very large collections. To specify conditions for queries, you will need to provide exact values to check from the client - you will not be able to use a get() to find other values.
If you want to test queries before publishing your rules, you should not be using the simulator, and instead use the local emulator to test code that actually performs a query.

In Firebase Firestore, is there any way to pass info to the Security Rules which is not part of the path?

I would like to send some info to Firestore database (Firebase), preferably in key-value pairs (but not necessarily), so that it can use it to evaluate access in their rules (both when reading and writing).
However, I don't want this info to be part of the path.
For example, suppose I had some passParameters method:
DocumentReference docRef =
db.collection("cities")
.document("SF")
.passParameters("abc", 123);
Then I could access this info when writing rules, like so:
service cloud.firestore {
match /databases/{database}/documents/cities/SF/ {
allow read, write: if request.parameters.abc == 123;
}
}
Please note, the above is just an example. Real-life uses cases are more complicated. In other words, don't pay too much attention to the example itself, but answer the more generic question: Is there any way to pass info to the Security Rules which is not part of the path?
You can send such parameters using custom tokens. Include those values as claims in the custom token, and use that token in your client when sending request to firestore (or signin).
This link explains how to-
1) create custom tokens, 2) include custom claims in those tokens, and 3) access those claims in the security rules.
You can have a cloud function to generate that custom token with custom claims for a specific user.
If the information you want to pass to firebase as parameter changes frequently, then this is going to be a cloud function call everytime you want to change the parameter value you are passing- so a bit costly. But if parameter tend to change less frequently (like- some role or special privilege that the user have), then this solution should work perfect and that's one of the primary benefits of custom token.
Even though it is not as simple as your example expectation snippet, still this I believe is one way to achieve what you want.
That's not supported. It wouldn't be a very "secure" security rule if the client could just specify whatever security parameters it wants with a query. That's really no different than allowing a client to pass a plaintext password that gives someone access to something. I would expect that sort of information to be discovered by an attacker.

Removing firebase-generated id of user saved in database

When I save a user in my firebase db I do it via this path:
/allUsers/serviceUsers/${usersUID}
However, firebase adds another apparent UID to this UID.
Is there anyway to prevent this or tell firebase to not do it?
The first ID after serviceUsers is the ID I care about. The second one is generated by firebase and is making working with these objects in the app more painful. I would like that the user object be directly nested under the ID after serviceUsers. How can I achieve this?
Try to call :
firebase.database().ref("/allUsers/serviceUsers/${uid}").setValue(user)
Instead of :
firebase.database().ref("/allUsers/serviceUsers/${uid}").pus‌​h(user)
The push function generate an automatic id.
Why not just use a regular POST to
/allUsers/serviceUsers/
and return the ID? The path parameter should only be used on an update, read, or delete, if you are adhering to REST.
https://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_Web_services

Meteor drop collection after every query

I am writing an application which communicates with an API and stores the response in a Meteor Collection so I can have the power of mongo to sort/filter.
I would like to clear the collection for every new result set. But a Meteor Collection is persistent.
What is the preferred way of clearing the collection? I know you can drop the meteor collection, but is that the preferred method?
Help appreciated. Thank you!
I would go about creating a local mongo collection which will be available on client side only. To create a client-side collection, just don't give it a name argument.
//This collection is client-only, and will not be sync with server
myCollection = new Mongo.Collection();
//To be more explicit, you can use `null` for the name:
myCollection = new Mongo.Collection(null);
Once you are done using the data empty the collection
myCollection.remove({});
myCollection.remove({}) is the syntax for removing all documents from a collection. This will only work on the server unless the collection is a client-side collection as per #Nakib's example. Otherwise documents can only be deleted by _id on the client side. Normally your allow/deny rules should block any attempt to delete anything on the client as it provides a great attack vector.
Not completely familiar with the Meteor best practice but if you were going to clear out an array in javascript the best practice would be to run the following.
myArrary.length = 0;
For more information I recommend this blog post by David Walsh where he details the reasoning behind zeroing out an array as follows:
Setting the length equal to zero empties the existing array, not
creating another array! This helps you to avoid pointer issues with
arrays as well.

Resources