Unable to set data in firestore subcollections - android

While uploading data on cloud firestore
lateinit var db: DocumentReference
db = FirebaseFirestore.getInstance().document("users/${mAuth.uid}")
val items=HashMap<String,Any>()
items["w"] = "t"
db.set(items).addOnSuccessListener {
Toast.makeText(this,"Data updated successfully", Toast.LENGTH_LONG).show()
}.addOnFailureListener{
Toast.makeText(this,"Data upload failed",Toast.LENGTH_LONG).show()
}
works, but
lateinit var db: DocumentReference
db = FirebaseFirestore.getInstance().document("users/${mAuth.uid}/othercollection/otherdocument")
val items=HashMap<String,Any>()
items["w"] = "t"
db.set(items).addOnSuccessListener {
Toast.makeText(this,"Data updated successfully", Toast.LENGTH_LONG).show()
}.addOnFailureListener{
Toast.makeText(this,"Data upload failed",Toast.LENGTH_LONG).show()
}
fails. ie. it prints "Data upload failed".
other method of declaring document path also fails, .collection().document().collection().document()..... also fails

In such cases, the most common operation that you need to do is to log the message that comes from addOnFailureListener which tells you what is all about. Most likely is a problem related to insufficient permissions. To solve this, please set the security rules that correspond with the queries that you are performing.

The error was insufficient permissions. I was using security rules from my old project which were not defined for nested documents. Problem resolved after changing that.
For someone else' reference, always take care while changing your project apps or adding new ones, make sure you check resources so that they are applicable on new applications linked to your project.

Related

How can I create a child with a given ID and value in Firebase in Android Studio? android - firebase

I want to make a new child in firebase with android studio
var dbref = FirebaseDatabase.getInstance().reference
dbref.child("chat").push().child(id).setValue(txt)
I used the code above and it looks like the picture below.
I want to create multiple ids with a value of txt under 'chat'.
But when I put push() in front of child(), strange value replaces id value and txt is not included.
This is what I want, but how do I get this?
chat
->id:txt
->me:hello
.
.
.
I'm using the Google Translator, so it can be awkward. But, as far as I know, this place the only place to ask this question.
the answer for your question is:
First create a model class like ChatModel(), that model will contain all the information inside that child "-M94B...". Then on your code pass the values to a empty ChatModel() variable like:
val chatModel = ChatModel()
chatModel.info1 = yourInfoVariable1
chatModel.info2 = yourInfoVariable2
.
.
.
and so on until you complete all of the data needed on that child. Then, you just need to pass it to your reference like:
FirebaseDatabase.getInstance().getReference("YOUR_CHAT_REFERENCE")
.child("HERE_YOU_PLACE_THE_ID_YOU_WANT")
.setValue(chatModel)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
//Execute a code if the data is updated in firebase
Toast.makeText(context!!, "Data Updated", Toast.LENGHT_SHORT).show()
}
.addOnFailureListener {
Toast.makeText(context!!, "Some error has ocurred!",Toast.LENGHT_SHORT).show()
}
These lines of code: ".child("HERE_YOU_PLACE_THE_ID_YOU_WANT").setValue(chatModel)" will set the UID you want for the child created, you just need to replace "HERE_YOU_PLACE_THE_ID_YOU_WANT" with your own UID.

Can I listen to a document content that has not already been created?

My requirement is the followin, I need to listen to a document value that has not yet been created.
My use case is this, I launch a cloud function that writes to an specific path in my firestore database, then from android I attach a listener to that value that has not yet been created, and then when its created a launch the logic with the data that has been placed at the database.
db.collection("home").document("homeTest/light_id").get().addOnCompleteListener {
if(it.isSuccessful){
progressBar.visibility = View.GONE
btnHomeLight.isEnabled = true
}
}else{
Log.e("Exception",""+it.exception)
}
}
At the time I have this code in my onCreate() the light_id has not yet been created at the database
After I trigger my function, that light_id is beign created and It should enable the btnHomeLight
But I'm getting this error
Caused by: java.lang.IllegalArgumentException: Invalid document
reference. Document references must have an even number of segments,
but home/hometest/light_id has 3
My database structure is this one
*Collection*
|__home
|__ *Document*
|___homeTest
|__ light_id : "Random_generated_ID"
Is my reference the problem ?
Since Im under the collection home and inside the document(homeTest/light_id) that should fetch that certain value.
Thanks
Solved the problem by doing this
val docRef = db.collection("home").document("homeTest")
docRef.addSnapshotListener { documentSnapshot, firebaseFirestoreException ->
if(!documentSnapshot?.getString("light_id").isNullOrEmpty()){
...
}
By the way, I dont really know if this is the best approach to do it.
If there is a better way I would be glad to hear about

Why does Android Studio indicate that an object might be null when it cannot be?

This is the code that I'm using:
ref.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "DocumentSnapshot data: " + document.getData());
}
}
});
And this how Android Studio highlights the error:
As I understand from this answer:
A successfully completed task will never pass null for the DocumentSnapshot.
How can I solve this without checking for nullity? Thanks!
Why does Android Studio indicate that an object might be null when it
cannot be?
Because lint cannot know. document.exists() can return null in general and that's why you see the warning. Lint performs basic checks and doesn't know the details about the Firebase API. You can even reproduce this behaviour with the Java Core API.
How can I solve this without checking for nullity? Thanks!
If you are 100% sure that it can never be null, you can use
#SuppressWarnings("ConstantConditions")
But I would not recommend that, because you don't know if the Firebase API will change in the future. Maybe the next version of Firebase will allow null returns.

