Description
I'm in the process of porting an existing project to spring-graphql 1.3.1 and want to use all of its convenience features for more readable code. From the docs about annotated controllers I gathered that defining and reusing DataLoaders should be quite easy. I have a setup similar to this:
@Controller
class EntityConnectionsBatchController {
@BatchMapping
fun entityConnections(keys: Set<EntityFilter>): Mono<Map<EntityBatchFilter, EntityConnectionGraphQL>> {
return Mono.just(
// complex filtering logic...
.toMap()
)
}
}
@Controller
class EntityQueryController {
@QueryMapping
fun entities(
@Argument first: Int,
@Argument after: CursorGraphQL?,
@Argument offset: Int?,
@Argument filter: EntityFilterInputGraphQL?,
dataLoader: DataLoader<EntityBatchFilter, EntityConnectionGraphQL>
): CompletableFuture<EntityConnectionGraphQL> =
dataLoader.load(
EntityFilter(
// add all the arguments into the filter
)
)
}
But when I try to query this, I run into an exception:
Cannot resolve DataLoader for parameter 'dataLoader' in method public java.util.concurrent.CompletableFuture<org.example.controller.graphql.model.EntityConnectionGraphQL> org.example.controller.graphql.resolvers.queries.EntityQueryController.entities(int,java.lang.String,java.lang.Integer,org.example.controller.graphql.model.EntityFilterInputGraphQL,org.dataloader.DataLoader<org.example.controller.graphql.resolvers.queries.EntityBatchFilter, org.example.controller.graphql.model.EntityConnectionGraphQL>). Neither the name of the declared value type 'class org.example.controller.graphql.model.EntityConnectionGraphQL' nor the method parameter name 'dataLoader' match to any DataLoader. The DataLoaderRegistry contains: [EntityGroupBatchFilter.entityGroupConnections, EntityBatchFilter.entityConnections, /* ... */]
I was trying to change some internal names to get it to work out of the box, but upon debugging a bit I am confused how this is supposed to work:
- The AnnotatedControllerConfigurer reads the methods and creates FieldCoordinates of each consisting of the pattern
<typeName>.<fieldName>
. Both are overridable from the annotation, typeName is by defaultClass<>.getSimpleName()
:
https://github.com/spring-projects/spring-graphql/blob/v1.3.1/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java#L324 - The DataLoaderMethodArgumentResolver however is looking up by
Class<>.getName()
(fully-qualified) OR alternatively<parameterName>
a few lines after:
https://github.com/spring-projects/spring-graphql/blob/v1.3.1/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/DataLoaderMethodArgumentResolver.java#L57
Not only will the class names never match by default, because one is simpleName and one is package-qualified, but it is impossible to select an existing data loader since a.) I cannot configure the @BatchMapping
annotation to not include a dot and b.) I cannot request injection of a data loader with dot in the name since dots are (thankfully) illegal in parameter names.
Am I missing something and there is a reason for this? To me it sounds as if the DataLoaderMethodArgumentResolver should actually request the same kind of pattern of <simpleClassName>.<parameterName>
by default.
So to get this to work currently I have to inject the environment and find it by knowing the naming convention of AnnotatedControllerConfigurer:
fun entities(
// ...
env: DataFetchingEnvironment
): CompletableFuture<EntityConnectionGraphQL> {
val dataLoader = env.getDataLoader<EntityBatchFilter, EntityConnectionGraphQL>("${EntityBatchFilter::class.simpleName}.entityConnections")
return dataLoader.load(/* ... */)