From f78a31213c381cf4b68aec1d8606fb51d2b0f758 Mon Sep 17 00:00:00 2001 From: Ciaran Liedeman Date: Sat, 26 Oct 2019 17:09:45 +0200 Subject: [PATCH] Add the TrivialDataFetcher interface to MethodFieldResolver so that it can be excluded from tracing --- .../graphql/tools/MethodFieldResolver.kt | 12 +++++- .../com/coxautodev/graphql/tools/Utils.kt | 19 +++++++++ .../UtilsTestTrivialDataFetcherBean.java | 8 ++++ .../com/coxautodev/graphql/tools/UtilsTest.kt | 40 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/coxautodev/graphql/tools/UtilsTestTrivialDataFetcherBean.java create mode 100644 src/test/kotlin/com/coxautodev/graphql/tools/UtilsTest.kt diff --git a/src/main/kotlin/com/coxautodev/graphql/tools/MethodFieldResolver.kt b/src/main/kotlin/com/coxautodev/graphql/tools/MethodFieldResolver.kt index 2b26e009..2e9e82a4 100644 --- a/src/main/kotlin/com/coxautodev/graphql/tools/MethodFieldResolver.kt +++ b/src/main/kotlin/com/coxautodev/graphql/tools/MethodFieldResolver.kt @@ -3,6 +3,7 @@ package com.coxautodev.graphql.tools import com.coxautodev.graphql.tools.SchemaParserOptions.GenericWrapper import com.esotericsoftware.reflectasm.MethodAccess import com.fasterxml.jackson.core.type.TypeReference +import graphql.TrivialDataFetcher import graphql.execution.batched.Batched import graphql.language.FieldDefinition import graphql.language.NonNullType @@ -105,7 +106,12 @@ internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolver return if (batched) { BatchedMethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options) } else { - MethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options) + if (args.isEmpty() && isTrivialDataFetcher(this.method)) { + TrivialMethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options) + } else { + MethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options) + } + } } @@ -191,6 +197,10 @@ open class MethodFieldResolverDataFetcher(private val sourceResolver: SourceReso } } +open class TrivialMethodFieldResolverDataFetcher(private val sourceResolver: SourceResolver, method: Method, private val args: List, private val options: SchemaParserOptions) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options), TrivialDataFetcher { + +} + private suspend inline fun MethodAccess.invokeSuspend(target: Any, methodIndex: Int, args: Array): Any? { return suspendCoroutineUninterceptedOrReturn { continuation -> invoke(target, methodIndex, *args + continuation) diff --git a/src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt b/src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt index ab51f3b3..2efd2562 100644 --- a/src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt +++ b/src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt @@ -6,6 +6,7 @@ import graphql.language.NonNullType import graphql.language.ObjectTypeDefinition import graphql.language.ObjectTypeExtensionDefinition import graphql.language.Type +import java.lang.reflect.Method import java.lang.reflect.ParameterizedType /** @@ -33,3 +34,21 @@ internal fun JavaType.unwrap(): Class = } else { this as Class<*> } + +/** + * Simple heuristic to check is a method is a trivial data fetcher. + * + * Requirements are: + * prefixed with get + * must have zero parameters + */ +internal fun isTrivialDataFetcher(method: Method): Boolean { + return (method.parameterCount == 0 + && ( + method.name.startsWith("get") + || isBooleanGetter(method))) +} + +private fun isBooleanGetter(method: Method) = (method.name.startsWith("is") + && (method.returnType == java.lang.Boolean::class.java) + || method.returnType == Boolean::class.java) \ No newline at end of file diff --git a/src/test/java/com/coxautodev/graphql/tools/UtilsTestTrivialDataFetcherBean.java b/src/test/java/com/coxautodev/graphql/tools/UtilsTestTrivialDataFetcherBean.java new file mode 100644 index 00000000..969f9da9 --- /dev/null +++ b/src/test/java/com/coxautodev/graphql/tools/UtilsTestTrivialDataFetcherBean.java @@ -0,0 +1,8 @@ +package com.coxautodev.graphql.tools; + +public class UtilsTestTrivialDataFetcherBean { + + public boolean isBooleanPrimitive() { + return false; + } +} diff --git a/src/test/kotlin/com/coxautodev/graphql/tools/UtilsTest.kt b/src/test/kotlin/com/coxautodev/graphql/tools/UtilsTest.kt new file mode 100644 index 00000000..a72bdae3 --- /dev/null +++ b/src/test/kotlin/com/coxautodev/graphql/tools/UtilsTest.kt @@ -0,0 +1,40 @@ +package com.coxautodev.graphql.tools + +import org.junit.Assert +import org.junit.Test + +class UtilsTest { + + @Suppress("unused") + class Bean { + fun getterValid(): String = "" + + fun getterWithArgument(@Suppress("UNUSED_PARAMETER") arg1: String): String = "" + + internal fun getterInternal(): String = "" + + fun notAGetter(): String = "" + + fun isString(): String = "" + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + fun isJavaBoolean(): java.lang.Boolean = java.lang.Boolean(false) + + fun isKotlinBoolean(): Boolean = false + } + + @Test + fun `isTrivialDataFetcher`() { + val clazz = Bean::class.java + + Assert.assertTrue(isTrivialDataFetcher(clazz.getMethod("getterValid"))) + Assert.assertFalse(isTrivialDataFetcher(clazz.getMethod("getterWithArgument", String::class.java))) + Assert.assertFalse(isTrivialDataFetcher(clazz.getMethod("notAGetter"))) + + Assert.assertFalse(isTrivialDataFetcher(clazz.getMethod("isString"))) + Assert.assertTrue(isTrivialDataFetcher(clazz.getMethod("isJavaBoolean"))) + Assert.assertTrue(isTrivialDataFetcher(clazz.getMethod("isKotlinBoolean"))) + + Assert.assertTrue(isTrivialDataFetcher(UtilsTestTrivialDataFetcherBean::class.java.getMethod("isBooleanPrimitive"))) + } +}