From 213f8443aa5529388709c576d2d17df2ad09b375 Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Fri, 27 Sep 2024 14:04:53 -0700
Subject: [PATCH 01/12] Upgrade to v22.x

---
 pom.xml                                       |  4 ++-
 .../kickstart/tools/resolver/FieldResolver.kt |  1 +
 .../tools/resolver/MethodFieldResolver.kt     | 25 ++++++++++++-------
 .../kickstart/tools/EndToEndSpecHelper.kt     |  5 ++--
 4 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/pom.xml b/pom.xml
index 555e0846..3525ddc1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,7 @@
             <dependency>
                 <groupId>com.graphql-java</groupId>
                 <artifactId>graphql-java</artifactId>
-                <version>${graphql-java.version}</version>
+                <version>22.1</version>
             </dependency>
             <dependency>
                 <groupId>org.antlr</groupId>
@@ -306,6 +306,8 @@
                     <includes>
                         <include>**/*Test.*</include>
                     </includes>
+<!--                    or solve this differently? tests fail-->
+                    <argLine>--add-reads kotlin.stdlib=kotlinx.coroutines.core</argLine>
                 </configuration>
             </plugin>
             <plugin>
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
index af88f6aa..440d4473 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
@@ -35,6 +35,7 @@ internal abstract class FieldResolver(
         } else {
             { environment ->
                 val source = environment.getSource<Any>()
+                    ?: throw ResolverError("Expected source object to not be null!")
 
                 if (!this.genericType.isAssignableFrom(source.javaClass)) {
                     throw ResolverError("Expected source object to be an instance of '${this.genericType.getRawClass().name}' but instead got '${source.javaClass.name}'")
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
index c76023d5..ebcb4a52 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
@@ -30,7 +30,7 @@ internal class MethodFieldResolver(
     field: FieldDefinition,
     search: FieldResolverScanner.Search,
     options: SchemaParserOptions,
-    val method: Method
+    val method: Method,
 ) : FieldResolver(field, search, options, search.type) {
 
     private val log = LoggerFactory.getLogger(javaClass)
@@ -53,6 +53,7 @@ internal class MethodFieldResolver(
 
             args.add { environment ->
                 val source = environment.getSource<Any>()
+                    ?: throw ResolverError("Expected source object to not be null!")
                 if (!expectedType.isAssignableFrom(source.javaClass)) {
                     throw ResolverError("Source type (${source.javaClass.name}) is not expected type (${expectedType.name})!")
                 }
@@ -114,6 +115,7 @@ internal class MethodFieldResolver(
                         environment.getContext() // TODO: remove deprecated use in next major release
                     }
                 }
+
                 GraphQLContext::class.java -> args.add { environment -> environment.graphQlContext }
                 else -> args.add { environment -> environment }
             }
@@ -139,19 +141,23 @@ internal class MethodFieldResolver(
         return when (type) {
             is ListType -> List::class.java.isAssignableFrom(this.genericType.getRawClass(genericParameterType))
                 && isConcreteScalarType(environment, type.type, this.genericType.unwrapGenericType(genericParameterType))
+
             is TypeName -> environment.graphQLSchema?.getType(type.name)?.let { isScalar(it) && type.name != "ID" }
                 ?: false
+
             is NonNullType -> isConcreteScalarType(environment, type.type, genericParameterType)
             else -> false
         }
     }
 
     override fun scanForMatches(): List<TypeClassMatcher.PotentialMatch> {
-        val unwrappedGenericType = genericType.unwrapGenericType(try {
-            method.kotlinFunction?.returnType?.javaType ?: method.genericReturnType
-        } catch (e: InternalError) {
-            method.genericReturnType
-        })
+        val unwrappedGenericType = genericType.unwrapGenericType(
+            try {
+                method.kotlinFunction?.returnType?.javaType ?: method.genericReturnType
+            } catch (e: InternalError) {
+                method.genericReturnType
+            }
+        )
         val returnValueMatch = TypeClassMatcher.PotentialMatch.returnValue(field.type, unwrappedGenericType, genericType, SchemaClassScanner.ReturnValueReference(method))
 
         return field.inputValueDefinitions.mapIndexed { i, inputDefinition ->
@@ -187,7 +193,7 @@ internal open class MethodFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
     method: Method,
     private val args: List<ArgumentPlaceholder>,
-    private val options: SchemaParserOptions
+    private val options: SchemaParserOptions,
 ) : DataFetcher<Any> {
 
     private val resolverMethod = method
@@ -238,11 +244,12 @@ internal open class MethodFieldResolverDataFetcher(
     }
 }
 
+// TODO use graphql.schema.LightDataFetcher
 internal class TrivialMethodFieldResolverDataFetcher(
     sourceResolver: SourceResolver,
     method: Method,
     args: List<ArgumentPlaceholder>,
-    options: SchemaParserOptions
+    options: SchemaParserOptions,
 ) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options),
     TrivialDataFetcher<Any> // just to mark it for tracing and optimizations
 
@@ -256,7 +263,7 @@ private fun invoke(method: Method, instance: Any, args: Array<Any?>): Any? {
     try {
         return method.invoke(instance, *args)
     } catch (e: InvocationTargetException) {
-        throw  e.cause ?: RuntimeException("Unknown error occurred while invoking resolver method")
+        throw e.cause ?: RuntimeException("Unknown error occurred while invoking resolver method")
     }
 }
 
diff --git a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt
index 329f9ed8..b728f7a0 100644
--- a/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt
@@ -141,8 +141,8 @@ input ItemSearchInput {
 }
 
 input NewItemInput {
-    name: String! @deprecated
-    type: Type! @deprecated(reason: "This is a reason")
+    name: String @deprecated
+    type: Type @deprecated(reason: "This is a reason")
 }
 
 enum Type {
@@ -536,4 +536,3 @@ val uploadScalar: GraphQLScalarType = GraphQLScalarType.newScalar()
             throw CoercingParseLiteralException("Must use variables to specify Upload values")
         }
     }).build()
-

From 5b309bb27a7034e9c1af9724a9ed010b58bc2317 Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Tue, 1 Oct 2024 08:28:19 -0700
Subject: [PATCH 02/12] Upgrade to v22.3

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 3525ddc1..acaf46f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,7 +45,7 @@
             <dependency>
                 <groupId>com.graphql-java</groupId>
                 <artifactId>graphql-java</artifactId>
-                <version>22.1</version>
+                <version>22.3</version>
             </dependency>
             <dependency>
                 <groupId>org.antlr</groupId>

From f19e0369b6b273e789d40c4544884ea897f43688 Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Tue, 1 Oct 2024 14:54:51 -0700
Subject: [PATCH 03/12] Upgrade other dependencies

---
 pom.xml | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/pom.xml b/pom.xml
index acaf46f9..685a8f7a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,10 +14,10 @@
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <java.version>11</java.version>
-        <kotlin.version>1.8.21</kotlin.version>
-        <kotlin-coroutines.version>1.7.3</kotlin-coroutines.version>
-        <jackson.version>2.16.0</jackson.version>
-        <graphql-java.version>21.3</graphql-java.version>
+        <kotlin.version>2.0.20</kotlin.version>
+        <kotlin-coroutines.version>1.9.0</kotlin-coroutines.version>
+        <jackson.version>2.17.0</jackson.version>
+        <graphql-java.version>22.3</graphql-java.version>
         <reactive-streams.version>1.0.4</reactive-streams.version>
 
         <maven.compiler.source>${java.version}</maven.compiler.source>
@@ -45,7 +45,7 @@
             <dependency>
                 <groupId>com.graphql-java</groupId>
                 <artifactId>graphql-java</artifactId>
-                <version>22.3</version>
+                <version>${graphql-java.version}</version>
             </dependency>
             <dependency>
                 <groupId>org.antlr</groupId>
@@ -83,7 +83,7 @@
                 <version>3.29.2-GA</version>
                 <scope>provided</scope>
             </dependency>
-            <!-- Optional for supporting spring proxies -->
+            <!-- Optional for supporting Spring proxies -->
             <dependency>
                 <groupId>org.springframework</groupId>
                 <artifactId>spring-aop</artifactId>
@@ -91,7 +91,7 @@
                 <scope>provided</scope>
             </dependency>
 
-            <!-- Test  -->
+            <!-- Test -->
             <dependency>
                 <groupId>cglib</groupId>
                 <artifactId>cglib-nodep</artifactId>
@@ -134,8 +134,8 @@
         <dependency>
             <groupId>org.jetbrains.kotlin</groupId>
             <artifactId>kotlin-stdlib</artifactId>
-            <!--TODO remove this after upgrading kotlin-->
             <exclusions>
+                <!-- kotlinx-coroutines-core-jvm brings more recent version -->
                 <exclusion>
                     <groupId>org.jetbrains</groupId>
                     <artifactId>annotations</artifactId>
@@ -306,7 +306,7 @@
                     <includes>
                         <include>**/*Test.*</include>
                     </includes>
