diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeGenerator.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurer.java similarity index 80% rename from spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeGenerator.java rename to spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurer.java index 6e2e1334..4d22fbcb 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeGenerator.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurer.java @@ -17,7 +17,6 @@ package org.springframework.graphql.execution; import java.util.LinkedHashSet; import java.util.Set; -import java.util.function.Function; import java.util.stream.Collectors; import graphql.language.FieldDefinition; @@ -30,18 +29,20 @@ import graphql.language.TypeName; import graphql.schema.idl.TypeDefinitionRegistry; /** - * Exposes the {@link #generateConnectionTypes(TypeDefinitionRegistry) - * generateConnectionTypes method} for adding boilerplate type definitions to a - * {@link TypeDefinitionRegistry}, for pagination based on the Relay - * GraphQL Cursor Connections Specification. + * {@link TypeDefinitionConfigurer} that generates "Connection" types by looking + * for fields whose type definition name ends in "Connection", considered by the + * GraphQL Cursor Connections Specification + * to be a {@literal Connection Type}, and adding the required type definitions + * if they don't already exist. * - *

Use {@link GraphQlSource.SchemaResourceBuilder#configureTypeDefinitionRegistry(Function)} - * to enable connection type generation. + *

This is intended to be set on + * {@link GraphQlSource.SchemaResourceBuilder#configureTypeDefinitions(TypeDefinitionConfigurer) + * GraphQlSource.Builder}. * * @author Rossen Stoyanchev * @since 1.2 */ -public class ConnectionTypeGenerator { +public class ConnectionTypeDefinitionConfigurer implements TypeDefinitionConfigurer { private static final TypeName STRING_TYPE = new TypeName("String"); @@ -50,14 +51,8 @@ public class ConnectionTypeGenerator { private static final TypeName PAGE_INFO_TYPE = new TypeName("PageInfo"); - /** - * Find fields whose type definition name ends in "Connection", considered - * by the spec to be a {@literal Connection Type}, and add type definitions - * for all such types, if they don't exist already. - * @param registry the registry to check and add types to - * @return the same registry instance with additional types added - */ - public TypeDefinitionRegistry generateConnectionTypes(TypeDefinitionRegistry registry) { + @Override + public void configure(TypeDefinitionRegistry registry) { Set typeNames = findConnectionTypeNames(registry); @@ -89,8 +84,6 @@ public class ConnectionTypeGenerator { .build()); }); } - - return registry; } private static Set findConnectionTypeNames(TypeDefinitionRegistry registry) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java index 9c67a078..96a38e0d 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java @@ -24,7 +24,6 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.function.BiFunction; -import java.util.function.Function; import java.util.stream.Collectors; import graphql.language.InterfaceTypeDefinition; @@ -61,8 +60,7 @@ final class DefaultSchemaResourceGraphQlSourceBuilder private final Set schemaResources = new LinkedHashSet<>(); - @Nullable - private Function typeDefinitionRegistryConfigurer; + private List typeDefinitionConfigurers = new ArrayList<>(); private final List runtimeWiringConfigurers = new ArrayList<>(); @@ -81,12 +79,8 @@ final class DefaultSchemaResourceGraphQlSourceBuilder } @Override - public GraphQlSource.SchemaResourceBuilder configureTypeDefinitionRegistry( - Function configurer) { - - this.typeDefinitionRegistryConfigurer = (this.typeDefinitionRegistryConfigurer != null ? - this.typeDefinitionRegistryConfigurer.andThen(configurer) : configurer); - + public GraphQlSource.SchemaResourceBuilder configureTypeDefinitions(TypeDefinitionConfigurer configurer) { + this.typeDefinitionConfigurers.add(configurer); return this; } @@ -118,8 +112,8 @@ final class DefaultSchemaResourceGraphQlSourceBuilder .reduce(TypeDefinitionRegistry::merge) .orElseThrow(MissingSchemaException::new); - if (this.typeDefinitionRegistryConfigurer != null) { - registry = this.typeDefinitionRegistryConfigurer.apply(registry); + for (TypeDefinitionConfigurer configurer : this.typeDefinitionConfigurers) { + configurer.configure(registry); } logger.info("Loaded " + this.schemaResources.size() + " resource(s) in the GraphQL schema."); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/GraphQlSource.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/GraphQlSource.java index f1db320f..8718f5bb 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/GraphQlSource.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/GraphQlSource.java @@ -173,17 +173,14 @@ public interface GraphQlSource { SchemaResourceBuilder schemaResources(Resource... resources); /** - * Provide a function to customize the {@link TypeDefinitionRegistry} - * created by parsing schema files. This allows adding or changing schema - * type definitions before {@link GraphQLSchema} is created and validated. - * @param configurer the function to apply accepting the current - * {@link TypeDefinitionRegistry} and returning the one to use, likely - * the same instance since {@link TypeDefinitionRegistry} is mutable. + * Customize the {@link TypeDefinitionRegistry} created from parsed + * schema files, adding or changing schema type definitions before the + * {@link GraphQLSchema} is created and validated. + * @param configurer the configurer to apply * @return the current builder - * @sine 1.2 + * @since 1.2 */ - SchemaResourceBuilder configureTypeDefinitionRegistry( - Function configurer); + SchemaResourceBuilder configureTypeDefinitions(TypeDefinitionConfigurer configurer); /** * Configure the underlying {@link RuntimeWiring.Builder} to register diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/TypeDefinitionConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/TypeDefinitionConfigurer.java new file mode 100644 index 00000000..ea71477d --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/TypeDefinitionConfigurer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2023 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 graphql.schema.GraphQLSchema; +import graphql.schema.idl.TypeDefinitionRegistry; + +/** + * Callback that allows customizing the {@link TypeDefinitionRegistry} created + * from parsed schema files. It allows adding schema types programmatically + * before the registry is used to create {@link GraphQLSchema}. + * + * @author Rossen Stoyanchev + * @since 1.2 + */ +public interface TypeDefinitionConfigurer { + + /** + * Customize the given {@link TypeDefinitionRegistry}. + * @param registry the registry to customize + */ + void configure(TypeDefinitionRegistry registry); + +} diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java index e912eaee..877912f0 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPaginationTests.java @@ -35,7 +35,7 @@ import org.springframework.graphql.data.pagination.ConnectionFieldTypeVisitor; import org.springframework.graphql.data.query.ScrollPositionCursorStrategy; import org.springframework.graphql.data.query.ScrollSubrange; import org.springframework.graphql.data.query.WindowConnectionAdapter; -import org.springframework.graphql.execution.ConnectionTypeGenerator; +import org.springframework.graphql.execution.ConnectionTypeDefinitionConfigurer; import org.springframework.stereotype.Controller; import static org.assertj.core.api.Assertions.assertThat; @@ -83,8 +83,7 @@ public class SchemaMappingPaginationTests { ExecutionGraphQlService graphQlService = graphQlService((configurer, setup) -> { - ConnectionTypeGenerator typeGenerator = new ConnectionTypeGenerator(); - setup.typeDefinitionRegistryConfigurer(typeGenerator::generateConnectionTypes); + setup.typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer()); ScrollPositionCursorStrategy cursorStrategy = new ScrollPositionCursorStrategy(); WindowConnectionAdapter connectionAdapter = new WindowConnectionAdapter(cursorStrategy); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitorTests.java index 32c7bb1d..06c310ca 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/pagination/ConnectionFieldTypeVisitorTests.java @@ -26,7 +26,7 @@ import org.springframework.graphql.BookSource; import org.springframework.graphql.ExecutionGraphQlResponse; import org.springframework.graphql.GraphQlSetup; import org.springframework.graphql.TestExecutionRequest; -import org.springframework.graphql.execution.ConnectionTypeGenerator; +import org.springframework.graphql.execution.ConnectionTypeDefinitionConfigurer; import static org.assertj.core.api.Assertions.assertThat; @@ -75,7 +75,7 @@ public class ConnectionFieldTypeVisitorTests { ExecutionGraphQlResponse response = GraphQlSetup.schemaContent(schemaContent) .dataFetcher("Query", "books", env -> BookSource.books()) - .typeDefinitionRegistryConfigurer(new ConnectionTypeGenerator()::generateConnectionTypes) + .typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer()) .typeVisitor(ConnectionFieldTypeVisitor.create(List.of(adapter))) .toGraphQlService() .execute(TestExecutionRequest.forDocument(document)) diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeGeneratorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurerTests.java similarity index 93% rename from spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeGeneratorTests.java rename to spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurerTests.java index d5ab9dcc..2a908fc6 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeGeneratorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ConnectionTypeDefinitionConfigurerTests.java @@ -38,12 +38,12 @@ import org.springframework.graphql.TestExecutionRequest; import static org.assertj.core.api.Assertions.assertThat; /** - * Unit tests for {@link ConnectionTypeGenerator}. + * Unit tests for {@link ConnectionTypeDefinitionConfigurer}. * * @author Rossen Stoyanchev * @since 1.2 */ -public class ConnectionTypeGeneratorTests { +public class ConnectionTypeDefinitionConfigurerTests { @Test void connectionTypeGeneration() throws Exception { @@ -108,8 +108,7 @@ public class ConnectionTypeGeneratorTests { } private GraphQlSetup initGraphQlSetup(String schema) { - ConnectionTypeGenerator generator = new ConnectionTypeGenerator(); - return GraphQlSetup.schemaContent(schema).typeDefinitionRegistryConfigurer(generator::generateConnectionTypes); + return GraphQlSetup.schemaContent(schema).typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer()); } private static Connection createConnection( diff --git a/spring-graphql/src/testFixtures/java/org/springframework/graphql/GraphQlSetup.java b/spring-graphql/src/testFixtures/java/org/springframework/graphql/GraphQlSetup.java index 3e0a461f..98e4fc55 100644 --- a/spring-graphql/src/testFixtures/java/org/springframework/graphql/GraphQlSetup.java +++ b/spring-graphql/src/testFixtures/java/org/springframework/graphql/GraphQlSetup.java @@ -19,14 +19,12 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.function.Function; import graphql.GraphQL; import graphql.execution.instrumentation.Instrumentation; import graphql.schema.DataFetcher; import graphql.schema.GraphQLTypeVisitor; import graphql.schema.TypeResolver; -import graphql.schema.idl.TypeDefinitionRegistry; import org.springframework.context.ApplicationContext; import org.springframework.core.io.ByteArrayResource; @@ -38,6 +36,7 @@ import org.springframework.graphql.execution.DefaultExecutionGraphQlService; import org.springframework.graphql.execution.GraphQlSource; import org.springframework.graphql.execution.RuntimeWiringConfigurer; import org.springframework.graphql.execution.SubscriptionExceptionResolver; +import org.springframework.graphql.execution.TypeDefinitionConfigurer; import org.springframework.graphql.server.WebGraphQlHandler; import org.springframework.graphql.server.WebGraphQlInterceptor; import org.springframework.graphql.server.WebGraphQlSetup; @@ -82,10 +81,8 @@ public class GraphQlSetup implements GraphQlServiceSetup { wiringBuilder.type(type, typeBuilder -> typeBuilder.dataFetcher(field, dataFetcher))); } - public GraphQlSetup typeDefinitionRegistryConfigurer( - Function configurer) { - - this.graphQlSourceBuilder.configureTypeDefinitionRegistry(configurer); + public GraphQlSetup typeDefinitionConfigurer(TypeDefinitionConfigurer configurer) { + this.graphQlSourceBuilder.configureTypeDefinitions(configurer); return this; }