Introduce GraphQLSource
A pluggable abstraction to encapsulate the way GraphQL is initialized and the way it is sourced at runtime. Closes gh-48
This commit is contained in:
@@ -15,19 +15,11 @@
|
||||
*/
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.SchemaTransformer;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.SchemaGenerator;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -35,31 +27,23 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.graphql.data.ReactorDataFetcherAdapter;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnMissingBean(GraphQL.class)
|
||||
@ConditionalOnMissingBean(GraphQLSource.class)
|
||||
@EnableConfigurationProperties(GraphQLProperties.class)
|
||||
public class GraphQLAutoConfiguration {
|
||||
|
||||
|
||||
@Configuration
|
||||
static class GraphQLConfiguration {
|
||||
|
||||
@Bean
|
||||
public GraphQL graphQL(GraphQL.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQLSource graphQLSource(GraphQLSource.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(GraphQL.Builder.class)
|
||||
static class GraphQLBuilderConfiguration {
|
||||
@ConditionalOnMissingBean(GraphQLSource.Builder.class)
|
||||
static class GraphQLSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@@ -70,36 +54,17 @@ public class GraphQLAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQL.Builder graphQLBuilder(GraphQLProperties properties, RuntimeWiring runtimeWiring,
|
||||
ResourceLoader resourceLoader,
|
||||
ObjectProvider<Instrumentation> instrumentationsProvider) {
|
||||
public GraphQLSource.Builder graphQLSourceBuilder(
|
||||
GraphQLProperties properties, RuntimeWiring runtimeWiring,
|
||||
ResourceLoader resourceLoader, ObjectProvider<Instrumentation> instrumentationsProvider) {
|
||||
|
||||
Resource schemaResource = resourceLoader.getResource(properties.getSchemaLocation());
|
||||
GraphQLSchema schema = buildSchema(schemaResource, runtimeWiring);
|
||||
schema = SchemaTransformer.transformSchema(schema, ReactorDataFetcherAdapter.TYPE_VISITOR);
|
||||
String schemaLocation = properties.getSchemaLocation();
|
||||
|
||||
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
|
||||
List<Instrumentation> instrumentations = instrumentationsProvider.orderedStream().collect(Collectors.toList());
|
||||
if (!instrumentations.isEmpty()) {
|
||||
builder = builder.instrumentation(new ChainedInstrumentation(instrumentations));
|
||||
}
|
||||
return builder;
|
||||
return GraphQLSource.builder()
|
||||
.schemaResource(resourceLoader.getResource(schemaLocation))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private GraphQLSchema buildSchema(Resource schemaResource, RuntimeWiring runtimeWiring) {
|
||||
if (!schemaResource.exists()) {
|
||||
throw new MissingGraphQLSchemaException(schemaResource);
|
||||
}
|
||||
try {
|
||||
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaResource.getInputStream());
|
||||
SchemaGenerator schemaGenerator = new SchemaGenerator();
|
||||
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
|
||||
}
|
||||
catch (IOException exc) {
|
||||
throw new MissingGraphQLSchemaException(exc, schemaResource);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.boot;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Exception thrown when no GraphQL schema is available.
|
||||
*/
|
||||
public class MissingGraphQLSchemaException extends RuntimeException {
|
||||
|
||||
private final Resource schemaResource;
|
||||
|
||||
public MissingGraphQLSchemaException(Throwable cause, Resource schemaResource) {
|
||||
super(cause);
|
||||
this.schemaResource = schemaResource;
|
||||
}
|
||||
|
||||
MissingGraphQLSchemaException(Resource schemaResource) {
|
||||
super("Schema resource " + schemaResource.toString() + " does not exist");
|
||||
this.schemaResource = schemaResource;
|
||||
}
|
||||
|
||||
public Resource getSchemaResource() {
|
||||
return this.schemaResource;
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
import org.springframework.graphql.web.DefaultWebGraphQLService;
|
||||
import org.springframework.graphql.web.WebGraphQLService;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
@@ -54,7 +55,7 @@ import static org.springframework.web.reactive.function.server.RequestPredicates
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQLSource.class)
|
||||
@AutoConfigureAfter(GraphQLAutoConfiguration.class)
|
||||
public class WebFluxGraphQLAutoConfiguration {
|
||||
|
||||
@@ -63,8 +64,8 @@ public class WebFluxGraphQLAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQLService webGraphQLService(GraphQL graphQL, ObjectProvider<WebInterceptor> interceptors) {
|
||||
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQL);
|
||||
public WebGraphQLService webGraphQLService(GraphQLSource graphQLSource, ObjectProvider<WebInterceptor> interceptors) {
|
||||
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQLSource);
|
||||
handler.setInterceptors(interceptors.orderedStream().collect(Collectors.toList()));
|
||||
return handler;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
import org.springframework.graphql.web.DefaultWebGraphQLService;
|
||||
import org.springframework.graphql.web.WebGraphQLService;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
@@ -61,7 +62,7 @@ import static org.springframework.web.servlet.function.RequestPredicates.content
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@ConditionalOnClass(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQL.class)
|
||||
@ConditionalOnBean(GraphQLSource.class)
|
||||
@AutoConfigureAfter(GraphQLAutoConfiguration.class)
|
||||
public class WebMvcGraphQLAutoConfiguration {
|
||||
|
||||
@@ -70,8 +71,8 @@ public class WebMvcGraphQLAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebGraphQLService webGraphQLService(GraphQL graphQL, ObjectProvider<WebInterceptor> interceptors) {
|
||||
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQL);
|
||||
public WebGraphQLService webGraphQLService(GraphQLSource graphQLSource, ObjectProvider<WebInterceptor> interceptors) {
|
||||
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQLSource);
|
||||
handler.setInterceptors(interceptors.orderedStream().collect(Collectors.toList()));
|
||||
return handler;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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,19 +16,15 @@
|
||||
|
||||
package org.springframework.graphql.boot;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.GraphQLObjectType;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
|
||||
import static graphql.Scalars.GraphQLString;
|
||||
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
|
||||
import static graphql.schema.GraphQLObjectType.newObject;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
@@ -44,7 +40,7 @@ class GraphQLAutoConfigurationTests {
|
||||
void shouldFailWhenSchemaFileIsMissing() {
|
||||
contextRunner.run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context).getFailure().getRootCause().isInstanceOf(MissingGraphQLSchemaException.class);
|
||||
assertThat(context).getFailure().getRootCause().hasMessage("'schemaResource' does not exist");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,9 +48,7 @@ class GraphQLAutoConfigurationTests {
|
||||
void shouldCreateBuilderWithSdl() {
|
||||
contextRunner
|
||||
.withPropertyValues("spring.graphql.schema-location:classpath:books/schema.graphqls")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQL.Builder.class);
|
||||
});
|
||||
.run((context) -> assertThat(context).hasSingleBean(GraphQLSource.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,8 +57,8 @@ class GraphQLAutoConfigurationTests {
|
||||
.withPropertyValues("spring.graphql.schema-location:classpath:books/schema.graphqls")
|
||||
.withUserConfiguration(CustomGraphQLBuilderConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasBean("customGraphQLBuilder");
|
||||
assertThat(context).hasSingleBean(GraphQL.Builder.class);
|
||||
assertThat(context).hasBean("customGraphQLSourceBuilder");
|
||||
assertThat(context).hasSingleBean(GraphQLSource.Builder.class);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,15 +66,8 @@ class GraphQLAutoConfigurationTests {
|
||||
static class CustomGraphQLBuilderConfiguration {
|
||||
|
||||
@Bean
|
||||
public GraphQL.Builder customGraphQLBuilder() {
|
||||
GraphQLObjectType queryType = newObject()
|
||||
.name("helloWorldQuery")
|
||||
.field(newFieldDefinition()
|
||||
.type(GraphQLString)
|
||||
.name("hello"))
|
||||
.build();
|
||||
GraphQLSchema schema = GraphQLSchema.newSchema().query(queryType).build();
|
||||
return GraphQL.newGraphQL(schema);
|
||||
public GraphQLSource.Builder customGraphQLSourceBuilder() {
|
||||
return GraphQLSource.builder().schemaResource(new ClassPathResource("books/schema.graphqls"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.GraphQLTypeVisitor;
|
||||
import graphql.schema.SchemaTransformer;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.SchemaGenerator;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
|
||||
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.
|
||||
*/
|
||||
class DefaultGraphQLSourceBuilder implements GraphQLSource.Builder {
|
||||
|
||||
@Nullable
|
||||
private Resource schemaResource;
|
||||
|
||||
private RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring().build();
|
||||
|
||||
private final List<GraphQLTypeVisitor> typeVisitors = new ArrayList<>();
|
||||
|
||||
private final List<Instrumentation> instrumentations = new ArrayList<>();
|
||||
|
||||
private Consumer<GraphQL.Builder> graphQLConfigurers = builder -> {};
|
||||
|
||||
|
||||
DefaultGraphQLSourceBuilder() {
|
||||
this.typeVisitors.add(ReactorDataFetcherAdapter.TYPE_VISITOR);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GraphQLSource.Builder schemaResource(Resource resource) {
|
||||
this.schemaResource = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQLSource.Builder runtimeWiring(RuntimeWiring runtimeWiring) {
|
||||
Assert.notNull(runtimeWiring, "RuntimeWiring is required");
|
||||
this.runtimeWiring = runtimeWiring;
|
||||
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 configureGraphQL(Consumer<GraphQL.Builder> configurer) {
|
||||
this.graphQLConfigurers = this.graphQLConfigurers.andThen(configurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQLSource build() {
|
||||
TypeDefinitionRegistry registry = parseSchemaResource();
|
||||
|
||||
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(registry, this.runtimeWiring);
|
||||
for (GraphQLTypeVisitor visitor : this.typeVisitors) {
|
||||
schema = SchemaTransformer.transformSchema(schema, visitor);
|
||||
}
|
||||
|
||||
GraphQL.Builder builder = GraphQL.newGraphQL(schema);
|
||||
if (!this.instrumentations.isEmpty()) {
|
||||
builder = builder.instrumentation(new ChainedInstrumentation(this.instrumentations));
|
||||
}
|
||||
this.graphQLConfigurers.accept(builder);
|
||||
GraphQL graphQL = builder.build();
|
||||
|
||||
return new CachedGraphQLSource(graphQL);
|
||||
}
|
||||
|
||||
private TypeDefinitionRegistry parseSchemaResource() {
|
||||
Assert.notNull(this.schemaResource, "'schemaResource' not provided");
|
||||
Assert.isTrue(this.schemaResource.exists(), "'schemaResource' does not exist");
|
||||
try {
|
||||
try (InputStream inputStream = this.schemaResource.getInputStream()) {
|
||||
return new SchemaParser().parse(inputStream);
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Failed to load resourceLocation " + this.schemaResource.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* GraphQLSource that returns the built GraphQL instance.
|
||||
*/
|
||||
private static class CachedGraphQLSource implements GraphQLSource {
|
||||
|
||||
private final GraphQL graphQL;
|
||||
|
||||
CachedGraphQLSource(GraphQL graphQL) {
|
||||
this.graphQL = graphQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphQL graphQL() {
|
||||
return this.graphQL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.support;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.GraphQLTypeVisitor;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Strategy to resolve the {@link GraphQL} instance to use.
|
||||
*
|
||||
* <p>This contract also includes a {@link GraphQLSource} builder encapsulating
|
||||
* the initialization of the {@link GraphQL} instance and associated
|
||||
* {@link graphql.schema.GraphQLSchema}.
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
GraphQL graphQL();
|
||||
|
||||
|
||||
/**
|
||||
* Return a builder for a {@link GraphQLSource} given input for the
|
||||
* initialization of {@link GraphQL} and {@link graphql.schema.GraphQLSchema}.
|
||||
*/
|
||||
static Builder builder() {
|
||||
return new DefaultGraphQLSourceBuilder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for a {@link GraphQLSource}.
|
||||
*/
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Provide the resource for the GraphQL {@literal ".schema"} file to parse.
|
||||
* @see graphql.schema.idl.SchemaParser#parse(File)
|
||||
*/
|
||||
Builder schemaResource(Resource resource);
|
||||
|
||||
/**
|
||||
* Set a {@link RuntimeWiring} to contribute data fetchers and more.
|
||||
* @see graphql.schema.idl.SchemaGenerator#makeExecutableSchema(TypeDefinitionRegistry, RuntimeWiring)
|
||||
*/
|
||||
Builder runtimeWiring(RuntimeWiring runtimeWiring);
|
||||
|
||||
/**
|
||||
* Add {@link GraphQLTypeVisitor}s to transform the underlying
|
||||
* {@link graphql.schema.GraphQLSchema} with.
|
||||
* @see graphql.schema.SchemaTransformer#transformSchema(GraphQLSchema, GraphQLTypeVisitor)
|
||||
*/
|
||||
Builder typeVisitors(List<GraphQLTypeVisitor> typeVisitors);
|
||||
|
||||
/**
|
||||
* Provide {@link Instrumentation} components to instrument the execution
|
||||
* of GraphQL queries.
|
||||
* @see graphql.GraphQL.Builder#instrumentation(Instrumentation)
|
||||
*/
|
||||
Builder instrumentation(List<Instrumentation> instrumentations);
|
||||
|
||||
/**
|
||||
* Configure consumers to be given access to the {@link GraphQL.Builder}
|
||||
* used to build {@link GraphQL}.
|
||||
*/
|
||||
Builder configureGraphQL(Consumer<GraphQL.Builder> configurer);
|
||||
|
||||
/**
|
||||
* Build the {@link GraphQLSource}.
|
||||
*/
|
||||
GraphQLSource build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql.data;
|
||||
package org.springframework.graphql.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@@ -34,6 +34,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -87,6 +88,7 @@ public class ReactorDataFetcherAdapter implements DataFetcher<Object> {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ContextView getReactorContext(DataFetchingEnvironment environment) {
|
||||
GraphQLContext graphQLContext = environment.getContext();
|
||||
return graphQLContext.get(REACTOR_CONTEXT_KEY);
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020-2020 the original author or authors.
|
||||
* Copyright 2020-2021 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.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
@NonNullApi
|
||||
@NonNullFields
|
||||
package org.springframework.graphql.data;
|
||||
package org.springframework.graphql.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
import org.springframework.lang.NonNullFields;
|
||||
@@ -23,7 +23,7 @@ import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.data.ReactorDataFetcherAdapter;
|
||||
import org.springframework.graphql.support.ReactorDataFetcherAdapter;
|
||||
|
||||
/**
|
||||
* Base class for {@link WebGraphQLService} implementations, providing support
|
||||
|
||||
@@ -21,23 +21,25 @@ import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
|
||||
/**
|
||||
* Extension of {@link AbstractWebGraphQLService} that executes GraphQL queries
|
||||
* through the {@link GraphQL} instance it is configured with.
|
||||
*/
|
||||
public class DefaultWebGraphQLService extends AbstractWebGraphQLService {
|
||||
|
||||
private final GraphQL graphQL;
|
||||
private final GraphQLSource graphQLSource;
|
||||
|
||||
|
||||
public DefaultWebGraphQLService(GraphQL graphQL) {
|
||||
this.graphQL = graphQL;
|
||||
public DefaultWebGraphQLService(GraphQLSource graphQLSource) {
|
||||
this.graphQLSource = graphQLSource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<ExecutionResult> executeInternal(ExecutionInput input) {
|
||||
return this.graphQL.executeAsync(input);
|
||||
return this.graphQLSource.graphQL().executeAsync(input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql.data;
|
||||
package org.springframework.graphql.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.graphql.web;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
@@ -24,17 +23,13 @@ import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.SchemaGenerator;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -64,7 +59,7 @@ public class DefaultWebGraphQLServiceTests {
|
||||
Map body = mapper.reader().readValue("{\"query\": \"" + query + "\"}", Map.class);
|
||||
WebInput webInput = new WebInput(URI.create("/graphql"), new HttpHeaders(), body, "1");
|
||||
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(createGraphQL());
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(createGraphQLSource());
|
||||
requestHandler.setInterceptors(interceptors);
|
||||
|
||||
WebOutput webOutput = requestHandler.execute(webInput).block();
|
||||
@@ -75,17 +70,15 @@ public class DefaultWebGraphQLServiceTests {
|
||||
}
|
||||
|
||||
|
||||
private static GraphQL createGraphQL() throws Exception {
|
||||
private static GraphQLSource createGraphQLSource() {
|
||||
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
|
||||
.type(newTypeWiring("Query").dataFetcher("bookById", GraphQLDataFetchers.getBookByIdDataFetcher()))
|
||||
.build();
|
||||
|
||||
File file = ResourceUtils.getFile("classpath:books/schema.graphqls");
|
||||
TypeDefinitionRegistry registry = new SchemaParser().parse(file);
|
||||
SchemaGenerator generator = new SchemaGenerator();
|
||||
GraphQLSchema schema = generator.makeExecutableSchema(registry, runtimeWiring);
|
||||
|
||||
return GraphQL.newGraphQL(schema).build();
|
||||
return GraphQLSource.builder()
|
||||
.schemaResource(new ClassPathResource("books/schema.graphqls"))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.graphql.web.webflux;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
@@ -25,12 +24,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.SchemaGenerator;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -38,9 +32,11 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
import org.springframework.graphql.web.ConsumeOneAndNeverCompleteInterceptor;
|
||||
import org.springframework.graphql.web.DefaultWebGraphQLService;
|
||||
import org.springframework.graphql.web.GraphQLDataFetchers;
|
||||
@@ -49,7 +45,6 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.reactive.socket.CloseStatus;
|
||||
import org.springframework.web.reactive.socket.HandshakeInfo;
|
||||
import org.springframework.web.reactive.socket.WebSocketMessage;
|
||||
@@ -278,9 +273,7 @@ public class GraphQLWebSocketHandlerTests {
|
||||
private GraphQLWebSocketHandler initWebSocketHandler(
|
||||
@Nullable List<WebInterceptor> interceptors, @Nullable Duration initTimeoutDuration) throws Exception {
|
||||
|
||||
GraphQL graphQL = initGraphQL();
|
||||
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(graphQL);
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(initGraphQLSource());
|
||||
if (interceptors != null) {
|
||||
requestHandler.setInterceptors(interceptors);
|
||||
}
|
||||
@@ -290,17 +283,16 @@ public class GraphQLWebSocketHandlerTests {
|
||||
(initTimeoutDuration != null ? initTimeoutDuration : Duration.ofSeconds(60)));
|
||||
}
|
||||
|
||||
private static GraphQL initGraphQL() throws Exception {
|
||||
File schemaFile = ResourceUtils.getFile("classpath:books/schema.graphqls");
|
||||
TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(schemaFile);
|
||||
|
||||
private static GraphQLSource initGraphQLSource() {
|
||||
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
|
||||
builder.type(newTypeWiring("Query").dataFetcher("bookById", GraphQLDataFetchers.getBookByIdDataFetcher()));
|
||||
builder.type(newTypeWiring("Subscription").dataFetcher("bookSearch", GraphQLDataFetchers.getBooksOnSale()));
|
||||
RuntimeWiring runtimeWiring = builder.build();
|
||||
|
||||
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
|
||||
return GraphQL.newGraphQL(schema).build();
|
||||
return GraphQLSource.builder()
|
||||
.schemaResource(new ClassPathResource("books/schema.graphqls"))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static WebSocketMessage toWebSocketMessage(String data) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.graphql.web.webmvc;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
@@ -27,15 +26,12 @@ import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import graphql.schema.idl.SchemaGenerator;
|
||||
import graphql.schema.idl.SchemaParser;
|
||||
import graphql.schema.idl.TypeDefinitionRegistry;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.graphql.support.GraphQLSource;
|
||||
import org.springframework.graphql.web.ConsumeOneAndNeverCompleteInterceptor;
|
||||
import org.springframework.graphql.web.DefaultWebGraphQLService;
|
||||
import org.springframework.graphql.web.GraphQLDataFetchers;
|
||||
@@ -45,7 +41,6 @@ import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketMessage;
|
||||
@@ -262,9 +257,7 @@ public class GraphQLWebSocketHandlerTests {
|
||||
@Nullable List<WebInterceptor> interceptors, @Nullable Duration initTimeoutDuration) {
|
||||
|
||||
try {
|
||||
GraphQL graphQL = initGraphQL();
|
||||
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(graphQL);
|
||||
DefaultWebGraphQLService requestHandler = new DefaultWebGraphQLService(initGraphQLSource());
|
||||
if (interceptors != null) {
|
||||
requestHandler.setInterceptors(interceptors);
|
||||
}
|
||||
@@ -277,17 +270,16 @@ public class GraphQLWebSocketHandlerTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static GraphQL initGraphQL() throws Exception {
|
||||
File schemaFile = ResourceUtils.getFile("classpath:books/schema.graphqls");
|
||||
TypeDefinitionRegistry typeDefinitionRegistry = new SchemaParser().parse(schemaFile);
|
||||
|
||||
private static GraphQLSource initGraphQLSource() throws Exception {
|
||||
RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring();
|
||||
builder.type(newTypeWiring("Query").dataFetcher("bookById", GraphQLDataFetchers.getBookByIdDataFetcher()));
|
||||
builder.type(newTypeWiring("Subscription").dataFetcher("bookSearch", GraphQLDataFetchers.getBooksOnSale()));
|
||||
RuntimeWiring runtimeWiring = builder.build();
|
||||
|
||||
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeDefinitionRegistry, runtimeWiring);
|
||||
return GraphQL.newGraphQL(schema).build();
|
||||
return GraphQLSource.builder()
|
||||
.schemaResource(new ClassPathResource("books/schema.graphqls"))
|
||||
.runtimeWiring(runtimeWiring)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void assertMessageType(WebSocketMessage<?> message, String messageType) {
|
||||
|
||||
Reference in New Issue
Block a user