@@ -22,216 +22,216 @@ import kotlin.reflect.jvm.kotlinFunction
2222 */
2323internal class MethodFieldResolver (field : FieldDefinition , search : FieldResolverScanner .Search , options : SchemaParserOptions , val method : Method ) : FieldResolver(field, search, options, search.type) {
2424
25- companion object {
26- fun isBatched (method : Method , search : FieldResolverScanner .Search ): Boolean {
27- if (method.getAnnotation(Batched ::class .java) != null ) {
28- if (! search.allowBatched) {
29- throw ResolverError (" The @Batched annotation is only allowed on non-root resolver methods, but it was found on ${search.type.unwrap().name} #${method.name} !" )
30- }
31-
32- return true
33- }
34- return false
25+ companion object {
26+ fun isBatched (method : Method , search : FieldResolverScanner .Search ): Boolean {
27+ if (method.getAnnotation(Batched ::class .java) != null ) {
28+ if (! search.allowBatched) {
29+ throw ResolverError (" The @Batched annotation is only allowed on non-root resolver methods, but it was found on ${search.type.unwrap().name} #${method.name} !" )
3530 }
36- }
3731
38- private val additionalLastArgument =
39- try {
40- method.kotlinFunction?.valueParameters?.size ? : method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1 )
41- } catch (e: InternalError ) {
42- method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1 )
43- }
44-
45- override fun createDataFetcher (): DataFetcher <* > {
46- val batched = isBatched(method, search)
47- val args = mutableListOf<ArgumentPlaceholder >()
48- val mapper = options.objectMapperProvider.provide(field)
49-
50- // Add source argument if this is a resolver (but not a root resolver)
51- if (this .search.requiredFirstParameterType != null ) {
52- val expectedType = if (batched) Iterable ::class .java else this .search.requiredFirstParameterType
53-
54- args.add { environment ->
55- val source = environment.getSource<Any >()
56- if (! (expectedType.isAssignableFrom(source.javaClass))) {
57- throw ResolverError (" Source type (${source.javaClass.name} ) is not expected type (${expectedType.name} )!" )
58- }
59-
60- source
61- }
32+ return true
33+ }
34+ return false
35+ }
36+ }
37+
38+ private val additionalLastArgument =
39+ try {
40+ method.kotlinFunction?.valueParameters?.size ? : method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1 )
41+ } catch (e: InternalError ) {
42+ method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1 )
43+ }
44+
45+ override fun createDataFetcher (): DataFetcher <* > {
46+ val batched = isBatched(method, search)
47+ val args = mutableListOf<ArgumentPlaceholder >()
48+ val mapper = options.objectMapperProvider.provide(field)
49+
50+ // Add source argument if this is a resolver (but not a root resolver)
51+ if (this .search.requiredFirstParameterType != null ) {
52+ val expectedType = if (batched) Iterable ::class .java else this .search.requiredFirstParameterType
53+
54+ args.add { environment ->
55+ val source = environment.getSource<Any >()
56+ if (! (expectedType.isAssignableFrom(source.javaClass))) {
57+ throw ResolverError (" Source type (${source.javaClass.name} ) is not expected type (${expectedType.name} )!" )
6258 }
6359
64- // Add an argument for each argument defined in the GraphQL schema
65- this .field.inputValueDefinitions.forEachIndexed { index, definition ->
66-
67- val genericParameterType = this .getJavaMethodParameterType(index)
68- ? : throw ResolverError (" Missing method type at position ${this .getJavaMethodParameterIndex(index)} , this is most likely a bug with graphql-java-tools" )
69-
70- val isNonNull = definition.type is NonNullType
71- val isOptional = this .genericType.getRawClass(genericParameterType) == Optional ::class .java
72-
73- val typeReference = object : TypeReference <Any >() {
74- override fun getType () = genericParameterType
75- }
76-
77- args.add { environment ->
78- val value = environment.arguments[definition.name] ? : if (isNonNull) {
79- throw ResolverError (" Missing required argument with name '${definition.name} ', this is most likely a bug with graphql-java-tools" )
80- } else {
81- null
82- }
83-
84- if (value == null && isOptional) {
85- if (environment.containsArgument(definition.name)) {
86- return @add Optional .empty<Any >()
87- } else {
88- return @add null
89- }
90- }
91-
92- if (value != null
93- && genericParameterType.unwrap().isAssignableFrom(value.javaClass)
94- && isScalarType(environment, definition.type, genericParameterType)) {
95- return @add value
96- }
97-
98- return @add mapper.convertValue(value, typeReference)
99- }
100- }
60+ source
61+ }
62+ }
10163
102- // Add DataFetchingEnvironment/Context argument
103- if (this .additionalLastArgument) {
104- when (this .method.parameterTypes.last()) {
105- null -> throw ResolverError (" Expected at least one argument but got none, this is most likely a bug with graphql-java-tools" )
106- options.contextClass -> args.add { environment -> environment.getContext() }
107- else -> args.add { environment -> environment }
108- }
109- }
64+ // Add an argument for each argument defined in the GraphQL schema
65+ this .field.inputValueDefinitions.forEachIndexed { index, definition ->
11066
111- return if (batched) {
112- BatchedMethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
113- } else {
114- if (args.isEmpty() && isTrivialDataFetcher(this .method)) {
115- TrivialMethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
116- } else {
117- MethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
118- }
67+ val genericParameterType = this .getJavaMethodParameterType(index)
68+ ? : throw ResolverError (" Missing method type at position ${this .getJavaMethodParameterIndex(index)} , this is most likely a bug with graphql-java-tools" )
11969
120- }
121- }
70+ val isNonNull = definition.type is NonNullType
71+ val isOptional = this .genericType.getRawClass(genericParameterType) == Optional :: class .java
12272
123- private fun isScalarType (environment : DataFetchingEnvironment , type : Type <* >, genericParameterType : JavaType ): Boolean =
124- when (type) {
125- is ListType -> List ::class .java.isAssignableFrom(this .genericType.getRawClass(genericParameterType))
126- && isScalarType(environment, type.type, this .genericType.unwrapGenericType(genericParameterType))
127- is TypeName -> environment.graphQLSchema?.getType(type.name)?.let { isScalar(it) } ? : false
128- is NonNullType -> isScalarType(environment, type.type, genericParameterType)
129- else -> false
130- }
131-
132- override fun scanForMatches (): List <TypeClassMatcher .PotentialMatch > {
133- val batched = isBatched(method, search)
134- val unwrappedGenericType = genericType.unwrapGenericType(try {
135- method.kotlinFunction?.returnType?.javaType ? : method.genericReturnType
136- } catch (e: InternalError ) {
137- method.genericReturnType
138- })
139- val returnValueMatch = TypeClassMatcher .PotentialMatch .returnValue(field.type, unwrappedGenericType, genericType, SchemaClassScanner .ReturnValueReference (method), batched)
140-
141- return field.inputValueDefinitions.mapIndexed { i, inputDefinition ->
142- TypeClassMatcher .PotentialMatch .parameterType(inputDefinition.type, getJavaMethodParameterType(i)!! , genericType, SchemaClassScanner .MethodParameterReference (method, i), batched)
143- } + listOf (returnValueMatch)
144- }
73+ val typeReference = object : TypeReference <Any >() {
74+ override fun getType () = genericParameterType
75+ }
14576
146- private fun getIndexOffset (): Int {
147- return if (resolverInfo is DataClassTypeResolverInfo && ! method.declaringClass.isAssignableFrom(resolverInfo.dataClassType) ) {
148- 1
77+ args.add { environment ->
78+ val value = environment.arguments[definition.name] ? : if (isNonNull ) {
79+ throw ResolverError ( " Missing required argument with name ' ${definition.name} ', this is most likely a bug with graphql-java-tools " )
14980 } else {
150- 0
81+ null
15182 }
152- }
153-
154- private fun getJavaMethodParameterIndex (index : Int ) = index + getIndexOffset()
15583
156- private fun getJavaMethodParameterType (index : Int ): JavaType ? {
157- val methodIndex = getJavaMethodParameterIndex(index)
158- val parameters = method.parameterTypes
84+ if (value == null && isOptional) {
85+ if (environment.containsArgument(definition.name)) {
86+ return @add Optional .empty<Any >()
87+ } else {
88+ return @add null
89+ }
90+ }
15991
160- return if (parameters.size > methodIndex) {
161- method.genericParameterTypes[getJavaMethodParameterIndex(index)]
162- } else {
163- null
92+ if (value != null
93+ && genericParameterType.unwrap().isAssignableFrom(value.javaClass)
94+ && isScalarType(environment, definition.type, genericParameterType)) {
95+ return @add value
16496 }
97+
98+ return @add mapper.convertValue(value, typeReference)
99+ }
165100 }
166101
167- override fun toString () = " MethodFieldResolver{method=$method }"
168- }
102+ // Add DataFetchingEnvironment/Context argument
103+ if (this .additionalLastArgument) {
104+ when (this .method.parameterTypes.last()) {
105+ null -> throw ResolverError (" Expected at least one argument but got none, this is most likely a bug with graphql-java-tools" )
106+ options.contextClass -> args.add { environment -> environment.getContext() }
107+ else -> args.add { environment -> environment }
108+ }
109+ }
169110
170- open class MethodFieldResolverDataFetcher (private val sourceResolver : SourceResolver , method : Method , private val args : List <ArgumentPlaceholder >, private val options : SchemaParserOptions ) : DataFetcher<Any> {
111+ return if (batched) {
112+ BatchedMethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
113+ } else {
114+ if (args.isEmpty() && isTrivialDataFetcher(this .method)) {
115+ TrivialMethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
116+ } else {
117+ MethodFieldResolverDataFetcher (getSourceResolver(), this .method, args, options)
118+ }
171119
172- // Convert to reflactasm reflection
173- private val methodAccess = MethodAccess .get(method.declaringClass)!!
174- private val methodIndex = methodAccess.getIndex(method.name, * method.parameterTypes)
175- private val isSuspendFunction = try {
176- method.kotlinFunction?.isSuspend == true
120+ }
121+ }
122+
123+ private fun isScalarType (environment : DataFetchingEnvironment , type : Type <* >, genericParameterType : JavaType ): Boolean =
124+ when (type) {
125+ is ListType -> List ::class .java.isAssignableFrom(this .genericType.getRawClass(genericParameterType))
126+ && isScalarType(environment, type.type, this .genericType.unwrapGenericType(genericParameterType))
127+ is TypeName -> environment.graphQLSchema?.getType(type.name)?.let { isScalar(it) } ? : false
128+ is NonNullType -> isScalarType(environment, type.type, genericParameterType)
129+ else -> false
130+ }
131+
132+ override fun scanForMatches (): List <TypeClassMatcher .PotentialMatch > {
133+ val batched = isBatched(method, search)
134+ val unwrappedGenericType = genericType.unwrapGenericType(try {
135+ method.kotlinFunction?.returnType?.javaType ? : method.genericReturnType
177136 } catch (e: InternalError ) {
178- false
137+ method.genericReturnType
138+ })
139+ val returnValueMatch = TypeClassMatcher .PotentialMatch .returnValue(field.type, unwrappedGenericType, genericType, SchemaClassScanner .ReturnValueReference (method), batched)
140+
141+ return field.inputValueDefinitions.mapIndexed { i, inputDefinition ->
142+ TypeClassMatcher .PotentialMatch .parameterType(inputDefinition.type, getJavaMethodParameterType(i)!! , genericType, SchemaClassScanner .MethodParameterReference (method, i), batched)
143+ } + listOf (returnValueMatch)
144+ }
145+
146+ private fun getIndexOffset (): Int {
147+ return if (resolverInfo is DataClassTypeResolverInfo && ! method.declaringClass.isAssignableFrom(resolverInfo.dataClassType)) {
148+ 1
149+ } else {
150+ 0
179151 }
152+ }
180153
181- private class CompareGenericWrappers {
182- companion object : Comparator <GenericWrapper > {
183- override fun compare (w1 : GenericWrapper , w2 : GenericWrapper ): Int = when {
184- w1.type.isAssignableFrom(w2.type) -> 1
185- else -> - 1
186- }
187- }
188- }
154+ private fun getJavaMethodParameterIndex (index : Int ) = index + getIndexOffset()
189155
190- override fun get ( environment : DataFetchingEnvironment ): Any ? {
191- val source = sourceResolver(environment )
192- val args = this .args.map { it(environment) }.toTypedArray()
156+ private fun getJavaMethodParameterType ( index : Int ): JavaType ? {
157+ val methodIndex = getJavaMethodParameterIndex(index )
158+ val parameters = method.parameterTypes
193159
194- return if (isSuspendFunction) {
195- environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
196- methodAccess.invokeSuspend(source, methodIndex, args)?.transformWithGenericWrapper(environment)
197- }
198- } else {
199- methodAccess.invoke(source, methodIndex, * args)?.transformWithGenericWrapper(environment)
200- }
160+ return if (parameters.size > methodIndex) {
161+ method.genericParameterTypes[getJavaMethodParameterIndex(index)]
162+ } else {
163+ null
201164 }
165+ }
202166
203- private fun Any.transformWithGenericWrapper (environment : DataFetchingEnvironment ): Any? {
204- return options.genericWrappers
205- .asSequence()
206- .filter { it.type.isInstance(this ) }
207- .sortedWith(CompareGenericWrappers )
208- .firstOrNull()
209- ?.transformer?.invoke(this , environment) ? : this
210- }
167+ override fun toString () = " MethodFieldResolver{method=$method }"
168+ }
211169
212- /* *
213- * Function that return the object used to fetch the data
214- * It can be a DataFetcher or an entity
215- */
216- @Suppress(" unused" )
217- open fun getWrappedFetchingObject (environment : DataFetchingEnvironment ): Any {
218- return sourceResolver(environment)
170+ open class MethodFieldResolverDataFetcher (private val sourceResolver : SourceResolver , method : Method , private val args : List <ArgumentPlaceholder >, private val options : SchemaParserOptions ) : DataFetcher<Any> {
171+
172+ // Convert to reflactasm reflection
173+ private val methodAccess = MethodAccess .get(method.declaringClass)!!
174+ private val methodIndex = methodAccess.getIndex(method.name, * method.parameterTypes)
175+ private val isSuspendFunction = try {
176+ method.kotlinFunction?.isSuspend == true
177+ } catch (e: InternalError ) {
178+ false
179+ }
180+
181+ private class CompareGenericWrappers {
182+ companion object : Comparator <GenericWrapper > {
183+ override fun compare (w1 : GenericWrapper , w2 : GenericWrapper ): Int = when {
184+ w1.type.isAssignableFrom(w2.type) -> 1
185+ else -> - 1
186+ }
187+ }
188+ }
189+
190+ override fun get (environment : DataFetchingEnvironment ): Any? {
191+ val source = sourceResolver(environment)
192+ val args = this .args.map { it(environment) }.toTypedArray()
193+
194+ return if (isSuspendFunction) {
195+ environment.coroutineScope().future(options.coroutineContextProvider.provide()) {
196+ methodAccess.invokeSuspend(source, methodIndex, args)?.transformWithGenericWrapper(environment)
197+ }
198+ } else {
199+ methodAccess.invoke(source, methodIndex, * args)?.transformWithGenericWrapper(environment)
219200 }
201+ }
202+
203+ private fun Any.transformWithGenericWrapper (environment : DataFetchingEnvironment ): Any? {
204+ return options.genericWrappers
205+ .asSequence()
206+ .filter { it.type.isInstance(this ) }
207+ .sortedWith(CompareGenericWrappers )
208+ .firstOrNull()
209+ ?.transformer?.invoke(this , environment) ? : this
210+ }
211+
212+ /* *
213+ * Function that return the object used to fetch the data
214+ * It can be a DataFetcher or an entity
215+ */
216+ @Suppress(" unused" )
217+ open fun getWrappedFetchingObject (environment : DataFetchingEnvironment ): Any {
218+ return sourceResolver(environment)
219+ }
220220}
221221
222- open class TrivialMethodFieldResolverDataFetcher (private val sourceResolver : SourceResolver , method : Method , private val args : List <ArgumentPlaceholder >, private val options : SchemaParserOptions ) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options), TrivialDataFetcher<Any> {
222+ open class TrivialMethodFieldResolverDataFetcher (sourceResolver : SourceResolver , method : Method , args : List <ArgumentPlaceholder >, options : SchemaParserOptions ) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options), TrivialDataFetcher<Any> {
223223
224224}
225225
226226private suspend inline fun MethodAccess.invokeSuspend (target : Any , methodIndex : Int , args : Array <Any ?>): Any? {
227- return suspendCoroutineUninterceptedOrReturn { continuation ->
228- invoke(target, methodIndex, * args + continuation)
229- }
227+ return suspendCoroutineUninterceptedOrReturn { continuation ->
228+ invoke(target, methodIndex, * args + continuation)
229+ }
230230}
231231
232232class BatchedMethodFieldResolverDataFetcher (sourceResolver : SourceResolver , method : Method , args : List <ArgumentPlaceholder >, options : SchemaParserOptions ) : MethodFieldResolverDataFetcher(sourceResolver, method, args, options) {
233- @Batched
234- override fun get (environment : DataFetchingEnvironment ) = super .get(environment)
233+ @Batched
234+ override fun get (environment : DataFetchingEnvironment ) = super .get(environment)
235235}
236236
237237internal typealias ArgumentPlaceholder = (DataFetchingEnvironment ) -> Any?
0 commit comments