I have a custom scalar type of `ClosedRange<Loc...
# graphql-kotlin
r
I have a custom scalar type of
ClosedRange<LocalDate>
. I have defined and registered a
Coercing<ClosedRange<LocalDate>, String>
(and via debugger confirmed the coercer is being called at runtime), but I get a Jackson exception when parsing an Input:
Copy code
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `kotlin.ranges.ClosedRange` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: my.MyInput["range"])
Any idea why that would be happening?
d
how did you define your
ClosedRange<LocalDate>
? Jackson requires default (empty) constructor or you will have to annotate your data class
r
ClosedRange
is a Kotlin stdlib type.
Maybe I misunderstand something about the interaction between coercers and Jackson. I thought if I defined a coercer then Jackson would not be invoked to instantiate that type, as my coercer instantiates it.
Could it have something to do with the fact that
ClosedRange
is an interface type? The underlying type is
ComparableRange
(private to stdlib).
d
where was it thrown? (from stacktrace)
r
Copy code
at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4236) ~[jackson-databind-2.11.4.jar:2.11.4]
	at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:4167) ~[jackson-databind-2.11.4.jar:2.11.4]
	at com.expediagroup.graphql.generator.execution.FunctionDataFetcher.convertValue(FunctionDataFetcher.kt:161) ~[graphql-kotlin-schema-generator-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.generator.execution.FunctionDataFetcher.convertArgumentToObject(FunctionDataFetcher.kt:142) ~[graphql-kotlin-schema-generator-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.generator.execution.FunctionDataFetcher.mapParameterToValue(FunctionDataFetcher.kt:114) ~[graphql-kotlin-schema-generator-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.generator.execution.FunctionDataFetcher.getParameters(FunctionDataFetcher.kt:90) ~[graphql-kotlin-schema-generator-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.generator.execution.FunctionDataFetcher.get(FunctionDataFetcher.kt:69) ~[graphql-kotlin-schema-generator-4.1.1.jar:4.1.1]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:270) ~[graphql-java-16.2.jar:?]
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:203) ~[graphql-java-16.2.jar:?]
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:60) ~[graphql-java-16.2.jar:?]
	at graphql.execution.ExecutionStrategy.completeValueForObject(ExecutionStrategy.java:646) ~[graphql-java-16.2.jar:?]
	at graphql.execution.ExecutionStrategy.completeValue(ExecutionStrategy.java:438) ~[graphql-java-16.2.jar:?]
	at graphql.execution.ExecutionStrategy.completeField(ExecutionStrategy.java:390) ~[graphql-java-16.2.jar:?]
	at graphql.execution.ExecutionStrategy.lambda$resolveFieldWithInfo$1(ExecutionStrategy.java:205) ~[graphql-java-16.2.jar:?]
	at java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:680) ~[?:?]
	at java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:658) ~[?:?]
	at java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2094) ~[?:?]
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:204) ~[graphql-java-16.2.jar:?]
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:60) ~[graphql-java-16.2.jar:?]
	at graphql.execution.Execution.executeOperation(Execution.java:165) ~[graphql-java-16.2.jar:?]
	at graphql.execution.Execution.execute(Execution.java:104) ~[graphql-java-16.2.jar:?]
	at graphql.GraphQL.execute(GraphQL.java:557) ~[graphql-java-16.2.jar:?]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:482) ~[graphql-java-16.2.jar:?]
	at graphql.GraphQL.executeAsync(GraphQL.java:446) ~[graphql-java-16.2.jar:?]
	at com.expediagroup.graphql.server.execution.GraphQLRequestHandler.executeRequest$suspendImpl(GraphQLRequestHandler.kt:45) ~[graphql-kotlin-server-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.server.execution.GraphQLRequestHandler.executeRequest(GraphQLRequestHandler.kt) ~[graphql-kotlin-server-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.server.execution.GraphQLServer.execute$suspendImpl(GraphQLServer.kt:48) ~[graphql-kotlin-server-4.1.1.jar:4.1.1]
	at com.expediagroup.graphql.server.execution.GraphQLServer.execute(GraphQLServer.kt) ~[graphql-kotlin-server-4.1.1.jar:4.1.1]
	<my call handler here>
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
	<jackson databind classes here>
r
Implying?
d
If I remember right* we had to add this logic to correctly cast the input arguments from data fetching environment *it's been a while
r
So what are my options?
d
you could look into creating custom data fetcher that extends default function data fetcher and override
mapParameterToValue
with custom logic to handle closed range
r
Thx, I'll give it a shot
Hmm, the custom data fetcher operates on the parent type that contains the
ClosedRange
field. So do I need to add special logic for every type where I use
ClosedRange
(yuck)?
d
based on the above stacktrace issue is when using this custom scalar as input -> this implies that it is a function
for properties default data fetcher should be fine (and values should be automatically coerced)
r
Doesn't seem.
ClosedRange
is a property of my input data class type, and when
mapParameterToValue
is called, the
param
value is the input data class type. Just tried it with the debugger.
d
yeah if it is a property on the input class that logic there attempts to serialize the whole object
you could try annotating your input data class with custom serializer/converter info that describes how to handle the closed range
r
I tried to create a custom Jackson
StdDeserializer
but this is what seems to be happening: serialized input on the wire -> coercer deserializes into
ClosedRange<LocalDate>
-> something serializes
ClosedRange<LocalDate>
into crazy JSON as the java.time objects have lots of internal state -> custom deserializer runs with crazy JSON
I can certainly extract what I need from the crazy JSON, but this seems really inelegant and inefficient
s
You need to create your custom object mapper and make sure that gets passed in and used in the
FunctionDataFetcher
https://github.com/ExpediaGroup/graphql-kotlin/blob/a6b957c999398c741cdfbc955fb862[…]expediagroup/graphql/generator/execution/FunctionDataFetcher.kt As mentioned above, you can customize the how
FunctionDataFetcher
gets created with
KotlinDataFetcherFactoryProvider
r
I tried that @Shane Myrick -- the FunctionDataFetcher gets the whole Input object, never just the
ClosedRange<LocalDate>
property. I'm not going to create custom fetchers for every object that happens to have a property of that type.
I created this issue with a working workaround, but there is some weird multi-level deserialization -> serialization -> deserialization happening: https://github.com/ExpediaGroup/graphql-kotlin/issues/1220.