Skip to content

Commit 2d1314d

Browse files
authored
Merge pull request #361 from jdorleans/fix-unwrap-multi-generic-types
fix unwrapGenericType to properly find generic type in arguments Map
2 parents 55066de + 4200572 commit 2d1314d

File tree

6 files changed

+121
-7
lines changed

6 files changed

+121
-7
lines changed

src/main/kotlin/graphql/kickstart/tools/GenericType.kt

+11-3
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,17 @@ internal open class GenericType(protected val mostSpecificType: JavaType, protec
121121
}
122122
}
123123

124-
private fun unwrapGenericType(declaringType: ParameterizedType, type: TypeVariable<*>) =
125-
unwrapGenericType(TypeUtils.determineTypeArguments(getRawClass(mostSpecificType), declaringType)[type]
126-
?: error("No type variable found for: ${TypeUtils.toLongString(type)}"))
124+
private fun unwrapGenericType(declaringType: ParameterizedType, type: TypeVariable<*>): JavaType {
125+
val rawClass = getRawClass(mostSpecificType)
126+
val arguments = TypeUtils.determineTypeArguments(rawClass, declaringType)
127+
val matchingType = arguments
128+
.filter { it.key.name == type.name }
129+
.values
130+
.firstOrNull()
131+
?: error("No type variable found for: ${TypeUtils.toLongString(type)}")
132+
133+
return unwrapGenericType(matchingType)
134+
}
127135

128136
private fun replaceTypeVariable(type: JavaType): JavaType {
129137
return when (type) {

src/test/groovy/graphql/kickstart/tools/SchemaParserSpec.groovy

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class SchemaParserSpec extends Specification {
3333

3434
def "builder doesn't throw FileNotFound exception when file is present"() {
3535
when:
36-
SchemaParser.newParser().file("test.graphqls")
36+
SchemaParser.newParser().file("Test.graphqls")
3737
.resolvers(new GraphQLQueryResolver() {
3838
String getId() { "1" }
3939
})
@@ -286,7 +286,7 @@ class SchemaParserSpec extends Specification {
286286
def "parser should include source location for field definition when loaded from single classpath file"() {
287287
when:
288288
GraphQLSchema schema = SchemaParser.newParser()
289-
.file("test.graphqls")
289+
.file("Test.graphqls")
290290
.resolvers(new QueryWithIdResolver())
291291
.build()
292292
.makeExecutableSchema()
@@ -298,7 +298,7 @@ class SchemaParserSpec extends Specification {
298298
sourceLocation != null
299299
sourceLocation.line == 2
300300
sourceLocation.column == 5
301-
sourceLocation.sourceName == "test.graphqls"
301+
sourceLocation.sourceName == "Test.graphqls"
302302
}
303303

304304
def "support enum types if only used as input type"() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package graphql.kickstart.tools
2+
3+
import graphql.ExecutionInput
4+
import graphql.GraphQL
5+
import org.junit.Test
6+
7+
class PlaceTest {
8+
9+
@Test
10+
fun shouldHandleGenericsDeepHierarchy() {
11+
val schema = SchemaParser.newParser()
12+
.file("Place.graphqls")
13+
.resolvers(PlaceQuery())
14+
.build().makeExecutableSchema()
15+
16+
val graphql = GraphQL.newGraphQL(schema).build()
17+
val query = "query { places1 { id } places2 { id } }"
18+
val executionInput = ExecutionInput.newExecutionInput().query(query).build()
19+
val result = graphql.execute(executionInput)
20+
21+
assert(result.getData<Map<String, List<*>>>()["places1"]?.size == 3)
22+
assert(result.getData<Map<String, List<*>>>()["places2"]?.size == 2)
23+
}
24+
}
25+
26+
private class PlaceQuery : GraphQLQueryResolver {
27+
28+
fun places1(): List<Place1> = listOf(Place1("1"), Place1("2"), Place1("3"))
29+
30+
fun places2(): List<Place2> = listOf(Place2("4"), Place2("5"))
31+
}
32+
33+
private abstract class Entity(val id: String? = null)
34+
35+
private abstract class OtherPlace<R : Review<*>>(id: String? = null) : Place<R>(id) {
36+
val other: String? = null
37+
}
38+
39+
private abstract class Place<R : Review<*>>(id: String? = null) : Entity(id) {
40+
val name: String? = null
41+
val reviews: MutableSet<R>? = null
42+
}
43+
44+
private class Place1(id: String? = null) : OtherPlace<Review1>(id)
45+
46+
private class Place2(id: String? = null) : OtherPlace<Review2>(id)
47+
48+
private abstract class Review<T : Entity>(id: String? = null) : Entity(id) {
49+
val rating: Int? = null
50+
val content: T? = null
51+
}
52+
53+
private class Review1(id: String? = null) : Review<Place1>(id)
54+
55+
private class Review2(id: String? = null) : Review<Place2>(id)

src/test/kotlin/graphql/kickstart/tools/UtilsTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class UtilsTest {
2424
}
2525

2626
@Test
27-
fun `isTrivialDataFetcher`() {
27+
fun isTrivialDataFetcher() {
2828
val clazz = Bean::class.java
2929

3030
Assert.assertTrue(isTrivialDataFetcher(clazz.getMethod("getterValid")))

src/test/resources/Place.graphqls

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
type Query {
2+
places1: [Place1!]
3+
places2: [Place2!]
4+
}
5+
6+
interface Entity {
7+
id: ID!
8+
}
9+
10+
interface Place {
11+
name: String
12+
reviews: [Review!]
13+
}
14+
15+
interface OtherPlace {
16+
name: String
17+
other: String
18+
reviews: [Review!]
19+
}
20+
21+
type Place1 implements Entity, Place, OtherPlace {
22+
id: ID!
23+
name: String
24+
other: String
25+
reviews: [Review1!]
26+
}
27+
28+
type Place2 implements Entity, Place, OtherPlace {
29+
id: ID!
30+
name: String
31+
other: String
32+
reviews: [Review2!]
33+
}
34+
35+
interface Review {
36+
id: ID!
37+
rating: Int
38+
content: Entity
39+
}
40+
41+
type Review1 implements Review {
42+
id: ID!
43+
rating: Int
44+
content: Place1
45+
}
46+
47+
type Review2 implements Review {
48+
id: ID!
49+
rating: Int
50+
content: Place2
51+
}
File renamed without changes.

0 commit comments

Comments
 (0)