Auto-configure DataLoaderRegistrar components
This commit configures all `DataLoaderRegistrar` application components with the `GraphQlService`. This allows applications to contribute `DataLoader` instances without using the annotated-controller programming model. Closes gh-189
This commit is contained in:
@@ -18,6 +18,7 @@ package org.springframework.graphql.boot;
|
||||
|
||||
import graphql.GraphQL;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -28,14 +29,14 @@ import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for creating a
|
||||
* {@link WebGraphQlHandler}.
|
||||
* {@link GraphQlService}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
@@ -45,19 +46,17 @@ import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
@AutoConfigureAfter(GraphQlAutoConfiguration.class)
|
||||
public class GraphQlServiceAutoConfiguration {
|
||||
|
||||
private final BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BatchLoaderRegistry batchLoaderRegistry() {
|
||||
return this.batchLoaderRegistry;
|
||||
return new DefaultBatchLoaderRegistry();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlService graphQlService(GraphQlSource graphQlSource) {
|
||||
public GraphQlService graphQlService(GraphQlSource graphQlSource, ObjectProvider<DataLoaderRegistrar> dataLoaderRegistrars) {
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
service.addDataLoaderRegistrar(this.batchLoaderRegistry);
|
||||
dataLoaderRegistrars.forEach(service::addDataLoaderRegistrar);
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
* <ul>
|
||||
* <li>{@code @Controller}
|
||||
* <li>{@code RuntimeWiringConfigurer}
|
||||
* <li>{@code DataLoaderRegistrar}
|
||||
* <li>{@code @JsonComponent}
|
||||
* <li>{@code Converter}
|
||||
* <li>{@code GenericConverter}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.boot.jackson.JsonComponent;
|
||||
import org.springframework.boot.test.autoconfigure.filter.StandardAnnotationCustomizableTypeExcludeFilter;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.converter.GenericConverter;
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -49,6 +50,7 @@ public class GraphQlTypeExcludeFilter extends StandardAnnotationCustomizableType
|
||||
Set<Class<?>> includes = new LinkedHashSet<>();
|
||||
includes.add(JsonComponent.class);
|
||||
includes.add(RuntimeWiringConfigurer.class);
|
||||
includes.add(DataLoaderRegistrar.class);
|
||||
includes.add(Converter.class);
|
||||
includes.add(GenericConverter.class);
|
||||
for (String optionalInclude : OPTIONAL_INCLUDES) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -50,6 +51,16 @@ class GraphQlServiceAutoConfigurationTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureDataLoaderRegistrars() {
|
||||
this.contextRunner.withUserConfiguration(CustomDataLoaderRegistrar.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQlService.class);
|
||||
assertThat(context).getBeanNames(DataLoaderRegistrar.class).contains("batchLoaderRegistry", "customDataLoaderRegistrar");
|
||||
assertThat(context).getBean(GraphQlService.class).extracting("dataLoaderRegistrars").asList().hasSize(2);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class GraphQlSourceConfiguration {
|
||||
|
||||
@@ -59,4 +70,13 @@ class GraphQlServiceAutoConfigurationTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomDataLoaderRegistrar {
|
||||
|
||||
@Bean
|
||||
DataLoaderRegistrar customDataLoaderRegistrar() {
|
||||
return mock(DataLoaderRegistrar.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package org.springframework.graphql.boot.test;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import graphql.GraphQLContext;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import org.dataloader.DataLoaderRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -28,6 +30,7 @@ import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
import org.springframework.graphql.web.WebInput;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
@@ -55,6 +58,7 @@ class GraphQlTypeExcludeFilterTests {
|
||||
assertThat(excludes(filter, Controller1.class)).isFalse();
|
||||
assertThat(excludes(filter, Controller2.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleDataLoaderRegistrar.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleService.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
|
||||
@@ -67,6 +71,7 @@ class GraphQlTypeExcludeFilterTests {
|
||||
assertThat(excludes(filter, Controller1.class)).isFalse();
|
||||
assertThat(excludes(filter, Controller2.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleDataLoaderRegistrar.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleService.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
|
||||
@@ -79,6 +84,7 @@ class GraphQlTypeExcludeFilterTests {
|
||||
assertThat(excludes(filter, Controller1.class)).isTrue();
|
||||
assertThat(excludes(filter, Controller2.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleDataLoaderRegistrar.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleService.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
|
||||
@@ -91,6 +97,7 @@ class GraphQlTypeExcludeFilterTests {
|
||||
assertThat(excludes(filter, Controller1.class)).isFalse();
|
||||
assertThat(excludes(filter, Controller2.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleDataLoaderRegistrar.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleService.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRepository.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
|
||||
@@ -103,6 +110,7 @@ class GraphQlTypeExcludeFilterTests {
|
||||
assertThat(excludes(filter, Controller1.class)).isTrue();
|
||||
assertThat(excludes(filter, Controller2.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleRuntimeWiringConfigurer.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleDataLoaderRegistrar.class)).isFalse();
|
||||
assertThat(excludes(filter, ExampleService.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleRepository.class)).isTrue();
|
||||
assertThat(excludes(filter, ExampleWebInterceptor.class)).isTrue();
|
||||
@@ -166,6 +174,13 @@ class GraphQlTypeExcludeFilterTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class ExampleDataLoaderRegistrar implements DataLoaderRegistrar {
|
||||
@Override
|
||||
public void registerDataLoaders(DataLoaderRegistry registry, GraphQLContext context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class ExampleWebInterceptor implements WebInterceptor {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -258,6 +258,10 @@ public class BookController {
|
||||
}
|
||||
----
|
||||
|
||||
`BatchLoaderRegistry` is itself a `DataLoaderRegistrar`, as it will register `DataLoader` instances for each request.
|
||||
Applications can also contribute other `DataLoaderRegistrar` instances as components - they will be configured with
|
||||
the `ExecutionGraphQlService` automatically.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -451,6 +455,7 @@ By default, `@GraphQlTest` limits scanning to the following beans:
|
||||
|
||||
- `@Controller`
|
||||
- `RuntimeWiringConfigurer`
|
||||
- `DataLoaderRegistrar`
|
||||
- `JsonComponent`
|
||||
- `Converter`
|
||||
- `GenericConverter`
|
||||
|
||||
Reference in New Issue
Block a user