Expected behavior
Given an implementing class with a bounded generic type:
@Builder
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class TaskProperties<F extends TaskField> implements Serializable {
@Serial
private static final long serialVersionUID = 1115187246981180834L;
private List<F> fields;
}
And the below schema implementation:
type Query {
getFakeGeneric: TaskProperties
}
type Mutation {
testGenericFieldTypeNotFound(taskProperties: TaskPropertyInput!): TaskProperties
}
type TaskProperties {
fields: [TaskField]
}
type TaskField {
fieldName: String
value: Object #corresponds to a FieldType implementation - can be wrapped Java type or custom
type: FieldType
}
input TaskPropertyInput {
fields: [TaskConfigurationInput]
}
input TaskConfigurationInput {
fieldName: String
value: Object
type: FieldType
}
The TaskPropertyInput should correctly map to the TaskProperties java class during DGS's deserialization of the input object. Instead an error is thrown. Note that this same object can be resolved correctly as a return type, and that it works via REST mapping (with the default ObjectMapper implementation).
Actual behavior
The below exception is thrown around identifying class F:
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.util.LinkedHashMap<?, ?>] to type [@javax.validation.constraints.NotNull @com.netflix.graphql.dgs.InputArgument com.lawsonbusch.inputdemo.domain.TaskProperties<com.lawsonbusch.inputdemo.domain.TaskConfigurationField<?>>] for value '{fields=[{fieldName=test field, value=string value, type=STRING}]}'; nested exception is java.lang.ClassNotFoundException: F
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47) ~[spring-core-5.3.23.jar:5.3.23]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[spring-core-5.3.23.jar:5.3.23]
at com.netflix.graphql.dgs.internal.method.AbstractInputArgumentResolver.convertValue(AbstractInputArgumentResolver.kt:93) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.method.AbstractInputArgumentResolver.resolveArgument(AbstractInputArgumentResolver.kt:49) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.method.ArgumentResolverComposite.resolveArgument(ArgumentResolverComposite.kt:39) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.DataFetcherInvoker.get(DataFetcherInvoker.kt:62) ~[graphql-dgs-5.5.0.jar:5.5.0]
at graphql.schema.DataFetcherFactories.lambda$wrapDataFetcher$2(DataFetcherFactories.java:37) ~[graphql-java-19.3.jar:na]
at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:86) ~[graphql-java-19.3.jar:na]
at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:282) ~[graphql-java-19.3.jar:na]
at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:211) ~[graphql-java-19.3.jar:na]
at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:183) ~[graphql-java-19.3.jar:na]
at graphql.execution.AsyncSerialExecutionStrategy.lambda$execute$1(AsyncSerialExecutionStrategy.java:47) ~[graphql-java-19.3.jar:na]
at graphql.execution.Async.eachSequentiallyImpl(Async.java:191) ~[graphql-java-19.3.jar:na]
at graphql.execution.Async.eachSequentially(Async.java:180) ~[graphql-java-19.3.jar:na]
at graphql.execution.AsyncSerialExecutionStrategy.execute(AsyncSerialExecutionStrategy.java:42) ~[graphql-java-19.3.jar:na]
at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-19.3.jar:na]
at graphql.execution.Execution.execute(Execution.java:105) ~[graphql-java-19.3.jar:na]
at graphql.GraphQL.execute(GraphQL.java:645) ~[graphql-java-19.3.jar:na]
at graphql.GraphQL.lambda$parseValidateAndExecute$11(GraphQL.java:564) ~[graphql-java-19.3.jar:na]
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187) ~[na:na]
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309) ~[na:na]
at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:559) ~[graphql-java-19.3.jar:na]
at graphql.GraphQL.executeAsync(GraphQL.java:527) ~[graphql-java-19.3.jar:na]
at com.netflix.graphql.dgs.internal.BaseDgsQueryExecutor.baseExecute(BaseDgsQueryExecutor.kt:127) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.DefaultDgsQueryExecutor.execute(DefaultDgsQueryExecutor.kt:79) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.mvc.DgsRestController$graphql$executionResult$1.invoke(DgsRestController.kt:206) ~[graphql-dgs-spring-webmvc-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.mvc.DgsRestController$graphql$executionResult$1.invoke(DgsRestController.kt:204) ~[graphql-dgs-spring-webmvc-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.utils.TimeTracer.logTime(TimeTracer.kt:24) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.mvc.DgsRestController.graphql(DgsRestController.kt:204) ~[graphql-dgs-spring-webmvc-5.5.0.jar:5.5.0]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.23.jar:5.3.23]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.23.jar:5.3.23]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.65.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.23.jar:5.3.23]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.23.jar:5.3.23]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.65.jar:9.0.65]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.lang.ClassNotFoundException: F
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ~[na:na]
at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
at java.base/java.lang.Class.forName(Class.java:375) ~[na:na]
at com.netflix.graphql.dgs.internal.DefaultInputObjectMapper.mapToJavaObject(DefaultInputObjectMapper.kt:125) ~[graphql-dgs-5.5.0.jar:5.5.0]
at com.netflix.graphql.dgs.internal.method.InputObjectMapperConverter.convert(InputObjectMapperConverter.kt:40) ~[graphql-dgs-5.5.0.jar:5.5.0]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.3.23.jar:5.3.23]
... 78 common frames omitted
Steps to reproduce
I have created a public repo on my github profile that contains a test project, with instructions on how to run the project (basic maven build and run from IntelliJ). This project also has example queries in the ReadMe.md and java docs that described the expected errors and known workarounds.
Note that if the generic type F is replaced with a concrete implementation, an exception is still thrown.
Expected behavior
Given an implementing class with a bounded generic type:
And the below schema implementation:
The
TaskPropertyInputshould correctly map to theTaskPropertiesjava class during DGS's deserialization of the input object. Instead an error is thrown. Note that this same object can be resolved correctly as a return type, and that it works via REST mapping (with the defaultObjectMapperimplementation).Actual behavior
The below exception is thrown around identifying class
F:Steps to reproduce
I have created a public repo on my github profile that contains a test project, with instructions on how to run the project (basic maven build and run from IntelliJ). This project also has example queries in the
ReadMe.mdand java docs that described the expected errors and known workarounds.Note that if the generic type
Fis replaced with a concrete implementation, an exception is still thrown.