diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index 14d6575f..1f94d16c 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -58,6 +58,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.convert.ConversionService; +import org.springframework.data.domain.ScrollPosition; import org.springframework.format.FormatterRegistrar; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.format.support.FormattingConversionService; @@ -82,14 +83,27 @@ import org.springframework.util.StringUtils; import org.springframework.validation.DataBinder; /** - * {@link RuntimeWiringConfigurer} that detects {@link SchemaMapping @SchemaMapping} - * annotated handler methods in {@link Controller @Controller} classes and - * registers them as {@link DataFetcher}s. + * {@link RuntimeWiringConfigurer} that finds {@link SchemaMapping @SchemaMapping} + * annotated handler methods in {@link Controller @Controller} classes declared in + * Spring configuration, and registers them as {@link DataFetcher}s. * *

In addition to initializing a {@link RuntimeWiring.Builder}, this class, also * provides an option to {@link #configure(GraphQLCodeRegistry.Builder) configure} * data fetchers on a {@link GraphQLCodeRegistry.Builder}. * + *

This class detects the following strategies in Spring configuration, + * expecting to find a single, unique bean of that type: + *

+ * + * + * * @author Rossen Stoyanchev * @author Brian Clozel * @since 1.0.0 @@ -125,9 +139,6 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I private final FormattingConversionService conversionService = new DefaultFormattingConversionService(); - @Nullable - private CursorStrategy cursorStrategy; - private final List customArgumentResolvers = new ArrayList<>(8); @Nullable @@ -156,21 +167,6 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I registrar.registerFormatters(this.conversionService); } - /** - * Configure a {@link CursorStrategy} to handle pagination requests, which - * results in one of the following: - * - * @since 1.2 - */ - public void setCursorStrategy(@Nullable CursorStrategy cursorStrategy) { - this.cursorStrategy = cursorStrategy; - } - /** * Add a {@link HandlerMethodArgumentResolver} for custom controller method * arguments. Such custom resolvers are ordered after built-in resolvers @@ -270,18 +266,8 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I // Type based resolvers.addResolver(new DataFetchingEnvironmentMethodArgumentResolver()); resolvers.addResolver(new DataLoaderMethodArgumentResolver()); - if (this.cursorStrategy != null) { - resolvers.addResolver(createSubrangeMethodArgumentResolver(this.cursorStrategy)); - } - if (springDataPresent) { - try { - resolvers.addResolver( - new SortMethodArgumentResolver(obtainApplicationContext().getBean(SortStrategy.class))); - } - catch (NoSuchBeanDefinitionException ex) { - // ignore - } - } + addSubrangeMethodArgumentResolver(resolvers); + addSortMethodArgumentResolver(resolvers); if (springSecurityPresent) { ApplicationContext context = obtainApplicationContext(); resolvers.addResolver(new PrincipalMethodArgumentResolver()); @@ -299,15 +285,34 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I return resolvers; } - @SuppressWarnings("unchecked") - private static HandlerMethodArgumentResolver createSubrangeMethodArgumentResolver(CursorStrategy strategy) { + @SuppressWarnings({"unchecked", "CastCanBeRemovedNarrowingVariableType"}) + private void addSubrangeMethodArgumentResolver(HandlerMethodArgumentResolverComposite resolvers) { + try { + CursorStrategy strategy = obtainApplicationContext().getBean(CursorStrategy.class); + if (springDataPresent) { + if (strategy.supports(ScrollPosition.class)) { + CursorStrategy strategyToUse = (CursorStrategy) strategy; + resolvers.addResolver(new ScrollSubrangeMethodArgumentResolver(strategyToUse)); + return; + } + } + resolvers.addResolver(new SubrangeMethodArgumentResolver<>(strategy)); + } + catch (NoSuchBeanDefinitionException ex) { + // ignore + } + } + + private void addSortMethodArgumentResolver(HandlerMethodArgumentResolverComposite resolvers) { if (springDataPresent) { - if (strategy.supports(org.springframework.data.domain.ScrollPosition.class)) { - return new ScrollSubrangeMethodArgumentResolver( - (CursorStrategy) strategy); + try { + SortStrategy strategy = obtainApplicationContext().getBean(SortStrategy.class); + resolvers.addResolver(new SortMethodArgumentResolver(strategy)); + } + catch (NoSuchBeanDefinitionException ex) { + // ignore } } - return new SubrangeMethodArgumentResolver<>(strategy); } protected final ApplicationContext obtainApplicationContext() { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java index 877912f0..f95d31fe 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java @@ -32,6 +32,7 @@ import org.springframework.graphql.GraphQlSetup; import org.springframework.graphql.TestExecutionRequest; import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.graphql.data.pagination.ConnectionFieldTypeVisitor; +import org.springframework.graphql.data.pagination.CursorStrategy; import org.springframework.graphql.data.query.ScrollPositionCursorStrategy; import org.springframework.graphql.data.query.ScrollSubrange; import org.springframework.graphql.data.query.WindowConnectionAdapter; @@ -81,16 +82,7 @@ public class SchemaMappingPaginationTests { } """; - ExecutionGraphQlService graphQlService = graphQlService((configurer, setup) -> { - - setup.typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer()); - - ScrollPositionCursorStrategy cursorStrategy = new ScrollPositionCursorStrategy(); - WindowConnectionAdapter connectionAdapter = new WindowConnectionAdapter(cursorStrategy); - setup.typeVisitor(ConnectionFieldTypeVisitor.create(List.of(connectionAdapter))); - - configurer.setCursorStrategy(cursorStrategy); - }); + ExecutionGraphQlService graphQlService = graphQlService(); ExecutionGraphQlResponse response = graphQlService.execute(TestExecutionRequest.forDocument(document)).block(); @@ -110,19 +102,22 @@ public class SchemaMappingPaginationTests { "}}}"); } - private ExecutionGraphQlService graphQlService(BiConsumer consumer) { + private ExecutionGraphQlService graphQlService() { + + ScrollPositionCursorStrategy cursorStrategy = new ScrollPositionCursorStrategy(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(BookController.class); + context.registerBean(CursorStrategy.class, () -> cursorStrategy); context.refresh(); AnnotatedControllerConfigurer configurer = new AnnotatedControllerConfigurer(); configurer.setApplicationContext(context); + configurer.afterPropertiesSet(); GraphQlSetup setup = GraphQlSetup.schemaContent(SCHEMA).runtimeWiring(configurer); - consumer.accept(configurer, setup); - - configurer.afterPropertiesSet(); + setup.typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer()); + setup.typeVisitor(ConnectionFieldTypeVisitor.create(List.of(new WindowConnectionAdapter(cursorStrategy)))); return setup.toGraphQlService(); }