graphql-java-codegen
graphql-java-codegen copied to clipboard
Java Time types cannot be used when serialising GraphQlRequest
Issue Description
We are trying to use the code-gen to generate the GraphQlRequest payload, from a graphql model with custom scalar types of type java.time
We have these custom scalars defined
customTypesMapping = [ Time: "java.time.OffsetTime", ]
We found that the Json generated by graphQLRequest.toHttpJsonBody() didnt append the result of during OffsetTime.toString, which is for example "20:00Z".
Debugging your task, we found these undocumented option useObjectMapperForRequestSerialization to add the type names where ObjectMapper should be used instead.
Unfortunately, the ObjetMapper your code uses, its defined as a static constant, so its not customizable. In theory if your ObjectMapper was created with new ObjectMapper().findAndRegisterModules(), it would use any jackson modules registered in the class-path, and make possible for example to have the java time types serialised
Steps to Reproduce
-
Add custom scalar type called Time
scalar Time -
Add a custom scalar parser so Time gets generated as java.time.OffsetTime
-
Add this config to the gradle task
task generateGraphqlClient(type: GraphQLCodegenGradleTask) {
graphqlSchemaPaths = ["$projectDir/src/main/resources/schema.graphqls".toString()]
outputDir = new File("$buildDir/generated-client")
customTypesMapping = [
Time: "java.time.OffsetTime",
]
generateClient = true
generateBuilder = true
generateToString = true
useObjectMapperForRequestSerialization = ["Time"]
}
Expected Result
We should be able to serialise graphql model objects where custom scalar types are used. ObjectMapper should be able to serialise fields where a custom ObjectMapper is used, or at least where the default ObjectMapper can load any jackson modules present in the classpath
'mutation .......(......: { ..... deliveryWindow: { start: \"10:00Z\", end: \"17:00Z \"}, .....'
Actual Result
Query failed to parse :
'mutation .......(......: { ..... deliveryWindow: { start: 10:00Z, end: 17:00Z }, .....'
Your Environment and Setup
- graphql-java-codegen version: 5.3.0
- Build tool: Gradle
- Mapping Config:
task generateGraphqlClient(type: GraphQLCodegenGradleTask) {
graphqlSchemaPaths = ["$projectDir/src/main/resources/schema.graphqls".toString()]
outputDir = new File("$buildDir/generated-client")
customTypesMapping = [
Time: "java.time.OffsetTime",
]
generateClient = true
generateBuilder = true
generateToString = true
useObjectMapperForRequestSerialization = ["Time"]
}
Hi @chusmc
Thanks for providing such detailed information.
What if you manually register required modules during the application init/startup phase in the following way:
GraphQLRequestSerializer.OBJECT_MAPPER.registerModule(new SimpleModule().addSerializer(new OffsetTimeSerializer()));
The working test case for java.time classes can be found here:
https://github.com/kobylynskyi/graphql-java-codegen/blob/f291209d204376881e886473c22bf4fdbc3af5ad/src/test/java/com/kobylynskyi/graphql/codegen/model/graphql/GraphQLRequestSerializerTest.java#L303-L317
Please let me know how it goes.
Also, I've added the description of useObjectMapperForRequestSerialization to the documentation. Thanks for pointing this out!
Ok. Thanks we weren't sure we should reconfigure the internal OBJECT_MAPPER
We will give it a go, as its just some unit tests,
Is it possible to suggest, that GraphQLRequestSerializer could be configured with an external injected ObjectMaper also?
Regards
I am bumping into same problem as well and have created a sample repo here could you please check and advise? We're expecting the serialised query like below
query reviews { reviews: reviews(searchInput: { submittedDate: "2022-07-15" }){ starScore } }
but the date is unquoted
use <useObjectMapperForRequestSerialization>Date</useObjectMapperForRequestSerialization>
Thanks @jay3047 Now I am getting below error
Java 8 date/time type java.time.LocalDate not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
How to customize the ObjectMapper thats being created in com.kobylynskyi.graphql.codegen.utils.Utils.OBJECT_MAPPER
Thanks @jay3047 Now I am getting below error
Java 8 date/time typejava.time.LocalDatenot supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handlingHow to customize the ObjectMapper thats being created in
com.kobylynskyi.graphql.codegen.utils.Utils.OBJECT_MAPPER
GraphQLRequestSerializer.OBJECT_MAPPER.registerModule(
new SimpleModule().addSerializer(new ZonedDateTimeSerializer()));
UpdateDateMutationRequest updateDateMutationRequest = new UpdateDateMutationRequest();
DateInput input = new DateInput();
input.setDateTime(ZonedDateTime.parse("2020-07-30T22:17:17.884-05:00[America/Chicago]"));
updateDateMutationRequest.setInput(input);
GraphQLRequest graphQLRequest = new GraphQLRequest(updateDateMutationRequest);
One mentioned by @jxnu-liguobin does work , but I created custom Deserializer something like below and it worked
GraphQLRequestSerializer.OBJECT_MAPPER.registerModule( SimpleModule().addSerializer( DateSerializerKotlin() ) )
`class DateSerializerKotlin : JsonSerializer<LocalDate>() { override fun serialize( localDate: LocalDate, gen: JsonGenerator?, serializers: SerializerProvider ) { val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val formattedString: String = localDate?.format(formatter) gen!!.writeString(formattedString) }
override fun handledType(): Class<LocalDate>? {
return LocalDate::class.java
}
}`
One mentioned by @jxnu-liguobin does work , but I created custom Deserializer something like below and it worked
GraphQLRequestSerializer.OBJECT_MAPPER.registerModule( SimpleModule().addSerializer( DateSerializerKotlin() ) )
class DateSerializerKotlin : JsonSerializer() { override fun serialize( localDate: LocalDate, gen: JsonGenerator?, serializers: SerializerProvider ) { val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val formattedString: String = localDate?.format(formatter) gen!!.writeString(formattedString) }
override fun handledType(): Class<LocalDate>? { return LocalDate::class.java }}
This is good if you need this more flexible format. 👍🏻