@@ -2,7 +2,6 @@ package graphql.kickstart.tools.resolver
2
2
3
3
import com.fasterxml.jackson.core.type.TypeReference
4
4
import graphql.GraphQLContext
5
- import graphql.TrivialDataFetcher
6
5
import graphql.kickstart.tools.*
7
6
import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper
8
7
import graphql.kickstart.tools.util.JavaType
@@ -12,12 +11,15 @@ import graphql.kickstart.tools.util.unwrap
12
11
import graphql.language.*
13
12
import graphql.schema.DataFetcher
14
13
import graphql.schema.DataFetchingEnvironment
14
+ import graphql.schema.GraphQLFieldDefinition
15
15
import graphql.schema.GraphQLTypeUtil.isScalar
16
+ import graphql.schema.LightDataFetcher
16
17
import kotlinx.coroutines.future.future
17
18
import org.slf4j.LoggerFactory
18
19
import java.lang.reflect.InvocationTargetException
19
20
import java.lang.reflect.Method
20
21
import java.util.*
22
+ import java.util.function.Supplier
21
23
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
22
24
import kotlin.reflect.full.valueParameters
23
25
import kotlin.reflect.jvm.javaType
@@ -122,9 +124,9 @@ internal class MethodFieldResolver(
122
124
}
123
125
124
126
return if (args.isEmpty() && isTrivialDataFetcher(this .method)) {
125
- TrivialMethodFieldResolverDataFetcher (getSourceResolver (), this .method, args , options)
127
+ LightMethodFieldResolverDataFetcher (createSourceResolver (), this .method, options)
126
128
} else {
127
- MethodFieldResolverDataFetcher (getSourceResolver (), this .method, args, options)
129
+ MethodFieldResolverDataFetcher (createSourceResolver (), this .method, args, options)
128
130
}
129
131
}
130
132
@@ -189,69 +191,102 @@ internal class MethodFieldResolver(
189
191
override fun toString () = " MethodFieldResolver{method=$method }"
190
192
}
191
193
192
- internal open class MethodFieldResolverDataFetcher (
194
+ internal class MethodFieldResolverDataFetcher (
193
195
private val sourceResolver : SourceResolver ,
194
- method : Method ,
196
+ private val method : Method ,
195
197
private val args : List <ArgumentPlaceholder >,
196
198
private val options : SchemaParserOptions ,
197
199
) : DataFetcher<Any> {
198
200
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()
205
202
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 })
211
210
}
211
+ } else {
212
+ invoke(method, source, args)?.transformWithGenericWrapper(options.genericWrappers, { environment })
212
213
}
213
214
}
214
215
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)
218
239
219
240
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 )
222
243
}
223
244
} else {
224
- invoke(resolverMethod , source, args) ?.transformWithGenericWrapper(environment )
245
+ invoke(method , source, emptyArray()) ?.transformWithGenericWrapper(options.genericWrappers, environmentSupplier )
225
246
}
226
247
}
227
248
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 })
235
251
}
236
252
237
253
/* *
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.
240
255
*/
241
256
@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 )
244
259
}
245
260
}
246
261
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
+ }
255
290
256
291
private suspend inline fun invokeSuspend (target : Any , resolverMethod : Method , args : Array <Any ?>): Any? {
257
292
return suspendCoroutineUninterceptedOrReturn { continuation ->
0 commit comments