Update SchemaMappingInspector to support Connection types

Closes gh-656
This commit is contained in:
rstoyanchev
2023-04-13 16:56:26 +01:00
parent dedebf4bad
commit 56b0b168b9
2 changed files with 61 additions and 8 deletions

View File

@@ -33,6 +33,7 @@ import graphql.schema.GraphQLType;
import graphql.schema.idl.RuntimeWiring;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
@@ -56,6 +57,7 @@ import org.springframework.util.MultiValueMap;
* even if a common interface is declared by a {@link SelfDescribingDataFetcher}.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
* @since 1.2.0
*/
class SchemaMappingInspector {
@@ -120,6 +122,10 @@ class SchemaMappingInspector {
if (isTypeAlreadyInspected(objectType)) {
return;
}
if (isConnectionType(objectType)) {
objectType = getEdgeNodeType(objectType);
declaredType = declaredType.getNested(2);
}
Map<String, DataFetcher> typeDataFetcher = this.runtimeWiring.getDataFetcherForType(objectType.getName());
Class<?> declaredClass = unwrapPublisherTypes(declaredType);
for (GraphQLFieldDefinition field : objectType.getFieldDefinitions()) {
@@ -142,6 +148,22 @@ class SchemaMappingInspector {
}
}
private boolean isConnectionType(GraphQLObjectType objectType) {
return (objectType.getName().endsWith("Connection") &&
objectType.getField("edges") != null && objectType.getField("pageInfo") != null);
}
@NotNull
private GraphQLObjectType getEdgeNodeType(GraphQLObjectType objectType) {
GraphQLList edgesListType = (GraphQLList) unwrapNonNull(objectType.getField("edges").getType());
GraphQLObjectType edgeType = (GraphQLObjectType) edgesListType.getWrappedType();
return (GraphQLObjectType) unwrapNonNull(edgeType.getField("node").getType());
}
private <T extends GraphQLType> GraphQLType unwrapNonNull(GraphQLType outputType) {
return (outputType instanceof GraphQLNonNull wrapper ? wrapper.getWrappedType() : outputType);
}
@Nullable
private Class<?> unwrapPublisherTypes(ResolvableType declaredType) {
Class<?> rawClass = declaredType.getRawClass();

View File

@@ -22,15 +22,17 @@ import java.util.List;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import org.assertj.core.api.AbstractAssert;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.data.domain.Window;
import org.springframework.graphql.Author;
import org.springframework.graphql.Book;
import org.springframework.graphql.GraphQlSetup;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.MutationMapping;
import org.springframework.graphql.data.method.annotation.QueryMapping;
@@ -45,6 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link SchemaMappingInspector}.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
*/
class SchemaMappingInspectorTests {
@@ -91,6 +94,23 @@ class SchemaMappingInspectorTests {
assertThatReport(report).hasSize(1).missesFields("Book", "missing");
}
@Test
void inspectTypeForConnection() {
String schema = """
type Query {
paginatedBooks: BookConnection
}
type Book {
id: ID
name: String
missing: Boolean
}
""";
SchemaMappingInspector.Report report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasSize(1).missesFields("Book", "missing");
}
@Test
void inspectExtensionTypesForQueries() {
String schema = """
@@ -413,6 +433,11 @@ class SchemaMappingInspectorTests {
return List.of(new Book());
}
@QueryMapping
public Window<Book> paginatedBooks() {
return Window.from(List.of(new Book()), OffsetScrollPosition::of);
}
@SchemaMapping
public String fetcher(Book book) {
return "custom fetcher";
@@ -457,12 +482,20 @@ class SchemaMappingInspectorTests {
}
SchemaMappingInspector.Report inspectSchema(String schemaContent, Class<?>... controllers) {
GraphQLSchema schema = SchemaGenerator.createdMockedSchema(schemaContent);
RuntimeWiring.Builder builder = createRuntimeWiring(controllers);
return new SchemaMappingInspector().inspectSchemaMappings(schema, builder.build());
RuntimeWiringConfigurer wiringConfigurer = createRuntimeWiring(controllers);
RuntimeWiring.Builder wiringBuilder = RuntimeWiring.newRuntimeWiring();
wiringConfigurer.configure(wiringBuilder);
GraphQLSchema schema = GraphQlSetup.schemaContent(schemaContent)
.runtimeWiring(wiringConfigurer)
.typeDefinitionConfigurer(new ConnectionTypeDefinitionConfigurer())
.toGraphQlSource()
.schema();
return new SchemaMappingInspector().inspectSchemaMappings(schema, wiringBuilder.build());
}
RuntimeWiring.Builder createRuntimeWiring(Class<?>... handlerTypes) {
private RuntimeWiringConfigurer createRuntimeWiring(Class<?>... handlerTypes) {
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext();
for (Class<?> handlerType : handlerTypes) {
appContext.registerBean(handlerType);
@@ -473,9 +506,7 @@ class SchemaMappingInspectorTests {
configurer.setApplicationContext(appContext);
configurer.afterPropertiesSet();
RuntimeWiring.Builder wiringBuilder = RuntimeWiring.newRuntimeWiring();
configurer.configure(wiringBuilder);
return wiringBuilder;
return configurer;
}
static SchemaInspectionReportAssert assertThatReport(SchemaMappingInspector.Report actual) {