-<!--                    or solve this differently? tests fail-->
+                    <!--                    or solve this differently? tests fail-->
                     <argLine>--add-reads kotlin.stdlib=kotlinx.coroutines.core</argLine>
                 </configuration>
             </plugin>

From 834afeaaa0ee0a56fa587550267c8a141492c94c Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Tue, 1 Oct 2024 15:57:38 -0700
Subject: [PATCH 04/12] Use LightDataFetcher where possible

---
 pom.xml                                       |   2 +-
 .../kickstart/tools/resolver/FieldResolver.kt |  22 +++-
 .../tools/resolver/MapFieldResolver.kt        |  20 +--
 .../tools/resolver/MethodFieldResolver.kt     | 117 ++++++++++++------
 .../tools/resolver/PropertyFieldResolver.kt   |  20 ++-
 .../graphql/kickstart/tools/util/Utils.kt     |   7 +-
 .../graphql/kickstart/tools/DirectiveTest.kt  |   1 +
 .../MethodFieldResolverDataFetcherTest.kt     |   8 ++
 .../graphql/kickstart/tools/TestUtils.kt      |  14 ++-
 9 files changed, 140 insertions(+), 71 deletions(-)

diff --git a/pom.xml b/pom.xml
index 685a8f7a..2944adf8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
     <groupId>com.graphql-java-kickstart</groupId>
     <artifactId>graphql-java-tools</artifactId>
