Extract base GraphQlSource builder

Separate more clearly the SDL builder options from other common options
independent of how GraphQLSchema is created.

See gh-312
This commit is contained in:
rstoyanchev
2022-04-19 14:51:36 +01:00
parent ddb740898e
commit 54d061b218
8 changed files with 442 additions and 326 deletions

View File

@@ -243,9 +243,9 @@ The main implementation, `DefaultExecutionGraphQlService`, is configured with a
`graphql.GraphQL` instance to use for request execution. It provides a builder API to
initialize GraphQL Java and build a `GraphQlSource`.
The default `GraphQlSource` builder, accessible via `GraphQlSource.builder()`, enables
support for <<execution-reactive-datafetcher>>, <<execution-context>>, and
<<execution-exceptions>>.
The default `GraphQlSource` builder, accessible via
`GraphQlSource.schemaResourceBuilder()`, enables support for
<<execution-reactive-datafetcher>>, <<execution-context>>, and <<execution-exceptions>>.
The Spring Boot {spring-boot-ref-docs}/web.html#web.graphql[starter] initializes a
`GraphQlSource` instance through the default `GraphQlSource.Builder` and also enables

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import graphql.GraphQL;
import graphql.execution.instrumentation.ChainedInstrumentation;
import graphql.execution.instrumentation.Instrumentation;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeVisitor;
import graphql.schema.SchemaTraverser;
/**
* Implementation of {@link GraphQlSource.Builder} that leaves it to subclasses
* to initialize {@link GraphQLSchema}.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 1.0.0
*/
abstract class AbstractGraphQlSourceBuilder<B extends GraphQlSource.Builder<B>> implements GraphQlSource.Builder<B> {
private final List<DataFetcherExceptionResolver> exceptionResolvers = new ArrayList<>();
private final List<GraphQLTypeVisitor> typeVisitors = new ArrayList<>();
private final List<Instrumentation> instrumentations = new ArrayList<>();
private Consumer<GraphQL.Builder> graphQlConfigurers = (builder) -> {
};
@Override
public B exceptionResolvers(List<DataFetcherExceptionResolver> resolvers) {
this.exceptionResolvers.addAll(resolvers);
return self();
}
@Override
public B typeVisitors(List<GraphQLTypeVisitor> typeVisitors) {
this.typeVisitors.addAll(typeVisitors);
return self();
}
@Override
public B instrumentation(List<Instrumentation> instrumentations) {
this.instrumentations.addAll(instrumentations);
return self();
}
@Override
public B configureGraphQl(Consumer<GraphQL.Builder> configurer) {
this.graphQlConfigurers = this.graphQlConfigurers.andThen(configurer);
return self();
}
@SuppressWarnings("unchecked")
private <T extends B> T self() {
return (T) this;
}
@Override
public GraphQlSource build() {
GraphQLSchema schema = initGraphQlSchema();
schema = applyTypeVisitors(schema);
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
builder.defaultDataFetcherExceptionHandler(new ExceptionResolversExceptionHandler(this.exceptionResolvers));
if (!this.instrumentations.isEmpty()) {
builder = builder.instrumentation(new ChainedInstrumentation(this.instrumentations));
}
this.graphQlConfigurers.accept(builder);
return new FixedGraphQlSource(builder.build(), schema);
}
/**
* Subclasses must implement this method to provide the
* {@link GraphQLSchema} instance.
*/
protected abstract GraphQLSchema initGraphQlSchema();
private GraphQLSchema applyTypeVisitors(GraphQLSchema schema) {
List<GraphQLTypeVisitor> visitors = new ArrayList<>(this.typeVisitors);
visitors.add(ContextDataFetcherDecorator.TYPE_VISITOR);
GraphQLCodeRegistry.Builder codeRegistry = GraphQLCodeRegistry.newCodeRegistry(schema.getCodeRegistry());
Map<Class<?>, Object> vars = Collections.singletonMap(GraphQLCodeRegistry.Builder.class, codeRegistry);
SchemaTraverser traverser = new SchemaTraverser();
traverser.depthFirstFullSchema(visitors, schema, vars);
return schema.transformWithoutTypes(builder -> builder.codeRegistry(codeRegistry));
}
/**
* {@link GraphQlSource} with fixed {@link GraphQL} and {@link GraphQLSchema} instances.
*/
private static class FixedGraphQlSource implements GraphQlSource {
private final GraphQL graphQl;
private final GraphQLSchema schema;
FixedGraphQlSource(GraphQL graphQl, GraphQLSchema schema) {
this.graphQl = graphQl;
this.schema = schema;
}
@Override
public GraphQL graphQl() {
return this.graphQl;
}
@Override
public GraphQLSchema schema() {
return this.schema;
}
}
}

