Skip to content

Commit a1c8743

Browse files
dariuszkucSaurav Tapader
authored and
Saurav Tapader
committed
fix: abstract classes should generate interface (#221)
Fixes: #220
1 parent 514ac52 commit a1c8743

File tree

4 files changed

+34
-7
lines changed

4 files changed

+34
-7
lines changed

example/src/main/kotlin/com/expedia/graphql/sample/query/PolymorphicQuery.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.expedia.graphql.sample.query
22

33
import com.expedia.graphql.annotations.GraphQLDescription
4-
import com.expedia.graphql.sample.model.BodyPart
54
import com.expedia.graphql.sample.model.Animal
65
import com.expedia.graphql.sample.model.AnimalType
6+
import com.expedia.graphql.sample.model.BodyPart
77
import com.expedia.graphql.sample.model.Cat
88
import com.expedia.graphql.sample.model.Dog
99
import com.expedia.graphql.sample.model.LeftHand
@@ -30,4 +30,4 @@ class PolymorphicQuery: Query {
3030
"right" -> RightHand(12)
3131
else -> LeftHand("hello world")
3232
}
33-
}
33+
}

src/main/kotlin/com/expedia/graphql/generator/TypeBuilder.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ internal open class TypeBuilder constructor(protected val generator: SchemaGener
5151
kClass.isEnum() -> @Suppress("UNCHECKED_CAST") (generator.enumType(kClass as KClass<Enum<*>>))
5252
kClass.isListType() -> generator.listType(type, inputType)
5353
kClass.isUnion() -> generator.unionType(kClass)
54-
kClass.isInterface() -> generator.interfaceType(kClass)
54+
kClass.isInterface() || kClass.isAbstract -> generator.interfaceType(kClass)
5555
inputType -> generator.inputObjectType(kClass)
5656
else -> generator.objectType(kClass)
5757
}

src/main/kotlin/com/expedia/graphql/generator/filters/superclassFilters.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import kotlin.reflect.KClass
88
private typealias SuperclassFilter = (KClass<*>) -> Boolean
99

1010
private val isPublic: SuperclassFilter = { it.isPublic() }
11-
private val isInterface: SuperclassFilter = { it.isInterface() }
11+
private val isInterface: SuperclassFilter = { it.isInterface() || it.isAbstract }
1212
private val isNotUnion: SuperclassFilter = { it.isUnion().not() }
1313

1414
internal val superclassFilters: List<SuperclassFilter> = listOf(isPublic, isInterface, isNotUnion)

src/test/kotlin/com/expedia/graphql/generator/PolymorphicTests.kt

+30-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.expedia.graphql.TopLevelObject
44
import com.expedia.graphql.exceptions.InvalidInputFieldTypeException
55
import com.expedia.graphql.testSchemaConfig
66
import com.expedia.graphql.toSchema
7+
import graphql.schema.GraphQLInterfaceType
78
import graphql.schema.GraphQLObjectType
89
import graphql.schema.GraphQLUnionType
910
import org.junit.jupiter.api.Assertions.assertThrows
@@ -35,7 +36,7 @@ internal class PolymorphicTests {
3536
fun `SchemaGenerator can expose an interface and its implementations`() {
3637
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithInterface())), config = testSchemaConfig)
3738

38-
val interfaceType = schema.getType("AnInterface")
39+
val interfaceType = schema.getType("AnInterface") as? GraphQLInterfaceType
3940
assertNotNull(interfaceType)
4041

4142
val implementationType = schema.getObjectType("AnImplementation")
@@ -60,7 +61,7 @@ internal class PolymorphicTests {
6061

6162
@Test
6263
fun `Object types implementing union and interfaces are only created once`() {
63-
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithInterfaceAnUnion())), config = testSchemaConfig)
64+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithInterfaceAndUnion())), config = testSchemaConfig)
6465

6566
val carType = schema.getType("Car") as? GraphQLObjectType
6667
assertNotNull(carType)
@@ -79,6 +80,19 @@ internal class PolymorphicTests {
7980
val personType = schema.getType("Person")
8081
assertNotNull(personType)
8182
}
83+
84+
@Test
85+
fun `Abstract classes should be converted to interfaces`() {
86+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithAbstract())), config = testSchemaConfig)
87+
88+
val abstractInterface = schema.getType("MyAbstract") as? GraphQLInterfaceType
89+
assertNotNull(abstractInterface)
90+
91+
val classWithBaseAbstractType = schema.getObjectType("MyClass")
92+
assertNotNull(classWithBaseAbstractType)
93+
assertEquals(1, classWithBaseAbstractType.interfaces.size)
94+
assertEquals(classWithBaseAbstractType.interfaces.first(), abstractInterface)
95+
}
8296
}
8397

8498
class QueryWithInterface {
@@ -128,7 +142,7 @@ data class Arm(
128142
val value: Boolean
129143
) : BodyPart
130144

131-
class QueryWithInterfaceAnUnion {
145+
class QueryWithInterfaceAndUnion {
132146
fun product(): Product = Car("DB9", "black")
133147
}
134148

@@ -156,3 +170,16 @@ data class Father(
156170
val dadJoke: String,
157171
override val child: Person?
158172
) : Person
173+
174+
class QueryWithAbstract {
175+
fun query(): MyAbstract = MyClass(id = 1, name = "JUnit")
176+
177+
fun queryImplementation(): MyClass = MyClass(id = 1, name = "JUnit_2")
178+
}
179+
180+
@Suppress("UnnecessaryAbstractClass")
181+
abstract class MyAbstract {
182+
abstract val id: Int
183+
}
184+
185+
data class MyClass(override val id: Int, val name: String) : MyAbstract()

0 commit comments

Comments
 (0)