Auto-configure Spring GraphQL base infrastructure
This commit adds the auto-configuration for setting up the base Spring GraphQL infrastructure. Because GraphQL doesn't depend on any particular transport, we must have a separate configuration for creating: * the `GraphQlSource`, which holds the schema and the `GraphQL` instance * the `GraphQlService` for executing incoming requests * the `BatchLoaderRegistry` for batch loading support * the `AnnotatedControllerConfigurer` for supporting the annotated controllers programming model This comes with a starting point for the `"spring.graphql.*"` configuration properties; we can now configure the locations and file extensions of GraphQL schema files we should load and configure at startup. See gh-29140pull/29177/head
parent
e5e157528b
commit
de808834f5
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2012-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.boot.autoconfigure.graphql;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
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.support.ResourcePatternResolver;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.execution.MissingSchemaException;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for creating a Spring GraphQL base
|
||||
* infrastructure.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ GraphQL.class, GraphQlSource.class })
|
||||
@EnableConfigurationProperties(GraphQlProperties.class)
|
||||
public class GraphQlAutoConfiguration {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GraphQlAutoConfiguration.class);
|
||||
|
||||
private final BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlSource graphQlSource(ResourcePatternResolver resourcePatternResolver, GraphQlProperties properties,
|
||||
ObjectProvider<DataFetcherExceptionResolver> exceptionResolversProvider,
|
||||
ObjectProvider<Instrumentation> instrumentationsProvider,
|
||||
ObjectProvider<RuntimeWiringConfigurer> wiringConfigurers,
|
||||
ObjectProvider<GraphQlSourceBuilderCustomizer> sourceCustomizers) {
|
||||
|
||||
List<Resource> schemaResources = resolveSchemaResources(resourcePatternResolver,
|
||||
properties.getSchema().getLocations(), properties.getSchema().getFileExtensions());
|
||||
GraphQlSource.Builder builder = GraphQlSource.builder()
|
||||
.schemaResources(schemaResources.toArray(new Resource[0]))
|
||||
.exceptionResolvers(exceptionResolversProvider.orderedStream().collect(Collectors.toList()))
|
||||
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
|
||||
wiringConfigurers.orderedStream().forEach(builder::configureRuntimeWiring);
|
||||
sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
try {
|
||||
return builder.build();
|
||||
}
|
||||
catch (MissingSchemaException exc) {
|
||||
throw new InvalidSchemaLocationsException(properties.getSchema().getLocations(), resourcePatternResolver,
|
||||
exc);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BatchLoaderRegistry batchLoaderRegistry() {
|
||||
return this.batchLoaderRegistry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public GraphQlService graphQlService(GraphQlSource graphQlSource) {
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
service.addDataLoaderRegistrar(this.batchLoaderRegistry);
|
||||
return service;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
|
||||
AnnotatedControllerConfigurer annotatedControllerConfigurer = new AnnotatedControllerConfigurer();
|
||||
annotatedControllerConfigurer.setConversionService(new DefaultFormattingConversionService());
|
||||
return annotatedControllerConfigurer;
|
||||
}
|
||||
|
||||
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, String[] schemaLocations,
|
||||
String[] fileExtensions) {
|
||||
List<Resource> schemaResources = new ArrayList<>();
|
||||
for (String location : schemaLocations) {
|
||||
for (String extension : fileExtensions) {
|
||||
String resourcePattern = location + "*" + extension;
|
||||
try {
|
||||
schemaResources.addAll(Arrays.asList(resolver.getResources(resourcePattern)));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.debug("Could not resolve schema location: '" + resourcePattern + "'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return schemaResources;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-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.boot.autoconfigure.graphql;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties properties} for Spring GraphQL.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.graphql")
|
||||
public class GraphQlProperties {
|
||||
|
||||
private final Schema schema = new Schema();
|
||||
|
||||
public Schema getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public static class Schema {
|
||||
|
||||
/**
|
||||
* Locations of GraphQL schema files.
|
||||
*/
|
||||
private String[] locations = new String[] { "classpath:graphql/**/" };
|
||||
|
||||
/**
|
||||
* File extensions for GraphQL schema files.
|
||||
*/
|
||||
private String[] fileExtensions = new String[] { ".graphqls", ".gqls" };
|
||||
|
||||
public String[] getLocations() {
|
||||
return this.locations;
|
||||
}
|
||||
|
||||
public void setLocations(String[] locations) {
|
||||
this.locations = appendSlashIfNecessary(locations);
|
||||
}
|
||||
|
||||
public String[] getFileExtensions() {
|
||||
return this.fileExtensions;
|
||||
}
|
||||
|
||||
public void setFileExtensions(String[] fileExtensions) {
|
||||
this.fileExtensions = fileExtensions;
|
||||
}
|
||||
|
||||
private String[] appendSlashIfNecessary(String[] locations) {
|
||||
return Arrays.stream(locations).map((location) -> location.endsWith("/") ? location : location + "/")
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-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.boot.autoconfigure.graphql;
|
||||
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize properties of
|
||||
* {@link org.springframework.graphql.execution.GraphQlSource.Builder} whilst retaining
|
||||
* default auto-configuration.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GraphQlSourceBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the {@link GraphQlSource.Builder} instance.
|
||||
* @param builder builder the builder to customize
|
||||
*/
|
||||
void customize(GraphQlSource.Builder builder);
|
||||
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.boot.autoconfigure.graphql;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.NestedRuntimeException;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link InvalidSchemaLocationsException} thrown when no schema file could be found in
|
||||
* the provided locations.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class InvalidSchemaLocationsException extends NestedRuntimeException {
|
||||
|
||||
private final List<SchemaLocation> schemaLocations;
|
||||
|
||||
public InvalidSchemaLocationsException(String[] locations, ResourcePatternResolver resolver) {
|
||||
this(locations, resolver, null);
|
||||
}
|
||||
|
||||
public InvalidSchemaLocationsException(String[] locations, ResourcePatternResolver resolver, Throwable cause) {
|
||||
super("No schema file could be found in the provided locations.", cause);
|
||||
Assert.notEmpty(locations, "locations should not be empty");
|
||||
Assert.notNull(resolver, "resolver should not be null");
|
||||
List<SchemaLocation> providedLocations = new ArrayList<>();
|
||||
for (String location : locations) {
|
||||
try {
|
||||
String uri = resolver.getResource(location).getURI().toASCIIString();
|
||||
providedLocations.add(new SchemaLocation(location, uri));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
providedLocations.add(new SchemaLocation(location, ""));
|
||||
}
|
||||
}
|
||||
this.schemaLocations = Collections.unmodifiableList(providedLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of provided locations where to look for schemas.
|
||||
* @return the list of locations
|
||||
*/
|
||||
public List<SchemaLocation> getSchemaLocations() {
|
||||
return this.schemaLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location where to look for schemas.
|
||||
*/
|
||||
public static class SchemaLocation {
|
||||
|
||||
private final String location;
|
||||
|
||||
private final String uri;
|
||||
|
||||
SchemaLocation(String location, String uri) {
|
||||
this.location = location;
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location String to be resolved by a {@link ResourcePatternResolver}.
|
||||
* @return the location
|
||||
*/
|
||||
public String getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resolved URI String for this location, an empty String if resolution
|
||||
* failed.
|
||||
* @return the resolved location or an empty String
|
||||
*/
|
||||
public String getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring GraphQL.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.graphql;
|
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2012-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.boot.autoconfigure.graphql;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.execution.instrumentation.ChainedInstrumentation;
|
||||
import graphql.execution.instrumentation.Instrumentation;
|
||||
import graphql.schema.GraphQLSchema;
|
||||
import graphql.schema.idl.RuntimeWiring;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
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.GraphQlService;
|
||||
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.execution.MissingSchemaException;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link GraphQlAutoConfiguration}.
|
||||
*/
|
||||
class GraphQlAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(GraphQlAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
void shouldContributeDefaultBeans() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQlSource.class);
|
||||
assertThat(context).hasSingleBean(BatchLoaderRegistry.class);
|
||||
assertThat(context).hasSingleBean(GraphQlService.class);
|
||||
assertThat(context).hasSingleBean(AnnotatedControllerConfigurer.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void schemaShouldScanNestedFolders() {
|
||||
this.contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQlSource.class);
|
||||
GraphQlSource graphQlSource = context.getBean(GraphQlSource.class);
|
||||
GraphQLSchema schema = graphQlSource.schema();
|
||||
assertThat(schema.getObjectType("Book")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailWhenSchemaFileIsMissing() {
|
||||
this.contextRunner.withPropertyValues("spring.graphql.schema.locations:classpath:missing/").run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(context).getFailure().getRootCause().isInstanceOf(MissingSchemaException.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUseProgrammaticallyDefinedBuilder() {
|
||||
this.contextRunner.withUserConfiguration(CustomGraphQlBuilderConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasBean("customGraphQlSourceBuilder");
|
||||
assertThat(context).hasSingleBean(GraphQlSource.Builder.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldScanLocationsWithCustomExtension() {
|
||||
this.contextRunner.withPropertyValues("spring.graphql.schema.file-extensions:.graphqls,.custom")
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(GraphQlSource.class);
|
||||
GraphQlSource graphQlSource = context.getBean(GraphQlSource.class);
|
||||
GraphQLSchema schema = graphQlSource.schema();
|
||||
assertThat(schema.getObjectType("Book")).isNotNull();
|
||||
assertThat(schema.getObjectType("Person")).isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBackOffWithCustomGraphQlSource() {
|
||||
this.contextRunner.withUserConfiguration(CustomGraphQlSourceConfiguration.class).run((context) -> {
|
||||
assertThat(context).getBeanNames(GraphQlSource.class).containsOnly("customGraphQlSource");
|
||||
assertThat(context).hasSingleBean(GraphQlProperties.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureDataFetcherExceptionResolvers() {
|
||||
this.contextRunner.withUserConfiguration(DataFetcherExceptionResolverConfiguration.class).run((context) -> {
|
||||
GraphQlSource graphQlSource = context.getBean(GraphQlSource.class);
|
||||
GraphQL graphQL = graphQlSource.graphQl();
|
||||
assertThat(graphQL.getQueryStrategy()).extracting("dataFetcherExceptionHandler")
|
||||
.satisfies((exceptionHandler) -> assertThat(exceptionHandler.getClass().getName())
|
||||
.endsWith("ExceptionResolversExceptionHandler"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldConfigureInstrumentation() {
|
||||
this.contextRunner.withUserConfiguration(InstrumentationConfiguration.class).run((context) -> {
|
||||
GraphQlSource graphQlSource = context.getBean(GraphQlSource.class);
|
||||
Instrumentation customInstrumentation = context.getBean("customInstrumentation", Instrumentation.class);
|
||||
GraphQL graphQL = graphQlSource.graphQl();
|
||||
assertThat(graphQL).extracting("instrumentation").isInstanceOf(ChainedInstrumentation.class)
|
||||
.extracting("instrumentations", InstanceOfAssertFactories.iterable(Instrumentation.class))
|
||||
.contains(customInstrumentation);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplyRuntimeWiringConfigurers() {
|
||||
this.contextRunner.withUserConfiguration(RuntimeWiringConfigurerConfiguration.class).run((context) -> {
|
||||
RuntimeWiringConfigurerConfiguration.CustomRuntimeWiringConfigurer configurer = context
|
||||
.getBean(RuntimeWiringConfigurerConfiguration.CustomRuntimeWiringConfigurer.class);
|
||||
assertThat(configurer.applied).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldApplyGraphQlSourceBuilderCustomizer() {
|
||||
this.contextRunner.withUserConfiguration(GraphQlSourceBuilderCustomizerConfiguration.class).run((context) -> {
|
||||
GraphQlSourceBuilderCustomizerConfiguration.CustomGraphQlSourceBuilderCustomizer customizer = context
|
||||
.getBean(GraphQlSourceBuilderCustomizerConfiguration.CustomGraphQlSourceBuilderCustomizer.class);
|
||||
assertThat(customizer.applied).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomGraphQlBuilderConfiguration {
|
||||
|
||||
@Bean
|
||||
GraphQlSource.Builder customGraphQlSourceBuilder() {
|
||||
return GraphQlSource.builder().schemaResources(new ClassPathResource("graphql/schema.graphqls"),
|
||||
new ClassPathResource("graphql/types/book.graphqls"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomGraphQlSourceConfiguration {
|
||||
|
||||
@Bean
|
||||
GraphQlSource customGraphQlSource() {
|
||||
return mock(GraphQlSource.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class DataFetcherExceptionResolverConfiguration {
|
||||
|
||||
@Bean
|
||||
DataFetcherExceptionResolver customDataFetcherExceptionResolver() {
|
||||
return mock(DataFetcherExceptionResolver.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class InstrumentationConfiguration {
|
||||
|
||||
@Bean
|
||||
Instrumentation customInstrumentation() {
|
||||
return mock(Instrumentation.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class RuntimeWiringConfigurerConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomRuntimeWiringConfigurer customRuntimeWiringConfigurer() {
|
||||
return new CustomRuntimeWiringConfigurer();
|
||||
}
|
||||
|
||||
public static class CustomRuntimeWiringConfigurer implements RuntimeWiringConfigurer {
|
||||
|
||||
public boolean applied = false;
|
||||
|
||||
@Override
|
||||
public void configure(RuntimeWiring.Builder builder) {
|
||||
this.applied = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class GraphQlSourceBuilderCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
CustomGraphQlSourceBuilderCustomizer customGraphQlSourceBuilderCustomizer() {
|
||||
return new CustomGraphQlSourceBuilderCustomizer();
|
||||
}
|
||||
|
||||
public static class CustomGraphQlSourceBuilderCustomizer implements GraphQlSourceBuilderCustomizer {
|
||||
|
||||
public boolean applied = false;
|
||||
|
||||
@Override
|
||||
public void customize(GraphQlSource.Builder builder) {
|
||||
this.applied = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2012-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.boot.autoconfigure.graphql;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link InvalidSchemaLocationsException}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
class InvalidSchemaLocationsExceptionTests {
|
||||
|
||||
private final String schemaFolder = "graphql/";
|
||||
|
||||
private final String[] locations = new String[] { "classpath:" + this.schemaFolder };
|
||||
|
||||
@Test
|
||||
void shouldRejectEmptyLocations() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new InvalidSchemaLocationsException(new String[] {}, new PathMatchingResourcePatternResolver()))
|
||||
.isInstanceOf(IllegalArgumentException.class).withMessage("locations should not be empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectNullResolver() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new InvalidSchemaLocationsException(this.locations, null))
|
||||
.isInstanceOf(IllegalArgumentException.class).withMessage("resolver should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeConfiguredLocations() {
|
||||
InvalidSchemaLocationsException exception = new InvalidSchemaLocationsException(this.locations,
|
||||
new PathMatchingResourcePatternResolver());
|
||||
assertThat(exception.getSchemaLocations()).hasSize(1);
|
||||
InvalidSchemaLocationsException.SchemaLocation schemaLocation = exception.getSchemaLocations().get(0);
|
||||
assertThat(schemaLocation.getLocation()).isEqualTo(this.locations[0]);
|
||||
assertThat(schemaLocation.getUri()).endsWith(this.schemaFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotFailWithUnresolvableLocations() {
|
||||
String unresolved = "classpath:unresolved/";
|
||||
InvalidSchemaLocationsException exception = new InvalidSchemaLocationsException(new String[] { unresolved },
|
||||
new PathMatchingResourcePatternResolver());
|
||||
assertThat(exception.getSchemaLocations()).hasSize(1);
|
||||
InvalidSchemaLocationsException.SchemaLocation schemaLocation = exception.getSchemaLocations().get(0);
|
||||
assertThat(schemaLocation.getLocation()).isEqualTo(unresolved);
|
||||
assertThat(schemaLocation.getUri()).isEmpty();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
type Query {
|
||||
greeting(name: String! = "Spring"): String!
|
||||
bookById(id: ID): Book
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
type Book {
|
||||
id: ID
|
||||
name: String
|
||||
pageCount: Int
|
||||
author: String
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
type Person {
|
||||
id: ID
|
||||
name: String
|
||||
}
|
Loading…
Reference in New Issue