-    <version>13.1.2-SNAPSHOT</version>
+    <version>14.0.0-LOCAL</version>
     <packaging>jar</packaging>
 
     <name>GraphQL Java Tools</name>
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
index 440d4473..440820c8 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
@@ -29,13 +29,20 @@ internal abstract class FieldResolver(
     /**
      * Add source resolver depending on whether or not this is a resolver method
      */
-    protected fun getSourceResolver(): SourceResolver {
+    protected fun createSourceResolver(): SourceResolver {
         return if (this.search.source != null) {
-            { this.search.source }
+            SourceResolver { _, _ -> this.search.source }
         } else {
-            { environment ->
-                val source = environment.getSource<Any>()
-                    ?: throw ResolverError("Expected source object to not be null!")
+            SourceResolver { environment, sourceObject ->
+                val source = if (sourceObject != null) {
+                    // if source object is known, environment is null as an optimization (LightDataFetcher)
+                    sourceObject
+                } else {
+                    environment
+                        ?: throw ResolverError("Expected DataFetchingEnvironment to not be null!")
+                    environment.getSource<Any>()
+                        ?: throw ResolverError("Expected source object to not be null!")
+                }
 
                 if (!this.genericType.isAssignableFrom(source.javaClass)) {
                     throw ResolverError("Expected source object to be an instance of '${this.genericType.getRawClass().name}' but instead got '${source.javaClass.name}'")
@@ -47,4 +54,7 @@ internal abstract class FieldResolver(
     }
 }
 
-internal typealias SourceResolver = (DataFetchingEnvironment) -> Any
+fun interface SourceResolver {
+
+    fun resolve(environment: DataFetchingEnvironment?, source: Any?): Any
+}
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
index 20e51a24..d7d29f5c 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
@@ -8,6 +8,9 @@ import graphql.kickstart.tools.util.JavaType
 import graphql.language.FieldDefinition
 import graphql.schema.DataFetcher
 import graphql.schema.DataFetchingEnvironment
+import graphql.schema.GraphQLFieldDefinition
+import graphql.schema.LightDataFetcher
+import java.util.function.Supplier
 
 /**
  * @author Nick Weedon
@@ -37,7 +40,7 @@ internal class MapFieldResolver(
     }
 
     override fun createDataFetcher(): DataFetcher<*> {
-        return MapFieldResolverDataFetcher(getSourceResolver(), field.name)
+        return MapFieldResolverDataFetcher(createSourceResolver(), field.name)
     }
 
     override fun scanForMatches(): List<TypeClassMatcher.PotentialMatch> {
@@ -49,15 +52,18 @@ internal class MapFieldResolver(
 
 internal class MapFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
-    private val key: String
-) : DataFetcher<Any> {
+    private val key: String,
+) : LightDataFetcher<Any> {
 
-    override fun get(environment: DataFetchingEnvironment): Any? {
-        val resolvedSourceObject = sourceResolver(environment)
-        if (resolvedSourceObject is Map<*, *>) {
-            return resolvedSourceObject[key]
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+        if (sourceObject is Map<*, *>) {
+            return sourceObject[key]
         } else {
             throw RuntimeException("MapFieldResolver attempt to fetch a field from an object instance that was not a map")
         }
     }
+
+    override fun get(environment: DataFetchingEnvironment): Any? {
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
+    }
 }
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
index ebcb4a52..3ae84c0c 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
@@ -2,7 +2,6 @@ package graphql.kickstart.tools.resolver
 
 import com.fasterxml.jackson.core.type.TypeReference
 import graphql.GraphQLContext
-import graphql.TrivialDataFetcher
 import graphql.kickstart.tools.*
 import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper
 import graphql.kickstart.tools.util.JavaType
@@ -12,12 +11,15 @@ import graphql.kickstart.tools.util.unwrap
 import graphql.language.*
 import graphql.schema.DataFetcher
 import graphql.schema.DataFetchingEnvironment
+import graphql.schema.GraphQLFieldDefinition
 import graphql.schema.GraphQLTypeUtil.isScalar
+import graphql.schema.LightDataFetcher
 import kotlinx.coroutines.future.future
 import org.slf4j.LoggerFactory
 import java.lang.reflect.InvocationTargetException
 import java.lang.reflect.Method
 import java.util.*
+import java.util.function.Supplier
 import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
 import kotlin.reflect.full.valueParameters
 import kotlin.reflect.jvm.javaType
@@ -122,9 +124,9 @@ internal class MethodFieldResolver(
         }
 
         return if (args.isEmpty() && isTrivialDataFetcher(this.method)) {
-            TrivialMethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options)
+            LightMethodFieldResolverDataFetcher(createSourceResolver(), this.method, options)
         } else {
-            MethodFieldResolverDataFetcher(getSourceResolver(), this.method, args, options)
+            MethodFieldResolverDataFetcher(createSourceResolver(), this.method, args, options)
         }
     }
 
@@ -189,69 +191,102 @@ internal class MethodFieldResolver(
     override fun toString() = "MethodFieldResolver{method=$method}"
 }
 
-internal open class MethodFieldResolverDataFetcher(
+internal class MethodFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
-    method: Method,
+    private val method: Method,
     private val args: List<ArgumentPlaceholder>,
     private val options: SchemaParserOptions,
 ) : DataFetcher<Any> {
 
-    private val resolverMethod = method
-    private val isSuspendFunction = try {
-        method.kotlinFunction?.isSuspend == true
-    } catch (e: InternalError) {
-        false
-    }
+    private val isSuspendFunction = method.isSuspendFunction()
 
-    private class CompareGenericWrappers {
-        companion object : Comparator<GenericWrapper> {
-            override fun compare(w1: GenericWrapper, w2: GenericWrapper): Int = when {
-                w1.type.isAssignableFrom(w2.type) -> 1
-                else -> -1
+    override fun get(environment: DataFetchingEnvironment): Any? {
+        val source = sourceResolver.resolve(environment, null)
+        val args = this.args.map { it(environment) }.toTypedArray()
+
+        return if (isSuspendFunction) {
+            environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
+                invokeSuspend(source, method, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
             }
+        } else {
+            invoke(method, source, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
         }
     }
 
-    override fun get(environment: DataFetchingEnvironment): Any? {
-        val source = sourceResolver(environment)
-        val args = this.args.map { it(environment) }.toTypedArray()
+    /**
+     * Function that returns the object used to fetch the data. It can be a DataFetcher or an entity.
+     */
+    @Suppress("unused")
+    fun getWrappedFetchingObject(environment: DataFetchingEnvironment): Any {
+        return sourceResolver.resolve(environment, null)
+    }
+}
+
+/**
+ * Similar to [MethodFieldResolverDataFetcher] but for light data fetchers which do not require the environment to be supplied unless suspend functions or
+ * generic wrappers are used.
+ */
+internal class LightMethodFieldResolverDataFetcher(
+    private val sourceResolver: SourceResolver,
+    private val method: Method,
+    private val options: SchemaParserOptions,
+) : LightDataFetcher<Any?> {
+
+    private val isSuspendFunction = method.isSuspendFunction()
+
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+        val source = sourceResolver.resolve(null, sourceObject)
 
         return if (isSuspendFunction) {
-            environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
-                invokeSuspend(source, resolverMethod, args)?.transformWithGenericWrapper(environment)
+            environmentSupplier.get().coroutineScope().future(options.coroutineContextProvider.provide()) {
+                invokeSuspend(source, method, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
             }
         } else {
-            invoke(resolverMethod, source, args)?.transformWithGenericWrapper(environment)
+            invoke(method, source, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
         }
     }
 
-    private fun Any.transformWithGenericWrapper(environment: DataFetchingEnvironment): Any? {
-        return options.genericWrappers
-            .asSequence()
-            .filter { it.type.isInstance(this) }
-            .sortedWith(CompareGenericWrappers)
-            .firstOrNull()
-            ?.transformer?.invoke(this, environment) ?: this
+    override fun get(environment: DataFetchingEnvironment): Any? {
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
     }
 
     /**
-     * Function that returns the object used to fetch the data.
-     * It can be a DataFetcher or an entity.
+     * Function that returns the object used to fetch the data. It can be a DataFetcher or an entity.
      */
     @Suppress("unused")
-    open fun getWrappedFetchingObject(environment: DataFetchingEnvironment): Any {
-        return sourceResolver(environment)
+    fun getWrappedFetchingObject(environment: DataFetchingEnvironment): Any {
+        return sourceResolver.resolve(environment, null)
     }
 }
 
-// TODO use graphql.schema.LightDataFetcher
-internal class TrivialMethodFieldResolverDataFetcher(
-    sourceResolver: SourceResolver,
-    method: Method,
-    args: List<ArgumentPlaceholder>,
-    options: SchemaParserOptions,
-) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options),
-    TrivialDataFetcher<Any> // just to mark it for tracing and optimizations
+private fun Any.transformWithGenericWrapper(
+    genericWrappers: List<GenericWrapper>,
+    environmentSupplier: Supplier<DataFetchingEnvironment>
+): Any? {
+    return genericWrappers
+        .asSequence()
+        .filter { it.type.isInstance(this) }
+        .sortedWith(CompareGenericWrappers)
+        .firstOrNull()
+        ?.transformer?.invoke(this, environmentSupplier.get()) ?: this
+}
+
+private class CompareGenericWrappers {
+    companion object : Comparator<GenericWrapper> {
+        override fun compare(w1: GenericWrapper, w2: GenericWrapper): Int = when {
+            w1.type.isAssignableFrom(w2.type) -> 1
+            else -> -1
+        }
+    }
+}
+
+private fun Method.isSuspendFunction(): Boolean {
+    return try {
+        this.kotlinFunction?.isSuspend == true
+    } catch (e: InternalError) {
+        false
+    }
+}
 
 private suspend inline fun invokeSuspend(target: Any, resolverMethod: Method, args: Array<Any?>): Any? {
     return suspendCoroutineUninterceptedOrReturn { continuation ->
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
index dba3e9dd..96c8953e 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
@@ -6,7 +6,10 @@ import graphql.kickstart.tools.TypeClassMatcher
 import graphql.language.FieldDefinition
 import graphql.schema.DataFetcher
 import graphql.schema.DataFetchingEnvironment
+import graphql.schema.GraphQLFieldDefinition
+import graphql.schema.LightDataFetcher
 import java.lang.reflect.Field
+import java.util.function.Supplier
 
 /**
  * @author Andrew Potter
@@ -15,11 +18,11 @@ internal class PropertyFieldResolver(
     field: FieldDefinition,
     search: FieldResolverScanner.Search,
     options: SchemaParserOptions,
-    private val property: Field
+    private val property: Field,
 ) : FieldResolver(field, search, options, property.declaringClass) {
 
     override fun createDataFetcher(): DataFetcher<*> {
-        return PropertyFieldResolverDataFetcher(getSourceResolver(), property)
+        return PropertyFieldResolverDataFetcher(createSourceResolver(), property)
     }
 
     override fun scanForMatches(): List<TypeClassMatcher.PotentialMatch> {
@@ -28,7 +31,8 @@ internal class PropertyFieldResolver(
                 field.type,
                 property.genericType,
                 genericType,
-                SchemaClassScanner.FieldTypeReference(property.toString()))
+                SchemaClassScanner.FieldTypeReference(property.toString())
+            )
         )
     }
 
@@ -37,10 +41,14 @@ internal class PropertyFieldResolver(
 
 internal class PropertyFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
-    private val field: Field
-) : DataFetcher<Any> {
+    private val field: Field,
+) : LightDataFetcher<Any> {
+
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+        return field.get(sourceResolver.resolve(null, sourceObject))
+    }
 
     override fun get(environment: DataFetchingEnvironment): Any? {
-        return field.get(sourceResolver(environment))
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
     }
 }
diff --git a/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt b/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
index ba5e22a5..f1802402 100644
--- a/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
@@ -62,11 +62,11 @@ internal fun getDocumentation(node: AbstractNode<*>, options: SchemaParserOption
     }
 
 /**
- * Simple heuristic to check is a method is a trivial data fetcher.
+ * Simple heuristic to check if a method is a trivial data fetcher.
  *
  * Requirements are:
- * prefixed with get
- * must have zero parameters
+ * - prefixed with get
+ * - must have zero parameters
  */
 internal fun isTrivialDataFetcher(method: Method): Boolean {
     return (method.parameterCount == 0
@@ -80,4 +80,3 @@ private fun isBooleanGetter(method: Method) = (method.name.startsWith("is")
     || method.returnType == Boolean::class.java)
 
 internal fun String.snakeToCamelCase(): String = split("_").joinToString(separator = "") { it.replaceFirstChar(Char::titlecase) }
-
diff --git a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt
index 912b3501..79e787cf 100644
--- a/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/DirectiveTest.kt
@@ -10,6 +10,7 @@ import graphql.schema.idl.SchemaDirectiveWiringEnvironment
 import org.junit.Test
 
 class DirectiveTest {
+
     @Test
     fun `should apply @uppercase directive on field`() {
         val schema = SchemaParser.newParser()
diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
index 481d2a4e..9401feef 100644
--- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
@@ -13,6 +13,8 @@ import graphql.language.TypeName
 import graphql.schema.DataFetcher
 import graphql.schema.DataFetchingEnvironment
 import graphql.schema.DataFetchingEnvironmentImpl
+import graphql.schema.GraphQLFieldDefinition
+import graphql.schema.GraphQLObjectType
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
@@ -299,6 +301,12 @@ class MethodFieldResolverDataFetcherTest {
     private fun createEnvironment(source: Any = Object(), arguments: Map<String, Any> = emptyMap(), context: GraphQLContext? = null): DataFetchingEnvironment {
         return DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext())
             .source(source)
+            .fieldDefinition(
+                GraphQLFieldDefinition.newFieldDefinition()
+                    .name("ignored")
+                    .type(GraphQLObjectType.newObject().name("ignored").build())
+                    .build()
+            )
             .arguments(arguments)
             .graphQLContext(context)
             .build()
diff --git a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt
index 2b0ff09b..32473510 100644
--- a/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/TestUtils.kt
@@ -7,14 +7,16 @@ import graphql.GraphQL
 private val mapper = ObjectMapper()
 
 fun assertNoGraphQlErrors(gql: GraphQL, args: Map<String, Any> = mapOf(), context: Map<Any, Any> = mapOf(), closure: () -> String): Map<String, Any> {
-    val result = gql.execute(ExecutionInput.newExecutionInput()
-        .query(closure.invoke())
-        .graphQLContext(context)
-        .root(context)
-        .variables(args))
+    val result = gql.execute(
+        ExecutionInput.newExecutionInput()
+            .query(closure.invoke())
+            .graphQLContext(context)
+            .root(context)
+            .variables(args)
+    )
 
     if (result.errors.isNotEmpty()) {
-        throw AssertionError("GraphQL result contained errors!\n${result.errors.map { mapper.writeValueAsString(it) }.joinToString { "\n" }}")
+        throw AssertionError("GraphQL result contained errors!\n${result.errors.map { it.message }.joinToString("\n")}")
     }
 
     return result.getData() as Map<String, Any>

From 29cecf01ca6e7eb68fd3d06cdf6b42d3473113d5 Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Wed, 2 Oct 2024 13:06:03 -0700
Subject: [PATCH 05/12] Downgrade build-helper-maven-plugin to 3.4.0 to fix
 JitPack build

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 2944adf8..bd3d7db2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -240,7 +240,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
-                <version>3.5.0</version>
+                <version>3.4.0</version>
                 <executions>
                     <execution>
                         <id>add-test-source</id>

From 0745d59b4fd261de22655521dd56d3b064eacc6e Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Thu, 3 Oct 2024 09:15:55 -0700
Subject: [PATCH 06/12] Simplify lightweight data fetcher heuristic

---
 pom.xml                                       |  2 +-
 .../tools/resolver/MapFieldResolver.kt        |  2 +-
 .../tools/resolver/MethodFieldResolver.kt     | 39 +++++++------------
 .../tools/resolver/PropertyFieldResolver.kt   |  2 +-
 .../graphql/kickstart/tools/util/Utils.kt     | 18 ---------
 .../graphql/kickstart/tools/util/UtilsTest.kt | 18 ---------
 6 files changed, 16 insertions(+), 65 deletions(-)

diff --git a/pom.xml b/pom.xml
index bd3d7db2..f23b3ee4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
     <groupId>com.graphql-java-kickstart</groupId>
     <artifactId>graphql-java-tools</artifactId>
-    <version>14.0.0-LOCAL</version>
+    <version>14.1.0-LOCAL</version>
     <packaging>jar</packaging>
 
     <name>GraphQL Java Tools</name>
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
index d7d29f5c..ce48e494 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
@@ -55,7 +55,7 @@ internal class MapFieldResolverDataFetcher(
     private val key: String,
 ) : LightDataFetcher<Any> {
 
-    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
         if (sourceObject is Map<*, *>) {
             return sourceObject[key]
         } else {
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
index 3ae84c0c..a400b1e7 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
@@ -6,7 +6,6 @@ import graphql.kickstart.tools.*
 import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper
 import graphql.kickstart.tools.util.JavaType
 import graphql.kickstart.tools.util.coroutineScope
-import graphql.kickstart.tools.util.isTrivialDataFetcher
 import graphql.kickstart.tools.util.unwrap
 import graphql.language.*
 import graphql.schema.DataFetcher
@@ -37,13 +36,9 @@ internal class MethodFieldResolver(
 
     private val log = LoggerFactory.getLogger(javaClass)
 
-    private val additionalLastArgument =
-        try {
-            (method.kotlinFunction?.valueParameters?.size
-                ?: method.parameterCount) == (field.inputValueDefinitions.size + getIndexOffset() + 1)
-        } catch (e: InternalError) {
-            method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1)
-        }
+    private val isSuspendFunction = method.isSuspendFunction()
+    private val numberOfParameters = method.kotlinFunction?.valueParameters?.size ?: method.parameterCount
+    private val hasAdditionalParameter = numberOfParameters == (field.inputValueDefinitions.size + getIndexOffset() + 1)
 
     override fun createDataFetcher(): DataFetcher<*> {
         val args = mutableListOf<ArgumentPlaceholder>()
@@ -100,7 +95,7 @@ internal class MethodFieldResolver(
         }
 
         // Add DataFetchingEnvironment/Context argument
-        if (this.additionalLastArgument) {
+        if (this.hasAdditionalParameter) {
             when (this.method.parameterTypes.last()) {
                 null -> throw ResolverError("Expected at least one argument but got none, this is most likely a bug with graphql-java-tools")
                 options.contextClass -> args.add { environment ->
@@ -123,10 +118,12 @@ internal class MethodFieldResolver(
             }
         }
 
-        return if (args.isEmpty() && isTrivialDataFetcher(this.method)) {
-            LightMethodFieldResolverDataFetcher(createSourceResolver(), this.method, options)
+        return if (numberOfParameters > 0 || isSuspendFunction) {
+            // requires arguments and environment or is a suspend function
+            MethodFieldResolverDataFetcher(createSourceResolver(), this.method, args, options, isSuspendFunction)
         } else {
-            MethodFieldResolverDataFetcher(createSourceResolver(), this.method, args, options)
+            // if there are no parameters an optimized version of the data fetcher can be used
+            LightMethodFieldResolverDataFetcher(createSourceResolver(), this.method, options)
         }
     }
 
@@ -196,10 +193,9 @@ internal class MethodFieldResolverDataFetcher(
     private val method: Method,
     private val args: List<ArgumentPlaceholder>,
     private val options: SchemaParserOptions,
+    private val isSuspendFunction: Boolean
 ) : DataFetcher<Any> {
 
-    private val isSuspendFunction = method.isSuspendFunction()
-
     override fun get(environment: DataFetchingEnvironment): Any? {
         val source = sourceResolver.resolve(environment, null)
         val args = this.args.map { it(environment) }.toTypedArray()
@@ -223,8 +219,7 @@ internal class MethodFieldResolverDataFetcher(
 }
 
 /**
- * Similar to [MethodFieldResolverDataFetcher] but for light data fetchers which do not require the environment to be supplied unless suspend functions or
- * generic wrappers are used.
+ * Similar to [MethodFieldResolverDataFetcher] but for light data fetchers which do not require the environment to be supplied unless generic wrappers are used.
  */
 internal class LightMethodFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
@@ -232,18 +227,10 @@ internal class LightMethodFieldResolverDataFetcher(
     private val options: SchemaParserOptions,
 ) : LightDataFetcher<Any?> {
 
-    private val isSuspendFunction = method.isSuspendFunction()
-
-    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
         val source = sourceResolver.resolve(null, sourceObject)
 
-        return if (isSuspendFunction) {
-            environmentSupplier.get().coroutineScope().future(options.coroutineContextProvider.provide()) {
-                invokeSuspend(source, method, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
-            }
-        } else {
-            invoke(method, source, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
-        }
+        return invoke(method, source, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
     }
 
     override fun get(environment: DataFetchingEnvironment): Any? {
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
index 96c8953e..afbb29f3 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
@@ -44,7 +44,7 @@ internal class PropertyFieldResolverDataFetcher(
     private val field: Field,
 ) : LightDataFetcher<Any> {
 
-    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
         return field.get(sourceResolver.resolve(null, sourceObject))
     }
 
diff --git a/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt b/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
index f1802402..e61e88c9 100644
--- a/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/util/Utils.kt
@@ -61,22 +61,4 @@ internal fun getDocumentation(node: AbstractNode<*>, options: SchemaParserOption
             .trimIndent()
     }
 
-/**
- * Simple heuristic to check if 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)
-
 internal fun String.snakeToCamelCase(): String = split("_").joinToString(separator = "") { it.replaceFirstChar(Char::titlecase) }
diff --git a/src/test/kotlin/graphql/kickstart/tools/util/UtilsTest.kt b/src/test/kotlin/graphql/kickstart/tools/util/UtilsTest.kt
index e8af532e..14cd8dd9 100644
--- a/src/test/kotlin/graphql/kickstart/tools/util/UtilsTest.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/util/UtilsTest.kt
@@ -1,8 +1,5 @@
 package graphql.kickstart.tools.util
 
-import org.junit.Assert
-import org.junit.Test
-
 class UtilsTest {
 
     @Suppress("unused")
@@ -26,19 +23,4 @@ class UtilsTest {
     private class UtilsTestTrivialDataFetcherBean {
         val isBooleanPrimitive = 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")))
-    }
 }

From edafe516d9e4739f660334bd4ee8e297f4a6a401 Mon Sep 17 00:00:00 2001
From: Vojtech Polivka <vojtech@eblock.ca>
Date: Thu, 3 Oct 2024 10:12:47 -0700
Subject: [PATCH 07/12] Adjust comments

---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index f23b3ee4..2479b230 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
     <groupId>com.graphql-java-kickstart</groupId>
     <artifactId>graphql-java-tools</artifactId>
-    <version>14.1.0-LOCAL</version>
+    <version>14.2.0-LOCAL</version>
     <packaging>jar</packaging>
 
     <name>GraphQL Java Tools</name>
@@ -240,6 +240,7 @@
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
+                <!-- keep at 3.4.0 for JitPack to work -->
                 <version>3.4.0</version>
                 <executions>
                     <execution>
@@ -306,7 +307,6 @@
                     <includes>
                         <include>**/*Test.*</include>
                     </includes>
-                    <!--                    or solve this differently? tests fail-->
                     <argLine>--add-reads kotlin.stdlib=kotlinx.coroutines.core</argLine>
                 </configuration>
             </plugin>

From e5ca52b724e588aa379c97732740c80f97ed8626 Mon Sep 17 00:00:00 2001
From: Oryan <oryan.mat@eblock.ca>
Date: Mon, 7 Oct 2024 13:08:34 -0400
Subject: [PATCH 08/12] Reformat

---
 .../kickstart/tools/resolver/FieldResolver.kt   | 17 ++++-------------
 .../tools/resolver/MapFieldResolver.kt          |  4 ++--
 .../tools/resolver/MethodFieldResolver.kt       | 12 ++++++------
 .../tools/resolver/PropertyFieldResolver.kt     |  6 +++---
 4 files changed, 15 insertions(+), 24 deletions(-)

diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
index 440820c8..addf61d7 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolver.kt
@@ -1,10 +1,6 @@
 package graphql.kickstart.tools.resolver
 
 import graphql.kickstart.tools.*
-import graphql.kickstart.tools.GenericType
-import graphql.kickstart.tools.ResolverError
-import graphql.kickstart.tools.ResolverInfo
-import graphql.kickstart.tools.TypeClassMatcher
 import graphql.kickstart.tools.util.JavaType
 import graphql.language.FieldDefinition
 import graphql.schema.DataFetcher
@@ -34,15 +30,10 @@ internal abstract class FieldResolver(
             SourceResolver { _, _ -> this.search.source }
         } else {
             SourceResolver { environment, sourceObject ->
-                val source = if (sourceObject != null) {
-                    // if source object is known, environment is null as an optimization (LightDataFetcher)
-                    sourceObject
-                } else {
-                    environment
-                        ?: throw ResolverError("Expected DataFetchingEnvironment to not be null!")
-                    environment.getSource<Any>()
-                        ?: throw ResolverError("Expected source object to not be null!")
-                }
+                // if source object is known, environment is null as an optimization (LightDataFetcher)
+                val source = sourceObject
+                    ?: environment?.getSource<Any>()
+                    ?: throw ResolverError("Expected DataFetchingEnvironment and source object to not be null!")
 
                 if (!this.genericType.isAssignableFrom(source.javaClass)) {
                     throw ResolverError("Expected source object to be an instance of '${this.genericType.getRawClass().name}' but instead got '${source.javaClass.name}'")
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
index ce48e494..e2b635be 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MapFieldResolver.kt
@@ -52,7 +52,7 @@ internal class MapFieldResolver(
 
 internal class MapFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
-    private val key: String,
+    private val key: String
 ) : LightDataFetcher<Any> {
 
     override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
@@ -64,6 +64,6 @@ internal class MapFieldResolverDataFetcher(
     }
 
     override fun get(environment: DataFetchingEnvironment): Any? {
-        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null)) { environment }
     }
 }
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
index a400b1e7..e7f6946d 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
@@ -31,7 +31,7 @@ internal class MethodFieldResolver(
     field: FieldDefinition,
     search: FieldResolverScanner.Search,
     options: SchemaParserOptions,
-    val method: Method,
+    val method: Method
 ) : FieldResolver(field, search, options, search.type) {
 
     private val log = LoggerFactory.getLogger(javaClass)
@@ -202,10 +202,10 @@ internal class MethodFieldResolverDataFetcher(
 
         return if (isSuspendFunction) {
             environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
-                invokeSuspend(source, method, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
+                invokeSuspend(source, method, args)?.transformWithGenericWrapper(options.genericWrappers) { environment }
             }
         } else {
-            invoke(method, source, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
+            invoke(method, source, args)?.transformWithGenericWrapper(options.genericWrappers) { environment }
         }
     }
 
@@ -224,7 +224,7 @@ internal class MethodFieldResolverDataFetcher(
 internal class LightMethodFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
     private val method: Method,
-    private val options: SchemaParserOptions,
+    private val options: SchemaParserOptions
 ) : LightDataFetcher<Any?> {
 
     override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
@@ -234,7 +234,7 @@ internal class LightMethodFieldResolverDataFetcher(
     }
 
     override fun get(environment: DataFetchingEnvironment): Any? {
-        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null)) { environment }
     }
 
     /**
@@ -249,7 +249,7 @@ internal class LightMethodFieldResolverDataFetcher(
 private fun Any.transformWithGenericWrapper(
     genericWrappers: List<GenericWrapper>,
     environmentSupplier: Supplier<DataFetchingEnvironment>
-): Any? {
+): Any {
     return genericWrappers
         .asSequence()
         .filter { it.type.isInstance(this) }
diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
index afbb29f3..6151181a 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/PropertyFieldResolver.kt
@@ -18,7 +18,7 @@ internal class PropertyFieldResolver(
     field: FieldDefinition,
     search: FieldResolverScanner.Search,
     options: SchemaParserOptions,
-    private val property: Field,
+    private val property: Field
 ) : FieldResolver(field, search, options, property.declaringClass) {
 
     override fun createDataFetcher(): DataFetcher<*> {
@@ -41,7 +41,7 @@ internal class PropertyFieldResolver(
 
 internal class PropertyFieldResolverDataFetcher(
     private val sourceResolver: SourceResolver,
-    private val field: Field,
+    private val field: Field
 ) : LightDataFetcher<Any> {
 
     override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
@@ -49,6 +49,6 @@ internal class PropertyFieldResolverDataFetcher(
     }
 
     override fun get(environment: DataFetchingEnvironment): Any? {
-        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null), { environment })
+        return get(environment.fieldDefinition, sourceResolver.resolve(environment, null)) { environment }
     }
 }

From e9074ec27b8a90f0e99793e4e2f910b723bc38a2 Mon Sep 17 00:00:00 2001
From: Oryan <oryan.mat@eblock.ca>
Date: Tue, 8 Oct 2024 10:20:47 -0400
Subject: [PATCH 09/12] Make nullable

---
 .../kickstart/tools/resolver/MethodFieldResolver.kt       | 2 +-
 .../kickstart/tools/MethodFieldResolverDataFetcherTest.kt | 8 --------
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
index e7f6946d..76fc7f19 100644
--- a/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/resolver/MethodFieldResolver.kt
@@ -227,7 +227,7 @@ internal class LightMethodFieldResolverDataFetcher(
     private val options: SchemaParserOptions
 ) : LightDataFetcher<Any?> {
 
-    override fun get(fieldDefinition: GraphQLFieldDefinition, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
+    override fun get(fieldDefinition: GraphQLFieldDefinition?, sourceObject: Any?, environmentSupplier: Supplier<DataFetchingEnvironment>): Any? {
         val source = sourceResolver.resolve(null, sourceObject)
 
         return invoke(method, source, emptyArray())?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier)
diff --git a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
index 9401feef..481d2a4e 100644
--- a/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
+++ b/src/test/kotlin/graphql/kickstart/tools/MethodFieldResolverDataFetcherTest.kt
@@ -13,8 +13,6 @@ import graphql.language.TypeName
 import graphql.schema.DataFetcher
 import graphql.schema.DataFetchingEnvironment
 import graphql.schema.DataFetchingEnvironmentImpl
-import graphql.schema.GraphQLFieldDefinition
-import graphql.schema.GraphQLObjectType
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
@@ -301,12 +299,6 @@ class MethodFieldResolverDataFetcherTest {
     private fun createEnvironment(source: Any = Object(), arguments: Map<String, Any> = emptyMap(), context: GraphQLContext? = null): DataFetchingEnvironment {
         return DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext())
             .source(source)
-            .fieldDefinition(
-                GraphQLFieldDefinition.newFieldDefinition()
-                    .name("ignored")
-                    .type(GraphQLObjectType.newObject().name("ignored").build())
-                    .build()
-            )
             .arguments(arguments)
             .graphQLContext(context)
             .build()

From d408078bcca217151d0bbb8478acee8532279109 Mon Sep 17 00:00:00 2001
From: Oryan <oryan.mat@eblock.ca>
Date: Tue, 8 Oct 2024 11:09:27 -0400
Subject: [PATCH 10/12] Add comment

---
 pom.xml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pom.xml b/pom.xml
index 2479b230..e9236fa1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,6 +307,7 @@
                     <includes>
                         <include>**/*Test.*</include>
                     </includes>
+                    <!-- instruct kotlin std to read the coroutines module -->
                     <argLine>--add-reads kotlin.stdlib=kotlinx.coroutines.core</argLine>
                 </configuration>
             </plugin>

From fd06ad24924b03be3f90479158237613e1673696 Mon Sep 17 00:00:00 2001
From: Oryan <oryan.mat@eblock.ca>
Date: Tue, 8 Oct 2024 11:11:56 -0400
Subject: [PATCH 11/12] Revert version

---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index e9236fa1..b5b67edc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
     <groupId>com.graphql-java-kickstart</groupId>
     <artifactId>graphql-java-tools</artifactId>
-    <version>14.2.0-LOCAL</version>
+    <version>13.1.2-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>GraphQL Java Tools</name>

From 5a6dca0d26002267fb27a776b2f04d40d0453b90 Mon Sep 17 00:00:00 2001
From: Oryan <oryan.mat@eblock.ca>
Date: Tue, 8 Oct 2024 11:22:25 -0400
Subject: [PATCH 12/12] Remove argline

---
 pom.xml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index b5b67edc..8e1c131b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,8 +307,6 @@
                     <includes>
                         <include>**/*Test.*</include>
                     </includes>
-                    <!-- instruct kotlin std to read the coroutines module -->
-                    <argLine>--add-reads kotlin.stdlib=kotlinx.coroutines.core</argLine>
                 </configuration>
             </plugin>
             <plugin>