Allow configuring schema through GraphQlSource customizer

As of gh-117, the GraphQlAutoConfiguration throws an exception if no
schema file was found while scanning configured locations.
This prevents developers from manually configuring the schema using
`GraphQlSourceBuilderCustomizer` instances.

This commit ensures that a `MissingSchemaException` is only raised if no
schema was configured at the time we're building the actual
GraphQlSource.

This exception is later wrapped by the GraphQlAutoConfiguration to raise
an `InvalidSchemaLocationsException` with useful information about
configured schema locations.

Fixes gh-124
This commit is contained in:
Brian Clozel
2021-09-02 11:10:06 +02:00
parent 771c42bfbe
commit 0587913ccb
7 changed files with 56 additions and 13 deletions

View File

@@ -38,6 +38,7 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.execution.MissingSchemaException;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
/**
@@ -71,7 +72,12 @@ public class GraphQlAutoConfiguration {
.instrumentation(instrumentationsProvider.orderedStream().collect(Collectors.toList()));
wiringConfigurers.orderedStream().forEach(builder::configureRuntimeWiring);
sourceCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
return builder.build();
try {
return builder.build();
}
catch (MissingSchemaException exc) {
throw new InvalidSchemaLocationsException(properties.getSchema().getLocations(), resourcePatternResolver, exc);
}
}
private List<Resource> resolveSchemaResources(ResourcePatternResolver resolver, List<String> schemaLocations) throws IOException {
@@ -86,9 +92,6 @@ public class GraphQlAutoConfiguration {
}
}
}
if (schemaResources.isEmpty()) {
throw new MissingSchemaException(schemaLocations, resolver);
}
return schemaResources;
}

View File

@@ -21,18 +21,25 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.core.NestedRuntimeException;
import org.springframework.core.io.support.ResourcePatternResolver;
/**
* {@link MissingSchemaException} thrown when no schema can be found in the provided locations.
* {@link InvalidSchemaLocationsException} thrown when no schema file could be found in the provided locations.
*
* @author Brian Clozel
* @since 1.0.0
*/
public class MissingSchemaException extends RuntimeException {
public class InvalidSchemaLocationsException extends NestedRuntimeException {
private final List<SchemaLocation> schemaLocations;
public MissingSchemaException(List<String> locations, ResourcePatternResolver resolver) {
public InvalidSchemaLocationsException(List<String> locations, ResourcePatternResolver resolver) {
this(locations, resolver, null);
}
public InvalidSchemaLocationsException(List<String> locations, ResourcePatternResolver resolver, Throwable cause) {
super("No schema file could be found in the provided locations.", cause);
List<SchemaLocation> providedLocations = new ArrayList<>();
for (String location : locations) {
try {

View File

@@ -21,17 +21,17 @@ import org.springframework.boot.diagnostics.FailureAnalysis;
/**
* An implementation of {@link AbstractFailureAnalyzer} to analyze failures caused by
* {@link MissingSchemaException}.
* {@link InvalidSchemaLocationsException}.
*
* @author Brian Clozel
*/
class MissingSchemaExceptionFailureAnalyzer extends AbstractFailureAnalyzer<MissingSchemaException> {
class InvalidSchemaLocationsExceptionFailureAnalyzer extends AbstractFailureAnalyzer<InvalidSchemaLocationsException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, MissingSchemaException cause) {
protected FailureAnalysis analyze(Throwable rootFailure, InvalidSchemaLocationsException cause) {
String message = "Could not find any GraphQL schema file under configured locations.";
StringBuilder action = new StringBuilder("Check that the following locations contain schema files: " + System.lineSeparator());
for (MissingSchemaException.SchemaLocation schemaLocation : cause.getSchemaLocations()) {
for (InvalidSchemaLocationsException.SchemaLocation schemaLocation : cause.getSchemaLocations()) {
action.append(String.format("- '%s' (%s)" + System.lineSeparator(), schemaLocation.getUri(), schemaLocation.getLocation()));
}
return new FailureAnalysis(message, action.toString(), cause);

View File

@@ -11,7 +11,7 @@ org.springframework.graphql.boot.data.GraphQlWebFluxQuerydslAutoConfiguration
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.graphql.boot.MissingSchemaExceptionFailureAnalyzer
org.springframework.graphql.boot.InvalidSchemaLocationsExceptionFailureAnalyzer
# Spring Test @AutoConfigureGraphQlTester
org.springframework.graphql.boot.test.tester.AutoConfigureGraphQlTester=\

View File

@@ -25,6 +25,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.graphql.execution.GraphQlSource;
import org.springframework.graphql.execution.MissingSchemaException;
import static org.assertj.core.api.Assertions.assertThat;

View File

@@ -103,7 +103,7 @@ class DefaultGraphQlSourceBuilder implements GraphQlSource.Builder {
public GraphQlSource build() {
TypeDefinitionRegistry registry = this.schemaResources.stream()
.map(this::parseSchemaResource).reduce(TypeDefinitionRegistry::merge)
.orElseThrow(() -> new IllegalArgumentException("'schemaResources' should not be empty"));
.orElseThrow(MissingSchemaException::new);
RuntimeWiring.Builder runtimeWiringBuilder = RuntimeWiring.newRuntimeWiring();
this.runtimeWiringConfigurers.forEach(configurer -> configurer.configure(runtimeWiringBuilder));

View File

@@ -0,0 +1,32 @@
/*
* 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.graphql.execution;
import org.springframework.core.NestedRuntimeException;
/**
* Indicates that no GraphQL schema definition was configured on the {@link GraphQlSource.Builder}.
*
* @author Brian Clozel
* @since 1.0.0
*/
public class MissingSchemaException extends NestedRuntimeException {
public MissingSchemaException() {
super("No GraphQL schema definition was configured.");
}
}