Skip to content

🐛 Bug Report: Kotlin server SDK does not properly process nullable values when mapping #667

@Narmo

Description

@Narmo

👟 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?

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions