Skip to content

Commit

Permalink
fix: recursive interfaces (#143)
Browse files Browse the repository at this point in the history
Fixes #141
  • Loading branch information
smyrick authored and amandaducrou committed Jan 10, 2019
1 parent 6ebed14 commit bde0e71
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.expedia.graphql.generator.types

import com.expedia.graphql.generator.extensions.getValidFunctions
import com.expedia.graphql.generator.extensions.getValidProperties
import com.expedia.graphql.generator.extensions.getGraphQLDescription
import com.expedia.graphql.generator.SchemaGenerator
import com.expedia.graphql.generator.TypeBuilder
import com.expedia.graphql.generator.extensions.getGraphQLDescription
import com.expedia.graphql.generator.extensions.getSimpleName
import com.expedia.graphql.generator.extensions.getValidFunctions
import com.expedia.graphql.generator.extensions.getValidProperties
import graphql.TypeResolutionEnvironment
import graphql.schema.GraphQLInterfaceType
import graphql.schema.GraphQLType
Expand Down Expand Up @@ -33,10 +33,11 @@ internal class InterfaceTypeBuilder(generator: SchemaGenerator) : TypeBuilder(ge
implementations.forEach {
val objectType = generator.objectType(it.kotlin, interfaceType)

// Only update the state if the object is fully constructed and not a reference
if (objectType !is GraphQLTypeReference) {
state.additionalTypes.add(objectType)
state.cache.removeTypeUnderConstruction(it.kotlin)
}
state.cache.removeTypeUnderConstruction(it.kotlin)
}

interfaceType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class ObjectTypeBuilder(generator: SchemaGenerator) : TypeBuilder(gener
builder.withInterface(interfaceType)
} else {
kClass.getValidSuperclasses(config.hooks)
.map { objectFromReflection(it.createType(), false) as? GraphQLInterfaceType }
.mapNotNull { objectFromReflection(it.createType(), false) as? GraphQLInterfaceType }
.forEach { builder.withInterface(it) }
}

Expand Down
54 changes: 54 additions & 0 deletions src/test/kotlin/com/expedia/graphql/integration/NodeGraphTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.expedia.graphql.integration

import com.expedia.graphql.TopLevelObject
import com.expedia.graphql.testSchemaConfig
import com.expedia.graphql.toSchema
import kotlin.test.Test
import kotlin.test.assertEquals

class NodeGraphTest {

@Test
fun nodeGraph() {
val root = Node(id = 0, value = "root", parent = null, children = emptyList())
val nodeA = Node(id = 1, value = "A", parent = root, children = emptyList())
val nodeB = Node(id = 2, value = "B", parent = root, children = emptyList())
val nodeC = Node(id = 3, value = "C", parent = nodeB, children = emptyList())

root.children = listOf(nodeA, nodeB)
nodeB.children = listOf(nodeC)

val queries = listOf(TopLevelObject(NodeQuery()))

val schema = toSchema(queries = queries, config = testSchemaConfig)

assertEquals(expected = 1, actual = schema.queryType.fieldDefinitions.size)
assertEquals(expected = "nodeGraph", actual = schema.queryType.fieldDefinitions.first().name)
}
}

/**
* Represents a recursive type that references itself,
* similar to a tree node structure.
*/
data class Node(
val id: Int,
val value: String,
val parent: Node?,
var children: List<Node>
)

class NodeQuery {

private val root = Node(id = 0, value = "root", parent = null, children = emptyList())
private val nodeA = Node(id = 1, value = "A", parent = root, children = emptyList())
private val nodeB = Node(id = 2, value = "B", parent = root, children = emptyList())
private val nodeC = Node(id = 3, value = "C", parent = nodeB, children = emptyList())

init {
root.children = listOf(nodeA, nodeB)
nodeB.children = listOf(nodeC)
}

fun nodeGraph() = root
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.expedia.graphql.integration

import com.expedia.graphql.TopLevelObject
import com.expedia.graphql.testSchemaConfig
import com.expedia.graphql.toSchema
import kotlin.test.Test
import kotlin.test.assertEquals

class RecursiveInterfaceTest {

@Test
fun recursiveInterface() {
val queries = listOf(TopLevelObject(RecursiveInterfaceQuery()))
val schema = toSchema(queries = queries, config = testSchemaConfig)
assertEquals(1, schema.queryType.fieldDefinitions.size)
val field = schema.queryType.fieldDefinitions.first()
assertEquals("getRoot", field.name)
}
}

class RecursiveInterfaceQuery {
fun getRoot() = RecursiveInterfaceA()
}

interface SomeInterface {
val id: String
}

class RecursiveInterfaceA : SomeInterface {
override val id = "A"
fun getB() = RecursiveInterfaceB()
}

class RecursiveInterfaceB : SomeInterface {
override val id = "B"
fun getA() = RecursiveInterfaceA()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.expedia.graphql.integration

import com.expedia.graphql.TopLevelObject
import com.expedia.graphql.testSchemaConfig
import com.expedia.graphql.toSchema
import kotlin.test.Test
import kotlin.test.assertEquals

class RecursiveUnionTest {

@Test
fun recursiveUnion() {
val queries = listOf(TopLevelObject(RecursiveUnionQuery()))
val schema = toSchema(queries = queries, config = testSchemaConfig)
assertEquals(1, schema.queryType.fieldDefinitions.size)
val field = schema.queryType.fieldDefinitions.first()
assertEquals("getRoot", field.name)
}
}

class RecursiveUnionQuery {
fun getRoot() = RecursiveUnionA()
}

interface SomeUnion

class RecursiveUnionA : SomeUnion {
fun getB() = RecursiveUnionB()
}

class RecursiveUnionB : SomeUnion {
fun getA() = RecursiveUnionA()
}

0 comments on commit bde0e71

Please sign in to comment.