From 06675e0243f9b0c9993af67fc4d0fb4fdc1a3fd6 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 27 Jun 2024 13:15:19 +0100 Subject: [PATCH] SchemaMappingInspector detects Kotlin functions Closes gh-995 --- .../execution/SchemaMappingInspector.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) 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 cea57752..7c3b1587 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 @@ -17,6 +17,8 @@ package org.springframework.graphql.execution; import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -58,6 +60,7 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; /** * Inspect schema mappings on startup to ensure the following: @@ -81,6 +84,15 @@ public final class SchemaMappingInspector { private static final Log logger = LogFactory.getLog(SchemaMappingInspector.class); + /** + * GraphQL Java detects "record-like" methods that match field names. + * This predicate aims to match the method {@code isRecordLike(Method)} in + * {@link graphql.schema.fetching.LambdaFetchingSupport}. + */ + private static final Predicate recordLikePredicate = (method) -> + (!method.getDeclaringClass().equals(Object.class) && !method.getReturnType().equals(Void.class) && + method.getParameterCount() == 0 && Modifier.isPublic(method.getModifiers())); + private final GraphQLSchema schema; @@ -173,6 +185,12 @@ public final class SchemaMappingInspector { checkField(fieldContainer, field, ResolvableType.forMethodReturnType(descriptor.getReadMethod())); continue; } + // Kotlin function? + Method method = getRecordLikeMethod(resolvableType, fieldName); + if (method != null) { + checkField(fieldContainer, field, ResolvableType.forMethodReturnType(method)); + continue; + } } this.reportBuilder.unmappedField(FieldCoordinates.coordinates(typeName, fieldName)); @@ -249,6 +267,19 @@ public final class SchemaMappingInspector { } } + @Nullable + private static Method getRecordLikeMethod(ResolvableType resolvableType, String fieldName) { + Class clazz = resolvableType.resolve(); + if (clazz != null) { + for (Method method : clazz.getDeclaredMethods()) { + if (recordLikePredicate.test(method) && fieldName.equals(StringUtils.uncapitalize(method.getName()))) { + return method; + } + } + } + return null; + } + private static boolean isNotScalarOrEnumType(GraphQLType type) { return !(type instanceof GraphQLScalarType || type instanceof GraphQLEnumType); }