Stylianos Gakis
04/10/2024, 1:46 PMbod
04/10/2024, 1:51 PMI think this fails at a validation step before our code even runs in the first placehm I don't think it should happen? Do you have a stacktrace?
Stylianos Gakis
04/10/2024, 1:54 PMmbonnin
04/10/2024, 1:54 PMmbonnin
04/10/2024, 1:55 PMmbonnin
04/10/2024, 1:55 PMkevin.cianfarini
04/10/2024, 1:56 PMsealed interface SomeSchemaEnum {
enum class Input : SomeSchemaEnum {
ValueOne, ValueTwo, ValueThree
}
data object Unknown : SomeSchemaEnum
}
Maybe something like this would help?Stylianos Gakis
04/10/2024, 1:56 PMenum ChatMessageContext {
FOO,
BAR,
}
And it is then in an input as
input SomeInput {
otherField: ID!
context: ChatMessageContext
}
And here I passed the UNKNOWN, but thatβs not meant for the input, only for forwards compat for things coming towards our app, not from us to backend I suppose πStylianos Gakis
04/10/2024, 1:57 PMStylianos Gakis
04/10/2024, 1:57 PMmbonnin
04/10/2024, 1:58 PMStylianos Gakis
04/10/2024, 1:58 PMmbonnin
04/10/2024, 1:59 PMkevin.cianfarini
04/10/2024, 2:00 PMmbonnin
04/10/2024, 2:00 PMAdapter<MyEnum>
mbonnin
04/10/2024, 2:01 PMStylianos Gakis
04/10/2024, 2:12 PMwasyl
04/10/2024, 2:52 PM// TODO: Ask for an common supertype for enums on Apollo side? Or figure something out to get rid of `Any.` extension
// Maybe there's an API in Apollo where one can hook into fallback enum conversion?
internal fun <T> Any.mapUnknownEnumValue(
owner: String,
target: T,
) = target.also { log.w { "Unknown $owner, value=$this" } }
that we use when mapping any Apollo enum, for example:
rating = when (val rating = rating) {
Rating.COMPETENT -> ApiTalentSkill.Rating.Competent
Rating.EXPERT -> ApiTalentSkill.Rating.Expert
Rating.STRONG -> ApiTalentSkill.Rating.Strong
is Rating.UNKNOWN__ -> rating.mapUnknownEnumValue("Rating", ApiTalentSkill.Rating.Competent)
}
The idea is to log an error for UNKNOWN__
for devs, but fall back to a safe value for the users.
An alternative to enum class Input : SomeSchemaEnum
(which btw I'd call KnownValue
or something, as the issue isn't exactly with input vs output, right?) maybe Apollo could define a wrapper for enums, pretty much like Optional
? The Unknown
doesn't have anything enum-specific inside anywayNicolas Chaduc
04/10/2024, 3:03 PMwasyl
04/10/2024, 3:04 PMOptional<SomeEnum?>
for nullable enum fields, then we're missing the raw value returned by the API (which IMO is a must for logging/debugging)mbonnin
04/10/2024, 3:08 PMUNKNOWN__(rawValue)
This is when the value is transparent to the client code. You get it from the server and pass it back in without needing to know what the value meansmbonnin
04/10/2024, 3:09 PMmbonnin
04/10/2024, 3:10 PMsealedClassesForEnumsMatching
?wasyl
04/10/2024, 3:10 PMStylianos Gakis
04/10/2024, 3:14 PMenum ChatMessageContext {
FOO,
BAR,
}
it does not know about that unknown value at all
And this mutation fails before it even reaches their code as it seems, DGS is just erroring out on some sort of validation step.
The tracing goes something like:
servlet.request -> spring.handler -> graphql.request
|-> graphql.parsing
|
|-> graphql.validation
Where it just throws an exceptionStylianos Gakis
04/10/2024, 3:15 PMsealedClassesForEnumsMatchingNo, not using that https://github.com/HedvigInsurance/android/blob/38557b0fada325958b99c1af3ca07999a3bbd8d6/app/apollo/apollo-octopus-public/build.gradle.kts#L21-L58
mbonnin
04/10/2024, 3:16 PMsealedClassesForEnumsMatching
the default and mark the UNKNOWN__
constructor with an Opt-In marker or so?mbonnin
04/10/2024, 3:17 PMUNKNOWN__
enum. Meaning it came from the backend and it's there's a very good chance it's valid thereStylianos Gakis
04/10/2024, 3:18 PMmbonnin
04/10/2024, 3:18 PMbod
04/10/2024, 3:18 PMmbonnin
04/10/2024, 3:19 PMbod
04/10/2024, 3:19 PMmbonnin
04/10/2024, 3:20 PM@ApolloDelicateEnum
?Stylianos Gakis
04/10/2024, 3:20 PMbod
04/10/2024, 3:22 PMStylianos Gakis
04/10/2024, 3:23 PMsealedClassesForEnumsMatching.set(false)
if it turns out to be hard to migrate πStylianos Gakis
04/10/2024, 3:23 PMkevin.cianfarini
04/10/2024, 9:42 PMTo add on top of everything that has been said, there's actually a use case where people actually want to be able sendUNKNOWN__(rawValue)
This is when the value is transparent to the client code. You get it from the server and pass it back in without needing to know what the value means
In this scenario I would think that the backing string value for the enum of an unknown value should be marked as
internal
somehow so that only Apollo's runtime could access it.
Perhaps a value class wrapped around a string with an internal value. Like:
// generated
data class UNKNOWN_(val value: OpaqueEnumValue)
// Apollo runtime
data class OpaqueEnumValue(internal val jsonValue: String)
This would allow Apollo users to pass around the unknown value without ever being able to infer what it's carrying. I think that's a nice safety guarantee given the current semantics of unknown values dont expose what they were over the wire.agrosner
04/10/2024, 11:28 PMYang
04/12/2024, 11:38 AMmbonnin
04/12/2024, 12:57 PMmbonnin
04/12/2024, 12:58 PMsealedClassesForEnumsMatching.set(emptyList())
and revert back to plain enumsYang
04/12/2024, 1:11 PMmbonnin
04/12/2024, 1:11 PMYang
04/12/2024, 1:24 PM