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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user