Cloud Firestore rules only allow author to read document

I have the following document
and I have set the following rules
Every time I try to read the document using the Android SDK
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").get().addOnCompleteListener(appExecutors
.networkIO(),
task -> {});
I am getting this error
"com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions."
I am not sure what is wrong is it the rules or the Android call.
It looks like you meant to type resource.data.author_id instead of resource.data.author_d in the rule.
This code is trying to access every document in the entire users collection:
db.collection("users").get()
If there any any document in that collection that violates the security rules you set up, then it will fail.
Did you instead intend to try to access the single document that the user is supposed to have access to?
I think you can fix this from firebase consol. You just need to sllow access to all user. As I remember by default there was allowed only authorized users.
It seems that I had to add a where close in my code
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
FirebaseFirestore.setLoggingEnabled(true);
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").whereEqualTo("authorId",user.getUid()).get().addOnCompleteListener(appExecutors.networkIO(),task -> {});

firebase db: models with Kotlin delegated properties

I'm using Kotlin objects to work with my Firebase Database models, as described in the guide. I have many fields that are stored as strings, but really are enums, so to be type-safe I have enum fields in the models, plus a string delegated property that returns the firebase stored value (as suggested in a question I asked some time ago). Now, these fields work if I get/set the string delegate in code, but firebase libs seem to skip them when converting to/from database's json format.
A simple example:
abstract class BaseModel {
#Exclude
open var path: String? = null // fails even if I delete this field!
}
class Weight() : BaseModel() {
constructor(v: Double, u: WeightUnit) : this() {
value = v
unitEnum = u
}
var value: Double = 0.0
#Exclude
var unitEnum: WeightUnit = WeightUnit.KG
var unit: String by EnumStringLowercaseConverter(WeightUnit::class.java).getDelegate(Weight::unitEnum)
}
[...]
val testWeight = Weight(7.0, "kg")
db.getReference("/valid/path/to/save/testWeight").setValue(testWeight)
.addOnSuccessListener { r -> Log.d(LOG_TAG, "set successful") }
.addOnFailureListener { e -> Log.e(LOG_TAG, "set error", e) }
The setValue always gives a Permission Denied error, but works, if I delete unitEnum field and make unit a normal String property.
It's similar for reading: Firebase gives no errors when getting a Weight object, but the weightUnit field is never set to anything else than the default. But, if I manually do weight.unit = "lb", the unitEnum field properly returns WeightUnit.LB.
I'm using firebase libs v10.0.1
Now, the questions:
What can I do to make the delegated properties work correctly with firebase? I can try a different approach to the delegated enum fields, as long as the points from my original question are satisfied (readable, concise and type-safe code).
is there any way to see how exactly do firebase libs convert objects to/from json? Or at least see the converted json? Maybe then I could tweak things myself. Unfortunately, everything firebase-related shows as /* compiled code */ in AndroidStudio.
UPDATE: I could of course add a toMap() method to each model, where I would construct a map containing all the properties needed in firebase, but it would be tiresome to do this for every model, and it solves the saving issue only, the enum fields still wouldn't be set when getting.
The delegated props are also skipped when serializing with GSON. So maybe is there a generic way to make the delegated properties look like regular fields?
Try this code, it should work.
#get:Exclude #set:Exclude
var unitEnum: WeightUnit = WeightUnit.KG
var unit: String
get() = unitEnum.name
set(v) { unitEnum = WeightUnit.valueOf(v) }

Categories

Resources