From 763877bee7cb98b2edb366fd5eeb2002e9a97483 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 13 Sep 2024 18:58:13 +0100 Subject: [PATCH] Improves generic type support in schema inspection The fix targets cases when a method's return type has a generic type, and the generic type is declared inline on the return type. We need to pass containing class information as a ResolvableType in order to be able to resolve generic types. Closes gh-1037 --- .../execution/SchemaMappingInspector.java | 7 +++-- .../SchemaMappingInspectorTests.java | 30 ++++++++++++++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java index e202cab2..1ac9f39f 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java @@ -93,7 +93,6 @@ public final class SchemaMappingInspector { (!method.getDeclaringClass().equals(Object.class) && !method.getReturnType().equals(Void.class) && method.getParameterCount() == 0 && Modifier.isPublic(method.getModifiers())); - private final GraphQLSchema schema; private final Map> dataFetchers; @@ -182,13 +181,15 @@ public final class SchemaMappingInspector { if (resolvableType != null) { PropertyDescriptor descriptor = getProperty(resolvableType, fieldName); if (descriptor != null) { - checkField(fieldContainer, field, ResolvableType.forMethodReturnType(descriptor.getReadMethod())); + MethodParameter returnType = new MethodParameter(descriptor.getReadMethod(), -1); + checkField(fieldContainer, field, ResolvableType.forMethodParameter(returnType, resolvableType)); continue; } // Kotlin function? Method method = getRecordLikeMethod(resolvableType, fieldName); if (method != null) { - checkField(fieldContainer, field, ResolvableType.forMethodReturnType(method)); + MethodParameter returnType = new MethodParameter(method, -1); + checkField(fieldContainer, field, ResolvableType.forMethodParameter(returnType, resolvableType)); continue; } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java index 3ce3fdbc..f3766d9c 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java @@ -310,7 +310,7 @@ class SchemaMappingInspectorTests extends SchemaMappingInspectorTestSupport { @Nested - class TypesInspectionTests { + class SchemaTypeInspectionTests { @Test void reportIsEmptyWhenFieldHasMatchingObjectProperty() { @@ -434,6 +434,25 @@ class SchemaMappingInspectorTests extends SchemaMappingInspectorTestSupport { assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Author", "missing"); } + @Test // gh-1037 + void reportHasUnmappedFieldOnGenericType() { + String schema = """ + type Query { + teamContainer: TeamContainer + } + type TeamContainer { + items: [Team] + } + type Team { + name: String + missing: String + } + """; + + SchemaReport report = inspectSchema(schema, TeamController.class); + assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Team", "missing"); + } + @Test void reportWorksWithCyclicRelations() { String schema = """ @@ -680,6 +699,10 @@ class SchemaMappingInspectorTests extends SchemaMappingInspectorTestSupport { return null; } + @QueryMapping + public ListContainer teamContainer() { + return new ListContainer<>(Collections.emptyList()); + } } @@ -692,4 +715,9 @@ class SchemaMappingInspectorTests extends SchemaMappingInspectorTestSupport { } + + record ListContainer(List items) { + + } + }