mk-5 / gdx-fireapp

libGDX Firebase API
Apache License 2.0
63 stars 21 forks source link

Database Read does not enter fail on Cast Exception #27

Closed florianbaethge closed 5 years ago

florianbaethge commented 5 years ago

Describe the bug I am reading an object from the database. If a field of the object is of a different type in the POJO than in the database, it logically throws an exception within the API.

However, it never enters the fail() method of my promise, but still also the subscribe() Method.

To Reproduce

GdxFIRDatabase.inst()
                    .inReference("/myapp/users/$uid")
                    .readValue(UserInformation::class.java)
                    .silentFail()
                    .subscribe { userInformation ->
                        Gdx.app.log(TAG, "Fetched user information for $uid: $userInformation")
                        userInformation?.let { info ->
                            listener.success(info)
                        } ?: run {
                            listener.empty()
                        }
                    }.fail { s, throwable ->
                        Gdx.app.error(TAG, "ERROR FETCHING")
                        Gdx.app.error(TAG, s, throwable)
                        listener.failure(s, throwable)
                    }
E/GdxFireapp: Can't deserialize Map to UserInformation
    com.badlogic.gdx.utils.SerializationException: Unable to convert value to required type: value:  (java.lang.Long)
    Serialization trace:
    totalAppLaunches (myapp.models.user.UserInformation)
        at com.badlogic.gdx.utils.Json.readValue(Json.java:1105)
        at com.badlogic.gdx.utils.Json.readValue(Json.java:891)
        at com.badlogic.gdx.utils.Json.readValue(Json.java:967)
        at com.badlogic.gdx.utils.Json.readFields(Json.java:863)
        at com.badlogic.gdx.utils.Json.readValue(Json.java:1011)
        at com.badlogic.gdx.utils.Json.fromJson(Json.java:789)
        at pl.mk5.gdx.fireapp.database.MapConverter.convert(MapConverter.java:52)
        at pl.mk5.gdx.fireapp.promises.MapMitmConverter.doMitmConversion(MapMitmConverter.java:64)
        at pl.mk5.gdx.fireapp.promises.ConverterPromise.doComplete(ConverterPromise.java:67)
        at pl.mk5.gdx.fireapp.android.database.DataSnapshotResolver.resolve(DataSnapshotResolver.java:44)
        at pl.mk5.gdx.fireapp.android.database.SnapshotValueListener.onDataChange(SnapshotValueListener.java:37)
        at com.google.firebase.database.Query$1.onDataChange(com.google.firebase:firebase-database@@17.0.0:184)
        at com.google.firebase.database.core.ValueEventRegistration.fireEvent(com.google.firebase:firebase-database@@17.0.0:75)
        at com.google.firebase.database.core.view.DataEvent.fire(com.google.firebase:firebase-database@@17.0.0:63)
        at com.google.firebase.database.core.view.EventRaiser$1.run(com.google.firebase:firebase-database@@17.0.0:55)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6718)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

Expected behavior The UserInformation class just constists of some nullable Float values. However, if one of the fields in the database is e.g. a String, it cannot be converted and throws and Exception in the API. Here, I would expect it to NOT go into subscribe() but call the fail() method I supplied.

However, fail() is never entered... Or am I doing something wrong here? (Btw: Adding silentFail() in the call didn't change anything)

Platform (please check one of the following):

Smartphone (please complete the following information only if platform is Android or iOS):

mk-5 commented 5 years ago

Hi!

Thanks for reporting. I will look at this really soon, and I will come back to you with some answer.

mk-5 commented 5 years ago

No, you are totally right, the behaviour you have described should be expected. I will investigate that problem, a little more deeply.

Maybe one remark here, you know that subscribe is not needed to be called manually, don't you? If you would like to subscribe your firebase calls manually, you should say API about that by calling: GdxFIRApp.setAutoSubscribePromises(false)

In your case,

GdxFIRDatabase.inst()
                    .inReference("/myapp/users/$uid")
                    .readValue(UserInformation::class.java)
                    .silentFail()
                    .then{ userInformation ->
                        Gdx.app.log(TAG, "Fetched user information for $uid: $userInformation")
                        userInformation?.let { info ->
                            listener.success(info)
                        } ?: run {
                            listener.empty()
                        }
                    }

should be good enough.

It even may occur the problem here, because whole promise flow is subscribed before you attach failure callback if there is some really fast fail it may be the case.

Can you check two things?

  1. Change the subscribe to then
  2. If you would not like to use auto-subscribe, you can try to move .fail(...) before .subscribe()

I'm looking forward to getting some feedback from you :)

mk-5 commented 5 years ago

ookay, I've checked that, and the problem occurs because map<>pojo conversion is done after firebase api call, and fail callback doesn't catch that.

I will make some fix for it :)

florianbaethge commented 5 years ago

I've tried with then() before and also changed the order of fail()... but it seems you found the cause... ;)

I must say, you are awesome! Cudos to the fast response times!

mk-5 commented 5 years ago

No problem.

The fix version 2.1.4 is already in deployment, so it should be available in a minutes

florianbaethge commented 5 years ago

You are awesome!

P.s: You should add a PayPal donation button ;)