Simplify GraphQlSource auto-configuration
This commit simplifies the `GraphQL` configuration by focusing the infrastructure around the `GraphQlSource` auto-configuration. With this commit, the auto-configuration contributes a single `GraphQlSource` bean and removes the previoulsy contributed beans: `GraphQlSource.Builder` and `RuntimeWiring`. If a developer wants to take full control over the GraphQL setup, then contributing a `GraphQlSource` bean is the easiest solution. This commit also improves the `GraphQlSource.Builder` and allows for customizing the `RuntimeWiring.Builder` instead of providing a direct instance. This aligns well with the `RuntimeWiringBuilderCustomizer` interface provided by the starter. Closes gh-101
This commit is contained in:
@@ -24,7 +24,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
@@ -51,50 +50,33 @@ import org.springframework.graphql.execution.GraphQlSource;
|
||||
@EnableConfigurationProperties(GraphQlProperties.class)
|
||||
public class GraphQlAutoConfiguration {
|
||||
|
||||
private static final String[] SCHEMA_FILES_EXTENSIONS = new String[] {"*.graphqls", "*.graphql", "*.gql", "*.gqls"};
|
||||
|
||||
@Bean
|
||||
public GraphQlSource graphQlSource(GraphQlSource.Builder builder) {
|
||||
public GraphQlSource graphQlSource(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
|
||||
ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
|
||||
ObjectProvider<Instrumentation> instrumentationsProvider,
|
||||
ObjectProvider<GraphQlSourceBuilderCustomizer> sourceCustomizers,
|
||||
ObjectProvider<RuntimeWiringBuilderCustomizer> wiringCustomizers) throws IOException {
|
||||
|
||||
|
||||
List<Resource> schemaResources = resolveSchemaResources(resourcePatternResolver, properties.getSchema().getLocations());
|
||||
GraphQlSource.Builder builder = GraphQlSource.builder().schemaResources(schemaResources.toArray(new Resource[0]))
|
||||
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))
|
||||
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
|
||||
wiringCustomizers.orderedStream().forEach((customizer) -> builder.runtimeWiring(customizer::customize));
|
||||
sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(GraphQlSource.Builder.class)
|
||||
public static class GraphQlSourceConfiguration {
|
||||
|
||||
private static final String[] SCHEMA_FILES_EXTENSIONS = new String[] {"*.graphqls", "*.graphql", "*.gql", "*.gqls"};
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RuntimeWiring runtimeWiring(ObjectProvider<RuntimeWiringBuilderCustomizer> customizers) {
|
||||
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQlSource.Builder graphQlSourceBuilder(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
|
||||
RuntimeWiring runtimeWiring, ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
|
||||
ObjectProvider<Instrumentation> instrumentationsProvider,
|
||||
ObjectProvider<GraphQlSourceBuilderCustomizer> customizersProvider) throws IOException {
|
||||
|
||||
List<Resource> schemaResources = resolveSchemaResources(resourcePatternResolver, properties.getSchema().getLocations());
|
||||
GraphQlSource.Builder builder = GraphQlSource.builder().schemaResources(schemaResources.toArray(new Resource[0]))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))
|
||||
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
|
||||
customizersProvider.forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
}
|
||||
|
||||
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, List<String> schemaLocations) throws IOException {
|
||||
List<Resource> schemaResources = new ArrayList<>();
|
||||
for (String location : schemaLocations) {
|
||||
for (String extension : SCHEMA_FILES_EXTENSIONS) {
|
||||
schemaResources.addAll(Arrays.asList(resolver.getResources(location + extension)));
|
||||
}
|
||||
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, List<String> schemaLocations) throws IOException {
|
||||
List<Resource> schemaResources = new ArrayList<>();
|
||||
for (String location : schemaLocations) {
|
||||
for (String extension : SCHEMA_FILES_EXTENSIONS) {
|
||||
schemaResources.addAll(Arrays.asList(resolver.getResources(location + extension)));
|
||||
}
|
||||
return schemaResources;
|
||||
}
|
||||
|
||||
return schemaResources;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.graphql.execution.GraphQlSource;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass({GraphQL.class, QuerydslPredicateExecutor.class })
|
||||
@ConditionalOnBean(GraphQlSource.Builder.class)
|
||||
@ConditionalOnBean(GraphQlSource.class)
|
||||
@AutoConfigureAfter(GraphQlAutoConfiguration.class)
|
||||
public class GraphQlWebFluxQuerydslAutoConfiguration {
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.graphql.execution.GraphQlSource;
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnClass({GraphQL.class, QuerydslPredicateExecutor.class })
|
||||
@ConditionalOnBean(GraphQlSource.Builder.class)
|
||||
@ConditionalOnBean(GraphQlSource.class)
|
||||
@AutoConfigureAfter(GraphQlAutoConfiguration.class)
|
||||
public class GraphQlWebMvcQuerydslAutoConfiguration {
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@ class DefaultGraphQlSourceBuilder implements GraphQlSource.Builder {
|
||||
|
||||
private final List<Resource> schemaResources = new ArrayList<>();
|
||||
|
||||
private RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
|
||||
private Consumer<RuntimeWiring.Builder> runtimeWiringConfigurers = (builder) -> {
|
||||
};
|
||||
|
||||
private final List<DataFetcherExceptionResolver> exceptionResolvers = new ArrayList<>();
|
||||
|
||||
@@ -70,9 +71,9 @@ class DefaultGraphQlSourceBuilder implements GraphQlSource.Builder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQlSource.Builder runtimeWiring(RuntimeWiring runtimeWiring) {
|
||||
Assert.notNull(runtimeWiring, "RuntimeWiring is required");
|
||||
this.runtimeWiring = runtimeWiring;
|
||||
public GraphQlSource.Builder runtimeWiring(Consumer<RuntimeWiring.Builder> configurer) {
|
||||
Assert.notNull(configurer, "RuntimeWiring configurer is required");
|
||||
this.runtimeWiringConfigurers = this.runtimeWiringConfigurers.andThen(configurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -106,7 +107,9 @@ class DefaultGraphQlSourceBuilder implements GraphQlSource.Builder {
|
||||
.map(this::parseSchemaResource).reduce(TypeDefinitionRegistry::merge)
|
||||
.orElseThrow(() -> new IllegalArgumentException("'schemaResources' should not be empty"));
|
||||
|
||||
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(registry, this.runtimeWiring);
|
||||
RuntimeWiring.Builder runtimeWiring = RuntimeWiring.newRuntimeWiring();
|
||||
this.runtimeWiringConfigurers.accept(runtimeWiring);
|
||||
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(registry, runtimeWiring.build());
|
||||
schema = applyTypeVisitors(schema);
|
||||
|
||||
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
|
||||
|
||||
@@ -79,13 +79,15 @@ public interface GraphQlSource {
|
||||
Builder schemaResources(Resource... resources);
|
||||
|
||||
/**
|
||||
* Set a {@link RuntimeWiring} to contribute data fetchers and more.
|
||||
* @param runtimeWiring the runtime wiring for contribution
|
||||
* Configure consumers that will be given access to the {@link RuntimeWiring.Builder} used to
|
||||
* build the {@link RuntimeWiring}. A {@link RuntimeWiring#newRuntimeWiring() default builder}
|
||||
* instance is created as a starting point.
|
||||
* @param configurer the runtime wiring configurer
|
||||
* @return the current builder
|
||||
* @see graphql.schema.idl.SchemaGenerator#makeExecutableSchema(TypeDefinitionRegistry,
|
||||
* RuntimeWiring)
|
||||
*/
|
||||
Builder runtimeWiring(RuntimeWiring runtimeWiring);
|
||||
Builder runtimeWiring(Consumer<RuntimeWiring.Builder> configurer);
|
||||
|
||||
/**
|
||||
* Add {@link DataFetcherExceptionResolver}'s to use for resolving exceptions from
|
||||
|
||||
@@ -50,13 +50,9 @@ public abstract class GraphQlTestUtils {
|
||||
public static GraphQlSource.Builder initGraphQlSource(String schemaContent, String typeName, String fieldName,
|
||||
DataFetcher<?> fetcher) {
|
||||
|
||||
RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring()
|
||||
.type(typeName, (builder) -> builder.dataFetcher(fieldName, fetcher))
|
||||
.build();
|
||||
|
||||
return GraphQlSource.builder()
|
||||
.schemaResources(new ByteArrayResource(schemaContent.getBytes(StandardCharsets.UTF_8)))
|
||||
.runtimeWiring(wiring);
|
||||
.runtimeWiring(wiring -> wiring.type(typeName, (builder) -> builder.dataFetcher(fieldName, fetcher)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class QuerydslDataFetcherTests {
|
||||
if (configurer != null) {
|
||||
TypeRuntimeWiring.Builder typeBuilder = TypeRuntimeWiring.newTypeWiring("Query");
|
||||
configurer.accept(typeBuilder);
|
||||
graphQlSourceBuilder.runtimeWiring(RuntimeWiring.newRuntimeWiring().type(typeBuilder).build());
|
||||
graphQlSourceBuilder.runtimeWiring(wiring -> wiring.type(typeBuilder));
|
||||
}
|
||||
|
||||
GraphQLTypeVisitor visitor = QuerydslDataFetcher.registrationTypeVisitor(
|
||||
|
||||
@@ -70,20 +70,18 @@ public abstract class BookTestUtils {
|
||||
}
|
||||
|
||||
private static GraphQlSource graphQlSource() {
|
||||
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
|
||||
builder.type(TypeRuntimeWiring.newTypeWiring("Query")
|
||||
.dataFetcher("bookById", (env) -> {
|
||||
Long id = Long.parseLong(env.getArgument("id"));
|
||||
return booksMap.get(id);
|
||||
}));
|
||||
builder.type(TypeRuntimeWiring.newTypeWiring("Subscription")
|
||||
.dataFetcher("bookSearch", (env) -> {
|
||||
String author = env.getArgument("author");
|
||||
return Flux.fromIterable(booksMap.values()).filter((book) -> book.getAuthor().contains(author));
|
||||
}));
|
||||
return GraphQlSource.builder()
|
||||
.schemaResources(new ClassPathResource("books/schema.graphqls"))
|
||||
.runtimeWiring(builder.build())
|
||||
.runtimeWiring(builder -> builder.type(TypeRuntimeWiring.newTypeWiring("Query")
|
||||
.dataFetcher("bookById", (env) -> {
|
||||
Long id = Long.parseLong(env.getArgument("id"));
|
||||
return booksMap.get(id);
|
||||
}))
|
||||
.type(TypeRuntimeWiring.newTypeWiring("Subscription")
|
||||
.dataFetcher("bookSearch", (env) -> {
|
||||
String author = env.getArgument("author");
|
||||
return Flux.fromIterable(booksMap.values()).filter((book) -> book.getAuthor().contains(author));
|
||||
})))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user