View File

@@ -1,242 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import graphql.GraphQL;
import graphql.execution.instrumentation.ChainedInstrumentation;
import graphql.execution.instrumentation.Instrumentation;
import graphql.language.InterfaceTypeDefinition;
import graphql.language.UnionTypeDefinition;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLSchema;
import graphql.schema.GraphQLTypeVisitor;
import graphql.schema.SchemaTraverser;
import graphql.schema.TypeResolver;
import graphql.schema.idl.CombinedWiringFactory;
import graphql.schema.idl.NoopWiringFactory;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import graphql.schema.idl.WiringFactory;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default implementation of {@link GraphQlSource.Builder} that initializes a
* {@link GraphQL} instance and wraps it with a {@link GraphQlSource} that returns it.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
*/
class DefaultGraphQlSourceBuilder implements GraphQlSource.Builder {
private final Set<Resource> schemaResources = new LinkedHashSet<>();
private final List<RuntimeWiringConfigurer> runtimeWiringConfigurers = new ArrayList<>();
@Nullable
private TypeResolver defaultTypeResolver;
private final List<DataFetcherExceptionResolver> exceptionResolvers = new ArrayList<>();
private final List<GraphQLTypeVisitor> typeVisitors = new ArrayList<>();
private final List<Instrumentation> instrumentations = new ArrayList<>();
@Nullable
private BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory;
private Consumer<GraphQL.Builder> graphQlConfigurers = (builder) -> {
};
@Override
public GraphQlSource.Builder schemaResources(Resource... resources) {
this.schemaResources.addAll(Arrays.asList(resources));
return this;
}
@Override
public GraphQlSource.Builder configureRuntimeWiring(RuntimeWiringConfigurer configurer) {
this.runtimeWiringConfigurers.add(configurer);
return this;
}
@Override
public GraphQlSource.Builder defaultTypeResolver(TypeResolver typeResolver) {
this.defaultTypeResolver = typeResolver;
return this;
}
@Override
public GraphQlSource.Builder exceptionResolvers(List<DataFetcherExceptionResolver> resolvers) {
this.exceptionResolvers.addAll(resolvers);
return this;
}
@Override
public GraphQlSource.Builder typeVisitors(List<GraphQLTypeVisitor> typeVisitors) {
this.typeVisitors.addAll(typeVisitors);
return this;
}
@Override
public GraphQlSource.Builder instrumentation(List<Instrumentation> instrumentations) {
this.instrumentations.addAll(instrumentations);
return this;
}
@Override
public GraphQlSource.Builder schemaFactory(
BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory) {
this.schemaFactory = schemaFactory;
return this;
}
@Override
public GraphQlSource.Builder configureGraphQl(Consumer<GraphQL.Builder> configurer) {
this.graphQlConfigurers = this.graphQlConfigurers.andThen(configurer);
return this;
}
@Override
public GraphQlSource build() {
TypeDefinitionRegistry registry = this.schemaResources.stream()
.map(this::parseSchemaResource).reduce(TypeDefinitionRegistry::merge)
.orElseThrow(MissingSchemaException::new);
RuntimeWiring runtimeWiring = initRuntimeWiring();
registerDefaultTypeResolver(registry, runtimeWiring);
GraphQLSchema schema = (this.schemaFactory != null ?
this.schemaFactory.apply(registry, runtimeWiring) :
new SchemaGenerator().makeExecutableSchema(registry, runtimeWiring));
schema = applyTypeVisitors(schema);
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
builder.defaultDataFetcherExceptionHandler(new ExceptionResolversExceptionHandler(this.exceptionResolvers));
if (!this.instrumentations.isEmpty()) {
builder = builder.instrumentation(new ChainedInstrumentation(this.instrumentations));
}
this.graphQlConfigurers.accept(builder);
GraphQL graphQl = builder.build();
return new CachedGraphQlSource(graphQl, schema);
}
private RuntimeWiring initRuntimeWiring() {
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
this.runtimeWiringConfigurers.forEach(configurer -> configurer.configure(builder));
List<WiringFactory> factories = new ArrayList<>();
WiringFactory factory = builder.build().getWiringFactory();
if (!factory.getClass().equals(NoopWiringFactory.class)) {
factories.add(factory);
}
this.runtimeWiringConfigurers.forEach(configurer -> configurer.configure(builder, factories));
if (!factories.isEmpty()) {
builder.wiringFactory(new CombinedWiringFactory(factories));
}
return builder.build();
}
private void registerDefaultTypeResolver(TypeDefinitionRegistry registry, RuntimeWiring runtimeWiring) {
TypeResolver typeResolver =
(this.defaultTypeResolver != null ? this.defaultTypeResolver : new ClassNameTypeResolver());
registry.types().values().stream()
.filter(def -> def instanceof UnionTypeDefinition || def instanceof InterfaceTypeDefinition)
.forEach(def -> runtimeWiring.getTypeResolvers().putIfAbsent(def.getName(), typeResolver));
}
private TypeDefinitionRegistry parseSchemaResource(Resource schemaResource) {
Assert.notNull(schemaResource, "'schemaResource' not provided");
Assert.isTrue(schemaResource.exists(), "'schemaResource' must exist: " + schemaResource);
try {
try (InputStream inputStream = schemaResource.getInputStream()) {
return new SchemaParser().parse(inputStream);
}
}
catch (IOException ex) {
throw new IllegalArgumentException("Failed to load schema resource: " + schemaResource);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to parse schema resource: " + schemaResource, ex);
}
}
private GraphQLSchema applyTypeVisitors(GraphQLSchema schema) {
List<GraphQLTypeVisitor> visitors = new ArrayList<>(this.typeVisitors);
visitors.add(ContextDataFetcherDecorator.TYPE_VISITOR);
GraphQLCodeRegistry.Builder codeRegistry = GraphQLCodeRegistry.newCodeRegistry(schema.getCodeRegistry());
Map<Class<?>, Object> vars = Collections.singletonMap(GraphQLCodeRegistry.Builder.class, codeRegistry);
SchemaTraverser traverser = new SchemaTraverser();
traverser.depthFirstFullSchema(visitors, schema, vars);
return schema.transformWithoutTypes(builder -> builder.codeRegistry(codeRegistry));
}
/**
* GraphQlSource that returns the built GraphQL instance and its schema.
*/
private static class CachedGraphQlSource implements GraphQlSource {
private final GraphQL graphQl;
private final GraphQLSchema schema;
CachedGraphQlSource(GraphQL graphQl, GraphQLSchema schema) {
this.graphQl = graphQl;
this.schema = schema;
}
@Override
public GraphQL graphQl() {
return this.graphQl;
}
@Override
public GraphQLSchema schema() {
return this.schema;
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import graphql.language.InterfaceTypeDefinition;
import graphql.language.UnionTypeDefinition;
import graphql.schema.GraphQLSchema;
import graphql.schema.TypeResolver;
import graphql.schema.idl.CombinedWiringFactory;
import graphql.schema.idl.NoopWiringFactory;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;
import graphql.schema.idl.WiringFactory;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Implementation of {@link GraphQlSource.SchemaResourceBuilder}.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 1.0.0
*/
final class DefaultSchemaResourceGraphQlSourceBuilder
extends AbstractGraphQlSourceBuilder<GraphQlSource.SchemaResourceBuilder>
implements GraphQlSource.SchemaResourceBuilder {
private final Set<Resource> schemaResources = new LinkedHashSet<>();
private final List<RuntimeWiringConfigurer> runtimeWiringConfigurers = new ArrayList<>();
@Nullable
private TypeResolver typeResolver;
@Nullable
private BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory;
@Override
public DefaultSchemaResourceGraphQlSourceBuilder schemaResources(Resource... resources) {
this.schemaResources.addAll(Arrays.asList(resources));
return this;
}
@Override
public DefaultSchemaResourceGraphQlSourceBuilder configureRuntimeWiring(RuntimeWiringConfigurer configurer) {
this.runtimeWiringConfigurers.add(configurer);
return this;
}
@Override
public DefaultSchemaResourceGraphQlSourceBuilder defaultTypeResolver(TypeResolver typeResolver) {
this.typeResolver = typeResolver;
return this;
}
@Override
public DefaultSchemaResourceGraphQlSourceBuilder schemaFactory(
BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory) {
this.schemaFactory = schemaFactory;
return this;
}
@Override
protected GraphQLSchema initGraphQlSchema() {
TypeDefinitionRegistry registry = this.schemaResources.stream()
.map(this::parse)
.reduce(TypeDefinitionRegistry::merge)
.orElseThrow(MissingSchemaException::new);
RuntimeWiring runtimeWiring = initRuntimeWiring();
TypeResolver typeResolver = initTypeResolver();
registry.types().values().forEach(def -> {
if (def instanceof UnionTypeDefinition || def instanceof InterfaceTypeDefinition) {
runtimeWiring.getTypeResolvers().putIfAbsent(def.getName(), typeResolver);
}
});
return (this.schemaFactory != null ?
this.schemaFactory.apply(registry, runtimeWiring) :
new SchemaGenerator().makeExecutableSchema(registry, runtimeWiring));
}
private TypeDefinitionRegistry parse(Resource schemaResource) {
Assert.notNull(schemaResource, "'schemaResource' not provided");
Assert.isTrue(schemaResource.exists(), "'schemaResource' must exist: " + schemaResource);
try {
try (InputStream inputStream = schemaResource.getInputStream()) {
return new SchemaParser().parse(inputStream);
}
}
catch (IOException ex) {
throw new IllegalArgumentException("Failed to load schema resource: " + schemaResource);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to parse schema resource: " + schemaResource, ex);
}
}
private RuntimeWiring initRuntimeWiring() {
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
this.runtimeWiringConfigurers.forEach(configurer -> configurer.configure(builder));
List<WiringFactory> factories = new ArrayList<>();
WiringFactory factory = builder.build().getWiringFactory();
if (!factory.getClass().equals(NoopWiringFactory.class)) {
factories.add(factory);
}
this.runtimeWiringConfigurers.forEach(configurer -> configurer.configure(builder, factories));
if (!factories.isEmpty()) {
builder.wiringFactory(new CombinedWiringFactory(factories));
}
return builder.build();
}
private TypeResolver initTypeResolver() {
return (this.typeResolver != null ? this.typeResolver : new ClassNameTypeResolver());
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.execution;
import graphql.schema.GraphQLSchema;
import org.springframework.util.Assert;
/**
* {@link GraphQlSource.Builder} that uses an externally prepared
* {@link GraphQLSchema}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class ExternalSchemaGraphQlSourceBuilder extends AbstractGraphQlSourceBuilder<ExternalSchemaGraphQlSourceBuilder>
implements GraphQlSource.Builder<ExternalSchemaGraphQlSourceBuilder> {
private final GraphQLSchema schema;
public ExternalSchemaGraphQlSourceBuilder(GraphQLSchema schema) {
Assert.notNull(schema, "GraphQLSchema is required");
this.schema = schema;
}
@Override
protected GraphQLSchema initGraphQlSchema() {
return this.schema;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
package org.springframework.graphql.execution;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -31,8 +31,9 @@ import graphql.schema.idl.TypeDefinitionRegistry;
import org.springframework.core.io.Resource;
/**
* Strategy to resolve the {@link GraphQL} instance to use.
* Strategy to resolve a {@link GraphQL} and a {@link GraphQLSchema}.
*
* <p>
* This contract also includes a {@link GraphQlSource} builder encapsulating the
@@ -44,78 +45,51 @@ import org.springframework.core.io.Resource;
*/
public interface GraphQlSource {
/**
* Return the {@link GraphQL} to use. This can be a cached instance or a different one
* from time to time (e.g. based on a reloaded schema).
* @return the GraphQL instance to use
* Return the {@link GraphQL} to use. This can be a cached instance or a
* different one from time to time (e.g. based on a reloaded schema).
*/
GraphQL graphQl();
/**
* Return the {@link GraphQLSchema} used by the current {@link GraphQL}.
* @return the current GraphQL schema
*/
GraphQLSchema schema();
/**
* Return a builder for a {@link GraphQlSource} given input for the initialization of
* {@link GraphQL} and {@link graphql.schema.GraphQLSchema}.
* @return a builder for a GraphQlSource
* Return a {@link GraphQlSource} builder that parses GraphQL Schema
* resources and uses {@link RuntimeWiring} to create the
* {@link graphql.schema.GraphQLSchema}.
*/
static Builder builder() {
return new DefaultGraphQlSourceBuilder();
static SchemaResourceBuilder schemaResourceBuilder() {
return new DefaultSchemaResourceGraphQlSourceBuilder();
}
/**
* Builder for a {@link GraphQlSource}.
* Return a {@link GraphQlSource} builder that uses an externally prepared
* {@link GraphQLSchema}.
*/
interface Builder {
static Builder<?> builder(GraphQLSchema schema) {
return new ExternalSchemaGraphQlSourceBuilder(schema);
}
/**
* Common configuration options for all {@link GraphQlSource} builders,
* independent of how {@link GraphQLSchema} is created.
*/
interface Builder<B extends Builder<B>> {
/**
* Add {@literal ".graphqls"} schema resources to be
* {@link TypeDefinitionRegistry#merge(TypeDefinitionRegistry) merged} into the type registry.
* @param resources resources for the GraphQL schema
* @return the current builder
* @see graphql.schema.idl.SchemaParser#parse(File)
*/
Builder schemaResources(Resource... resources);
/**
* Add a component that is given access to the {@link RuntimeWiring.Builder}
* used to register {@link graphql.schema.DataFetcher}s, custom scalar
* types, type resolvers, and more.
* @param configurer the configurer to apply
* @return the current builder
* @see graphql.schema.idl.SchemaGenerator#makeExecutableSchema(TypeDefinitionRegistry, RuntimeWiring)
*/
Builder configureRuntimeWiring(RuntimeWiringConfigurer configurer);
/**
* Configure the default {@link TypeResolver} to use for GraphQL Interface
* and Union types that don't already have such a registration after all
* {@link #configureRuntimeWiring(RuntimeWiringConfigurer) RuntimeWiringConfigurer's}
* have been applied.
* <p>A GraphQL {@code TypeResolver} is used to determine the GraphQL Object
* type of values returned from DataFetcher's of GraphQL Interface or
* Union fields.
* <p>By default this is set to {@link ClassNameTypeResolver}, which
* tries to match the simple class name of the Object value to a GraphQL
* Object type, and it also tries the same for supertypes (base classes
* and interfaces). See the Javadoc of {@code ClassNameTypeResolver} for
* further ways to customize matching a Java class to a GraphQL Object type.
* @param typeResolver the {@code TypeResolver} to use
* @return the current builder
* @see ClassNameTypeResolver
*/
Builder defaultTypeResolver(TypeResolver typeResolver);
/**
* Add {@link DataFetcherExceptionResolver}'s to use for resolving exceptions from
* {@link graphql.schema.DataFetcher}'s.
* Add {@link DataFetcherExceptionResolver}s for resolving exceptions
* from {@link graphql.schema.DataFetcher}s.
* @param resolvers the resolvers to add
* @return the current builder
*/
Builder exceptionResolvers(List<DataFetcherExceptionResolver> resolvers);
B exceptionResolvers(List<DataFetcherExceptionResolver> resolvers);
/**
* Add {@link GraphQLTypeVisitor}s to visit all element of the created
@@ -124,45 +98,81 @@ public interface GraphQlSource {
* {@link graphql.schema.SchemaTraverser} and cannot change the schema.
* @param typeVisitors the type visitors
* @return the current builder
* @see graphql.schema.SchemaTransformer#transformSchema(GraphQLSchema,
* GraphQLTypeVisitor)
* @see graphql.schema.SchemaTransformer#transformSchema(GraphQLSchema, GraphQLTypeVisitor)
*/
Builder typeVisitors(List<GraphQLTypeVisitor> typeVisitors);
B typeVisitors(List<GraphQLTypeVisitor> typeVisitors);
/**
* Provide {@link Instrumentation} components to instrument the execution of
* GraphQL queries.
* Provide {@link Instrumentation} components to instrument the
* execution of GraphQL queries.
* @param instrumentations the instrumentation components
* @return the current builder
* @see graphql.GraphQL.Builder#instrumentation(Instrumentation)
*/
Builder instrumentation(List<Instrumentation> instrumentations);
B instrumentation(List<Instrumentation> instrumentations);
/**
* Configure a function to create the {@link GraphQLSchema} instance from the
* given {@link TypeDefinitionRegistry} and {@link RuntimeWiring}. This may
* be useful for federation to create a combined schema.
* <p>By default, the schema is created with
* {@link graphql.schema.idl.SchemaGenerator#makeExecutableSchema}.
* @param schemaFactory the function to create the schema
* @return the current builder
*/
Builder schemaFactory(BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory);
/**
* Configure consumers to be given access to the {@link GraphQL.Builder} used to
* build {@link GraphQL}.
* Configure consumers to be given access to the {@link GraphQL.Builder}
* used to build {@link GraphQL}.
* @param configurer the configurer
* @return the current builder
*/
Builder configureGraphQl(Consumer<GraphQL.Builder> configurer);
B configureGraphQl(Consumer<GraphQL.Builder> configurer);
/**
* Build the {@link GraphQlSource}.
* @return the built GraphQlSource
* Build the {@link GraphQlSource} instance.
*/
GraphQlSource build();
}
/**
* {@link GraphQlSource} builder that relies on parsing schema definition
* files and uses a {@link RuntimeWiring} to create the underlying
* {@link GraphQLSchema}.
*/
interface SchemaResourceBuilder extends Builder<SchemaResourceBuilder> {
/**
* Add schema definition resources, typically {@literal ".graphqls"} files, to be
* {@link graphql.schema.idl.SchemaParser#parse(InputStream) parsed} and
* {@link TypeDefinitionRegistry#merge(TypeDefinitionRegistry) merged}.
* @param resources resources with GraphQL schema definitions
* @return the current builder
*/
SchemaResourceBuilder schemaResources(Resource... resources);
/**
* Configure the underlying {@link RuntimeWiring.Builder} to register
* data fetchers, custom scalar types, type resolvers, and more.
* @param configurer the configurer to apply
* @return the current builder
*/
SchemaResourceBuilder configureRuntimeWiring(RuntimeWiringConfigurer configurer);
/**
* Configure the default {@link TypeResolver} to use for GraphQL interface
* and union types that don't have such a registration after
* {@link #configureRuntimeWiring(RuntimeWiringConfigurer) applying}
* {@code RuntimeWiringConfigurer}s.
* <p>By default this is set to {@link ClassNameTypeResolver}.
* @param typeResolver the {@code TypeResolver} to use
* @return the current builder
*/
SchemaResourceBuilder defaultTypeResolver(TypeResolver typeResolver);
/**
* Configure a function to create the {@link GraphQLSchema} from the
* given {@link TypeDefinitionRegistry} and {@link RuntimeWiring}.
* This may be used for federation to create a combined schema.
* <p>By default, the schema is created with
* {@link graphql.schema.idl.SchemaGenerator#makeExecutableSchema}.
* @param schemaFactory the function to create the schema
* @return the current builder
*/
SchemaResourceBuilder schemaFactory(BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory);
}
}

View File

@@ -33,11 +33,11 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Unit tests for {@link DefaultGraphQlSourceBuilder}.
* Unit tests for {@link DefaultSchemaResourceGraphQlSourceBuilder}.
*
* @author Rossen Stoyanchev
*/
public class DefaultGraphQlSourceBuilderTests {
public class DefaultSchemaResourceGraphQlSourceBuilderTests {
@Test // gh-230
void duplicateResourcesAreIgnored() {

View File

@@ -51,7 +51,7 @@ import org.springframework.lang.Nullable;
@SuppressWarnings("unused")
public class GraphQlSetup implements GraphQlServiceSetup {
private final GraphQlSource.Builder graphQlSourceBuilder;
private final GraphQlSource.SchemaResourceBuilder graphQlSourceBuilder;
private final List<DataLoaderRegistrar> dataLoaderRegistrars = new ArrayList<>();
@@ -61,7 +61,7 @@ public class GraphQlSetup implements GraphQlServiceSetup {
private GraphQlSetup(Resource... schemaResources) {
this.graphQlSourceBuilder = GraphQlSource.builder().schemaResources(schemaResources);
this.graphQlSourceBuilder = GraphQlSource.schemaResourceBuilder().schemaResources(schemaResources);
}