-
Notifications
You must be signed in to change notification settings - Fork 319
Description
I believe this might be a bug, and as the title suggest, by wrapping an optional in a projected payload interface it will fail with an IllegalStateException.
We have some queries that allow for omitting the arguments. If this is contrary to the graphql spec, we'll adjust our usage insted. But it would be beneficial to be able to wrap any argument in optional and still have them resolve.
When wrapping a projected payload interface in an optional, the ProjectedPayloadMethodArgumentResolver
is not checking if there is an interface inside an optional type, which then fails with the following error:
2022-10-14 15:17:56,931 [DEBUG] o.s.g.d.m.HandlerMethod -- Could not resolve parameter [0] in public java.util.List<no.nav.nom.contract.nom.api.graphql.OrgEnhetResult> no.nav.nom.apps.api.graphql.controller.GraphQLOrgEnhetController.organisasjonsenheter(java.util.Optional<no.nav.nom.apps.api.graphql.common.projections.OrgEnheterSearch>,graphql.schema.DataFetchingEnvironment): No primary or single unique constructor found for interface no.nav.nom.apps.api.graphql.common.projections.OrgEnheterSearch
2022-10-14 15:17:56,953 [ERROR] o.s.g.e.ExceptionResolversExceptionHandler -- Unresolved IllegalStateException for executionId c5666fb2-5c5e-c803-0bde-0060fd82488f
java.lang.IllegalStateException: No primary or single unique constructor found for interface no.nav.nom.apps.api.graphql.common.projections.OrgEnheterSearch
at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:267)
at org.springframework.graphql.data.GraphQlArgumentBinder.createValue(GraphQlArgumentBinder.java:248)
at org.springframework.graphql.data.GraphQlArgumentBinder.bind(GraphQlArgumentBinder.java:163)
at org.springframework.graphql.data.method.annotation.support.ArgumentMethodArgumentResolver.resolveArgument(ArgumentMethodArgumentResolver.java:58)
at org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:83)
at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.getMethodArgumentValues(DataFetcherHandlerMethod.java:170)
at org.springframework.graphql.data.method.annotation.support.DataFetcherHandlerMethod.invoke(DataFetcherHandlerMethod.java:115)
at org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer$SchemaMappingDataFetcher.get(AnnotatedControllerConfigurer.java:514)
at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:74)
at org.springframework.graphql.execution.ReactorContextManager.invokeCallable(ReactorContextManager.java:104)
at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:73)
at org.springframework.boot.actuate.metrics.graphql.GraphQlMetricsInstrumentation.lambda$instrumentDataFetcher$1(GraphQlMetricsInstrumentation.java:98)
at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:282)
at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:211)
at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:59)
at graphql.execution.Execution.executeOperation(Execution.java:159)
at graphql.execution.Execution.execute(Execution.java:105)
at graphql.GraphQL.execute(GraphQL.java:645)
at graphql.GraphQL.lambda$parseValidateAndExecute$11(GraphQL.java:564)
at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1187)
at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2309)
at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:559)
at graphql.GraphQL.executeAsync(GraphQL.java:527)
...
at java.base/java.lang.Thread.run(Thread.java:833)
The controller method in question is mapped like this by the AnnotatedControllerConfigurer
Query.organisasjonsenheter => organisasjonsenheter(java.util.Optional<no.nav.nom.apps.api.graphql.common.projections.OrgEnheterSearch>,graphql.schema.DataFetchingEnvironment)
the projected payload we're using
@ProjectedPayload
public interface OrgEnheterSearch {
Set<String> getAgressoIder();
String getOrgNiv();
default Set<String> resolveAgressoIder() {
if(getAgressoIder() == null) {
return emptySet();
}
return getAgressoIder();
}
static OrgEnheterSearch emptyOrgEnheterSearch() {
return new OrgEnheterSearch() {
@Override
public Set<String> getAgressoIder() {
return emptySet();
}
@Override
public String getOrgNiv() {
return null;
}
};
}
}
query in question.
query {
organisasjonsenheter(where: {agressoIder: ["testId1","notFoundIdent"]}) {
id
code
}
}
optional query without arguments
query {
organisasjonsenheter {
id
code
}
}