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:
Rossen Stoyanchev
2021-04-28 17:32:35 +01:00
parent f3d941c0f4
commit e5a92a310d
15 changed files with 315 additions and 175 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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"));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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) {