apollographql / apollo-kotlin

:rocket:  A strongly-typed, caching GraphQL client for the JVM, Android, and Kotlin multiplatform.
https://www.apollographql.com/docs/kotlin
MIT License
3.73k stars 654 forks source link

[v3] Calling toJson on an Operation.Data object throws ClassNotFoundException #6024

Closed PhilippeStoffel closed 1 month ago

PhilippeStoffel commented 1 month ago

Version

3.8.4

Summary

We recently migrated our Apollo integration to version 3, and we are experiencing an issue with the function toJson that we didn't have in version 2. Calling toJson on an Operation.Data object throws java.lang.ClassNotFoundException: qf.adapter.x$b_ResponseAdapter$Data. We have no idea why it happens now, we can use the ApolloResponse normally otherwise, it's just toJson that seems broken.

Steps to reproduce the behavior

Steps to reproduce:

  1. Execute a graphql query
  2. Try to call toJson on the Data object you get in your response
  3. It should crash with a java.lang.ClassNotFoundException exception

⚠️ Important point: The problem only happens on a release build, calling toJson on a debug build works fine.

We suspect that this might be linked to R8 optimisations, but it seems very strange to us that it didn't happen when using version 2.

What we tried: We tried converting the object to JSON with another library (Gson) but the result uses obfuscated names. Example: {"data":{"singleWorkoutSession":{"a":[{"a":"AudioSet","b":{"a":2389,"b":"https:\/\/

As you can see, the JSON objects are named a or b instead of the actual name of the variables in the response.

We also tried to add a proguard rule for the generated ResponseAdapter.Data of the query where we have the problem.

Logs

java.lang.ClassNotFoundException: qf.adapter.x$b_ResponseAdapter$Data at java.lang.Class.classForName(Native Method) at java.lang.Class.forName(Class.java:454) at java.lang.Class.forName(Class.java:379) at lb.v0.a(SourceFile:57) at lb.v0.b(SourceFile:16) at lb.s0.a(SourceFile:22) at lb.s0.b(Unknown Source:6) at ih.e.k(SourceFile:194) at ih.f$f.invokeSuspend(SourceFile:360) at kotlin.coroutines.jvm.internal.a.resumeWith(SourceFile:12) at mu.y0.run(SourceFile:124) at ru.o$a.run(SourceFile:4) at tu.k.run(SourceFile:3) at tu.a.s(SourceFile:1) at tu.a$c.d(SourceFile:15) at tu.a$c.p(SourceFile:29) at tu.a$c.run(Unknown Source:0) Caused by: java.lang.ClassNotFoundException: qf.adapter.x$b_ResponseAdapter$Data

BoD commented 1 month ago

Hi! This appears to be an R8 issue indeed. .toJson() will use introspection to resolve the adapter - if its name has been obfuscated, this will fail.

This is also why you're seeing field names being different when using Gson.

Can you adapt your R8 rules to exclude the package where the models are generated?

BoD commented 1 month ago

Hi! Do you still need help with this?

BoD commented 1 month ago

Closing for now, don't hesitate to re-open if needed.