@@ -2,7 +2,6 @@ package graphql.kickstart.tools.resolver
22
33import com.fasterxml.jackson.core.type.TypeReference
44import graphql.GraphQLContext
5- import graphql.TrivialDataFetcher
65import graphql.kickstart.tools.*
76import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper
87import graphql.kickstart.tools.util.JavaType
@@ -12,12 +11,15 @@ import graphql.kickstart.tools.util.unwrap
1211import graphql.language.*
1312import graphql.schema.DataFetcher
1413import graphql.schema.DataFetchingEnvironment
14+ import graphql.schema.GraphQLFieldDefinition
1515import graphql.schema.GraphQLTypeUtil.isScalar
16+ import graphql.schema.LightDataFetcher
1617import kotlinx.coroutines.future.future
1718import org.slf4j.LoggerFactory
1819import java.lang.reflect.InvocationTargetException
1920import java.lang.reflect.Method
2021import java.util.*
22+ import java.util.function.Supplier
2123import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
2224import kotlin.reflect.full.valueParameters
2325import kotlin.reflect.jvm.javaType
@@ -122,9 +124,9 @@ internal class MethodFieldResolver(
122124 }
123125
124126 return if (args.isEmpty() && isTrivialDataFetcher(this .method)) {
125- TrivialMethodFieldResolverDataFetcher (getSourceResolver (), this .method, args , options)
127+ LightMethodFieldResolverDataFetcher (createSourceResolver (), this .method, options)
126128 } else {
127- MethodFieldResolverDataFetcher (getSourceResolver (), this .method, args, options)
129+ MethodFieldResolverDataFetcher (createSourceResolver (), this .method, args, options)
128130 }
129131 }
130132
@@ -189,69 +191,102 @@ internal class MethodFieldResolver(
189191 override fun toString () = " MethodFieldResolver{method=$method }"
190192}
191193
192- internal open class MethodFieldResolverDataFetcher (
194+ internal class MethodFieldResolverDataFetcher (
193195 private val sourceResolver : SourceResolver ,
194- method : Method ,
196+ private val method : Method ,
195197 private val args : List <ArgumentPlaceholder >,
196198 private val options : SchemaParserOptions ,
197199) : DataFetcher<Any> {
198200
199- private val resolverMethod = method
200- private val isSuspendFunction = try {
201- method.kotlinFunction?.isSuspend == true
202- } catch (e: InternalError ) {
203- false
204- }
201+ private val isSuspendFunction = method.isSuspendFunction()
205202
206- private class CompareGenericWrappers {
207- companion object : Comparator <GenericWrapper > {
208- override fun compare (w1 : GenericWrapper , w2 : GenericWrapper ): Int = when {
209- w1.type.isAssignableFrom(w2.type) -> 1
210- else -> - 1
203+ override fun get (environment : DataFetchingEnvironment ): Any? {
204+ val source = sourceResolver.resolve(environment, null )
205+ val args = this .args.map { it(environment) }.toTypedArray()
206+
207+ return if (isSuspendFunction) {
208+ environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
209+ invokeSuspend(source, method, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
211210 }
211+ } else {
212+ invoke(method, source, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
212213 }
213214 }
214215
215- override fun get (environment : DataFetchingEnvironment ): Any? {
216- val source = sourceResolver(environment)
217- val args = this .args.map { it(environment) }.toTypedArray()
216+ /* *
217+ * Function that returns the object used to fetch the data. It can be a DataFetcher or an entity.
218+ */
219+ @Suppress(" unused" )
220+ fun getWrappedFetchingObject (environment : DataFetchingEnvironment ): Any {
221+ return sourceResolver.resolve(environment, null )
222+ }
223+ }
224+
225+ /* *
226+ * Similar to [MethodFieldResolverDataFetcher] but for light data fetchers which do not require the environment to be supplied unless suspend functions or
227+ * generic wrappers are used.
228+ */
229+ internal class LightMethodFieldResolverDataFetcher (
230+ private val sourceResolver : SourceResolver ,
231+ private val method : Method ,
232+ private val options : SchemaParserOptions ,
233+ ) : LightDataFetcher<Any?> {
234+
235+ private val isSuspendFunction = method.isSuspendFunction()
236+
237+ override fun get (fieldDefinition : GraphQLFieldDefinition , sourceObject : Any , environmentSupplier : Supplier <DataFetchingEnvironment >): Any? {
238+ val source = sourceResolver.resolve(null , sourceObject)
218239
219240 return if (isSuspendFunction) {
220- environment .coroutineScope().future(options.coroutineContextProvider.provide()) {
221- invokeSuspend(source, resolverMethod, args) ?.transformWithGenericWrapper(environment )
241+ environmentSupplier.get() .coroutineScope().future(options.coroutineContextProvider.provide()) {
242+ invokeSuspend(source, method, emptyArray()) ?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier )
222243 }
223244 } else {
224- invoke(resolverMethod , source, args) ?.transformWithGenericWrapper(environment )
245+ invoke(method , source, emptyArray()) ?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier )
225246 }
226247 }
227248
228- private fun Any.transformWithGenericWrapper (environment : DataFetchingEnvironment ): Any? {
229- return options.genericWrappers
230- .asSequence()
231- .filter { it.type.isInstance(this ) }
232- .sortedWith(CompareGenericWrappers )
233- .firstOrNull()
234- ?.transformer?.invoke(this , environment) ? : this
249+ override fun get (environment : DataFetchingEnvironment ): Any? {
250+ return get(environment.fieldDefinition, sourceResolver.resolve(environment, null ), { environment })
235251 }
236252
237253 /* *
238- * Function that returns the object used to fetch the data.
239- * It can be a DataFetcher or an entity.
254+ * Function that returns the object used to fetch the data. It can be a DataFetcher or an entity.
240255 */
241256 @Suppress(" unused" )
242- open fun getWrappedFetchingObject (environment : DataFetchingEnvironment ): Any {
243- return sourceResolver(environment)
257+ fun getWrappedFetchingObject (environment : DataFetchingEnvironment ): Any {
258+ return sourceResolver.resolve (environment, null )
244259 }
245260}
246261
247- // TODO use graphql.schema.LightDataFetcher
248- internal class TrivialMethodFieldResolverDataFetcher (
249- sourceResolver : SourceResolver ,
250- method : Method ,
251- args : List <ArgumentPlaceholder >,
252- options : SchemaParserOptions ,
253- ) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options),
254- TrivialDataFetcher <Any > // just to mark it for tracing and optimizations
262+ private fun Any.transformWithGenericWrapper (
263+ genericWrappers : List <GenericWrapper >,
264+ environmentSupplier : Supplier <DataFetchingEnvironment >
265+ ): Any? {
266+ return genericWrappers
267+ .asSequence()
268+ .filter { it.type.isInstance(this ) }
269+ .sortedWith(CompareGenericWrappers )
270+ .firstOrNull()
271+ ?.transformer?.invoke(this , environmentSupplier.get()) ? : this
272+ }
273+
274+ private class CompareGenericWrappers {
275+ companion object : Comparator <GenericWrapper > {
276+ override fun compare (w1 : GenericWrapper , w2 : GenericWrapper ): Int = when {
277+ w1.type.isAssignableFrom(w2.type) -> 1
278+ else -> - 1
279+ }
280+ }
281+ }
282+
283+ private fun Method.isSuspendFunction (): Boolean {
284+ return try {
285+ this .kotlinFunction?.isSuspend == true
286+ } catch (e: InternalError ) {
287+ false
288+ }
289+ }
255290
256291private suspend inline fun invokeSuspend (target : Any , resolverMethod : Method , args : Array <Any ?>): Any? {
257292 return suspendCoroutineUninterceptedOrReturn { continuation ->
0 commit comments