๐ Bug Report: Kotlin server SDK does not properly process nullable values when mapping
Narmo opened this issue ยท 2 comments
๐ Reproduction steps
When I try to fetch teams list using Teams object, I get NPE.
Demo code:
val client = Client().setEndpoint(Application.appWriteEndpoint).setProject(Application.appWriteProjectId).setKey(Application.appWriteApiKey)
val teamsClient = Teams(client)
val teams = teamsClient.list().teams // this line causes NPE, see below
teams.forEach {
println(it.name)
}
๐ Expected behavior
Teams list should load.
๐ Actual Behavior
The call crashes with following stack trace:
Exception in thread "OkHttp Dispatcher" java.lang.NullPointerException: null cannot be cast to non-null type kotlin.collections.Map<kotlin.String, kotlin.Any>
at io.appwrite.models.Team$Companion.from(Team.kt:83)
at io.appwrite.models.TeamList$Companion.from(TeamList.kt:43)
at io.appwrite.services.Teams$list$converter$1.invoke(Teams.kt:43)
at io.appwrite.services.Teams$list$converter$1.invoke(Teams.kt:42)
at io.appwrite.Client$awaitResponse$2$1.onResponse(Client.kt:507)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
I've investigated the crash and found the point. The error occurs in mapping the Team object's preferences in Team.kt file:
@Suppress("UNCHECKED_CAST")
fun <T> from(
map: Map<String, Any>,
nestedType: Class<T>
) = Team<T>(
id = map["\$id"] as String,
createdAt = map["\$createdAt"] as String,
updatedAt = map["\$updatedAt"] as String,
name = map["name"] as String,
total = (map["total"] as Number).toLong(),
prefs = Preferences.from(map = map["prefs"] as Map<String, Any>, nestedType),
)
In last line, where prefs is assigned, actual value of maps["prefs"] is null, while cast expects it to be Map<String, Any>. So the prefs field in data class Team should be nullable, and I suggest that the cast should be rewritten, something like that:
prefs = (map["prefs"] as? Map<String, Any>)?.run { prefs = Preferences.from(map = this, nestedType),
I've checked the sdk-generator
project and found that all models, when processing maps or lists of maps, always assume that the input value is not nullable, while it actually can be null (and so this is true for corresponding fields in data classes, which have to be nullable too).
Here is example from templates/kotlin/src/main/kotlin/io/appwrite/models/Model.kt.twig
, line 64, where aforementioned code for Kotlin Server SDK is being generated:
{{ property.name | escapeKeyword | removeDollarSign }} = {% if property.sub_schema %}{% if property.type == 'array' %}(map["{{ property.name | escapeDollarSign }}"] as List<Map<String, Any>>).map { {{ property.sub_schema | caseUcfirst }}.from(map = it{% if definition.name | hasGenericType(spec) %}, nestedType{% endif %}) }{% else %}{{ property.sub_schema | caseUcfirst }}.from(map = map["{{property.name | escapeDollarSign }}"] as Map<String, Any>{% if definition.name | hasGenericType(spec) %}, nestedType{% endif %}){% endif %}{% else %}{% if property.type == "integer" or property.type == "number" %}({% endif %}map["{{ property.name | escapeDollarSign }}"]{% if property.type == "integer" or property.type == "number" %} as{% if not property.required %}?{% endif %} Number){% endif %}{% if property.type == "integer" %}{% if not property.required %}?{% endif %}.toLong(){% elseif property.type == "number" %}{% if not property.required %}?{% endif %}.toDouble(){% else %} as{% if not property.required %}?{% endif %} {{ property | propertyType(spec) | raw }}{% endif %}{% endif %},
Unfortunately due to my lack of knowledge of Twig or PHP stops me from creating proper pull-request for this feature, so I hope that someone will be able to fix this in future releases.
I've created similar issue in sdk-for-kotlin
repo before discovering this repo, so please remove that issue if it is not required anymore: appwrite/sdk-for-kotlin#30.
๐ฒ Appwrite version
Different version (specify in environment)
๐ป Operating system
MacOS
๐งฑ Your Environment
I use io.appwrite:sdk-for-kotlin:2.0.0
which automatically fetches all required dependencies.
๐ Have you spent some time to check if this issue has been raised before?
- I checked and didn't find similar issue
๐ข Have you read the Code of Conduct?
- I have read the Code of Conduct
@Narmo Thanks a lot for raising this issue. We will get someone to work on this :)
This was fixed server-side