Skip to content

Commit 0d857e3

Browse files
committed
Updated tests to find a solution for generics
1 parent db3c047 commit 0d857e3

File tree

7 files changed

+58
-22
lines changed

7 files changed

+58
-22
lines changed

src/main/kotlin/com/coxautodev/graphql/tools/FieldResolverScanner.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import org.apache.commons.lang3.reflect.FieldUtils
99
import org.slf4j.LoggerFactory
1010
import java.lang.reflect.Modifier
1111
import java.lang.reflect.ParameterizedType
12-
import java.lang.reflect.TypeVariable
1312

1413
/**
1514
* @author Andrew Potter
@@ -22,7 +21,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
2221
private val log = LoggerFactory.getLogger(FieldResolverScanner::class.java)
2322

2423
fun getAllMethods(type: Class<*>) =
25-
(type.methods.toList() + ClassUtils.getAllSuperclasses(type).flatMap { it.methods.toList() })
24+
(type.declaredMethods.toList() + ClassUtils.getAllSuperclasses(type).flatMap { it.methods.toList() })
2625
.asSequence()
2726
.filter { !it.isSynthetic }
2827
.filter { !Modifier.isPrivate(it.modifiers) }
@@ -74,7 +73,6 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
7473
private fun isBoolean(type: GraphQLLangType) = type.unwrap().let { it is TypeName && it.name == Scalars.GraphQLBoolean.name }
7574

7675
private fun findResolverMethod(field: FieldDefinition, search: Search): java.lang.reflect.Method? {
77-
7876
val methods = getAllMethods(search.type)
7977
val argumentCount = field.inputValueDefinitions.size + if (search.requiredFirstParameterType != null) 1 else 0
8078
val name = field.name

src/main/kotlin/com/coxautodev/graphql/tools/GenericType.kt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import java.lang.reflect.TypeVariable
99
/**
1010
* @author Andrew Potter
1111
*/
12-
open internal class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) {
12+
internal open class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) {
1313

1414
fun isTypeAssignableFromRawClass(type: ParameterizedType, clazz: Class<*>) =
15-
clazz.isAssignableFrom(getRawClass(type.rawType))
15+
clazz.isAssignableFrom(getRawClass(type.rawType))
1616

1717
fun getRawClass() = getRawClass(mostSpecificType)
1818

@@ -21,29 +21,30 @@ open internal class GenericType(protected val mostSpecificType: JavaType, protec
2121
fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, mostSpecificType)
2222

2323
fun relativeToPotentialParent(declaringType: JavaType): RelativeTo {
24-
if(declaringType !is Class<*>) {
24+
if (declaringType !is Class<*> || declaringType.isInterface) {
2525
return relativeToType(declaringType)
2626
}
2727

2828
val type = getGenericSuperType(mostSpecificType, declaringType)
29-
if(type == null) {
29+
if (type == null) {
3030
error("Unable to find generic type of class ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}")
3131
} else {
3232
return relativeToType(type)
3333
}
3434
}
35+
3536
fun relativeToType(declaringType: JavaType) = RelativeTo(declaringType, mostSpecificType, options)
3637

3738
fun getGenericInterface(targetInterface: Class<*>) = getGenericInterface(mostSpecificType, targetInterface)
3839

3940
private fun getGenericInterface(type: JavaType?, targetInterface: Class<*>): JavaType? {
40-
if(type == null) {
41+
if (type == null) {
4142
return null
4243
}
4344

4445
val raw = type as? Class<*> ?: getRawClass(type)
4546

46-
if(raw == targetInterface) {
47+
if (raw == targetInterface) {
4748
return type
4849
}
4950

@@ -59,32 +60,32 @@ open internal class GenericType(protected val mostSpecificType: JavaType, protec
5960
fun getGenericSuperType(targetSuperClass: Class<*>) = getGenericSuperType(mostSpecificType, targetSuperClass)
6061

6162
private fun getGenericSuperType(type: JavaType?, targetSuperClass: Class<*>): JavaType? {
62-
if(type == null) {
63+
if (type == null) {
6364
return null
6465
}
6566

6667
val raw = type as? Class<*> ?: TypeUtils.getRawType(type, type)
6768

68-
if(raw == targetSuperClass) {
69+
if (raw == targetSuperClass) {
6970
return type
7071
}
7172

7273
return getGenericSuperType(raw.genericSuperclass, targetSuperClass)
7374
}
7475

75-
class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions): GenericType(mostSpecificType, options) {
76+
class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions) : GenericType(mostSpecificType, options) {
7677

7778
/**
7879
* Unwrap certain Java types to find the "real" class.
7980
*/
8081
fun unwrapGenericType(type: JavaType): JavaType {
81-
return when(type) {
82+
return when (type) {
8283
is ParameterizedType -> {
8384
val rawType = type.rawType
8485
val genericType = options.genericWrappers.find { it.type == rawType } ?: return type
8586

8687
val typeArguments = type.actualTypeArguments
87-
if(typeArguments.size <= genericType.index) {
88+
if (typeArguments.size <= genericType.index) {
8889
throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!")
8990
}
9091

@@ -96,15 +97,17 @@ open internal class GenericType(protected val mostSpecificType: JavaType, protec
9697

9798
return unwrapGenericType(unwrapsTo)
9899
}
99-
is Class<*> -> if(type.isPrimitive) Primitives.wrap(type) else type
100+
is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type
100101
is TypeVariable<*> -> {
101-
if(declaringType !is ParameterizedType) {
102+
if (declaringType !is ParameterizedType) {
102103
error("Could not resolve type variable '${TypeUtils.toLongString(type)}' because declaring type is not parameterized: ${TypeUtils.toString(declaringType)}")
103104
}
104105

105-
unwrapGenericType(TypeUtils.determineTypeArguments(getRawClass(mostSpecificType), declaringType)[type] ?: error("No type variable found for: ${TypeUtils.toLongString(type)}"))
106+
unwrapGenericType(TypeUtils.determineTypeArguments(getRawClass(mostSpecificType), declaringType)[type]
107+
?: error("No type variable found for: ${TypeUtils.toLongString(type)}"))
106108
}
107-
is WildcardTypeImpl -> type.upperBounds.firstOrNull() ?: throw error("Unable to unwrap type, wildcard has no upper bound: $type")
109+
is WildcardTypeImpl -> type.upperBounds.firstOrNull()
110+
?: throw error("Unable to unwrap type, wildcard has no upper bound: $type")
108111
else -> error("Unable to unwrap type: $type")
109112
}
110113
}

src/main/kotlin/com/coxautodev/graphql/tools/ResolverInfo.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ internal class MultiResolverInfo(val resolverInfoList: List<NormalResolverInfo>)
6464

6565
override fun getFieldSearches(): List<FieldResolverScanner.Search> {
6666
return resolverInfoList
67+
.asSequence()
6768
.map { FieldResolverScanner.Search(it.resolverType, this, it.resolver, dataClassType, true) }
6869
.plus(FieldResolverScanner.Search(dataClassType, this, null))
70+
.toList()
6971
}
7072
}
7173

src/main/kotlin/com/coxautodev/graphql/tools/SchemaClassScanner.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
412412
}
413413

414414
class ReturnValueReference(private val method: Method) : Reference() {
415-
fun getMethod() = method;
415+
fun getMethod() = method
416416
override fun getDescription() = "return type of method $method"
417417
}
418418

src/main/kotlin/com/coxautodev/graphql/tools/TypeClassMatcher.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import graphql.language.NonNullType
66
import graphql.language.ScalarTypeDefinition
77
import graphql.language.TypeDefinition
88
import graphql.language.TypeName
9-
import graphql.relay.Connection
109
import graphql.schema.idl.ScalarInfo
11-
import org.apache.commons.lang3.reflect.TypeUtils
1210
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
1311
import java.lang.reflect.ParameterizedType
1412
import java.util.*

src/test/groovy/com/coxautodev/graphql/tools/FieldResolverScannerSpec.groovy

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package com.coxautodev.graphql.tools
22

33
import graphql.language.FieldDefinition
44
import graphql.language.TypeName
5+
import graphql.relay.Connection
6+
import graphql.relay.DefaultConnection
7+
import graphql.relay.SimpleListConnection
8+
import graphql.schema.DataFetchingEnvironment
59
import spock.lang.Specification
610

711
/**
@@ -59,6 +63,17 @@ class FieldResolverScannerSpec extends Specification {
5963
version instanceof PropertyFieldResolver
6064
}
6165

66+
def "scanner finds generic return type"() {
67+
setup:
68+
def resolver = new RootResolverInfo([new GenericQuery()], options)
69+
70+
when:
71+
def users = scanner.findFieldResolver(new FieldDefinition("users", new TypeName("UserConnection")), resolver)
72+
73+
then:
74+
users instanceof MethodFieldResolver
75+
}
76+
6277
class RootQuery1 implements GraphQLQueryResolver {
6378
def field1() {}
6479
}
@@ -78,4 +93,12 @@ class FieldResolverScannerSpec extends Specification {
7893
class PropertyQuery extends ParentPropertyQuery implements GraphQLQueryResolver {
7994
private String name = "name"
8095
}
96+
97+
class User {
98+
99+
}
100+
101+
class GenericQuery implements GraphQLQueryResolver {
102+
Connection<User> getUsers() { }
103+
}
81104
}

src/test/groovy/com/coxautodev/graphql/tools/RelayConnectionSpec.groovy

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,27 @@ package com.coxautodev.graphql.tools
22

33
import graphql.relay.Connection
44
import graphql.relay.DefaultConnection
5+
import graphql.relay.Edge
56
import graphql.relay.SimpleListConnection
67
import graphql.schema.DataFetchingEnvironment
78
import spock.lang.Specification
89

910
class RelayConnectionSpec extends Specification {
1011

12+
private static final SchemaParserOptions options = SchemaParserOptions.newOptions().genericWrappers(
13+
new SchemaParserOptions.GenericWrapper(
14+
Connection.class,
15+
0
16+
),
17+
new SchemaParserOptions.GenericWrapper(
18+
Edge.class,
19+
0
20+
)
21+
).build()
22+
1123
def "relay connection types are compatible"() {
1224
when:
13-
SchemaParser.newParser().schemaString('''\
25+
SchemaParser.newParser().options(options).schemaString('''\
1426
type Query {
1527
users(first: Int, after: String): UserConnection
1628
}

0 commit comments

Comments
 (0)