Skip to content

Commit

Permalink
Support recurise types (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
gscheibel authored Oct 16, 2018
1 parent 59396a0 commit 75136a5
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import graphql.schema.GraphQLObjectType
import graphql.schema.GraphQLOutputType
import graphql.schema.GraphQLSchema
import graphql.schema.GraphQLType
import graphql.schema.GraphQLTypeReference
import graphql.schema.GraphQLUnionType
import org.reflections.Reflections
import kotlin.reflect.KClass
Expand Down Expand Up @@ -240,33 +241,39 @@ internal class SchemaGenerator(
GraphQLList.list(graphQLTypeOf(type.arguments.first().type!!, inputType))

private fun objectType(kClass: KClass<*>, interfaceType: GraphQLInterfaceType? = null): GraphQLType {
val builder = GraphQLObjectType.newObject()
if (cache.isTypeUnderConstruction(kClass)) {
return GraphQLTypeReference.typeRef(kClass.simpleName)
} else {
cache.putTypeUnderConstruction(kClass)

builder.name(kClass.simpleName)
builder.description(kClass.graphQLDescription())
val builder = GraphQLObjectType.newObject()

kClass.directives().map {
builder.withDirective(it)
directives.add(it)
}
builder.name(kClass.simpleName)
builder.description(kClass.graphQLDescription())

if (interfaceType != null) {
builder.withInterface(interfaceType)
} else {
kClass.superclasses
.asSequence()
.filter { it.canBeGraphQLInterface() && !it.canBeGraphQLUnion() }
.map { objectFromReflection(it.createType(), false) as GraphQLInterfaceType }
.forEach { builder.withInterface(it) }
}
kClass.directives().map {
builder.withDirective(it)
directives.add(it)
}

kClass.getValidProperties(config.hooks)
.forEach { builder.field(property(it)) }
if (interfaceType != null) {
builder.withInterface(interfaceType)
} else {
kClass.superclasses
.asSequence()
.filter { it.canBeGraphQLInterface() && !it.canBeGraphQLUnion() }
.map { objectFromReflection(it.createType(), false) as GraphQLInterfaceType }
.forEach { builder.withInterface(it) }
}

kClass.getValidFunctions(config.hooks)
.forEach { builder.field(function(it)) }
kClass.getValidProperties(config.hooks)
.forEach { builder.field(property(it)) }

return builder.build()
kClass.getValidFunctions(config.hooks)
.forEach { builder.field(function(it)) }

return builder.build()
}
}

private fun inputObjectType(kClass: KClass<*>): GraphQLType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@file:Suppress("TooManyFunctions")
package com.expedia.graphql.schema.generator

import com.expedia.graphql.schema.exceptions.ConflictingTypesException
Expand All @@ -16,6 +17,7 @@ internal data class TypesCacheKey(val type: KType, val inputType: Boolean)
internal class TypesCache(private val supportedPackages: List<String>) {

private val cache: MutableMap<String, KGraphQLType> = mutableMapOf()
private val typeUnderConstruction: MutableSet<KClass<*>> = mutableSetOf()

fun get(cacheKey: TypesCacheKey): GraphQLType? {
val cacheKeyString = getCacheKeyString(cacheKey)
Expand All @@ -33,7 +35,10 @@ internal class TypesCache(private val supportedPackages: List<String>) {
return null
}

fun put(key: TypesCacheKey, kGraphQLType: KGraphQLType) = cache.put(getCacheKeyString(key), kGraphQLType)
fun put(key: TypesCacheKey, kGraphQLType: KGraphQLType): KGraphQLType? {
typeUnderConstruction.remove(kGraphQLType.kClass)
return cache.put(getCacheKeyString(key), kGraphQLType)
}

fun doesNotContainGraphQLType(graphQLType: GraphQLType) =
cache.none { (_, v) -> v.graphQLType.name == graphQLType.name }
Expand Down Expand Up @@ -82,4 +87,8 @@ internal class TypesCache(private val supportedPackages: List<String>) {
throw TypeNotSupportedException(qualifiedName, supportedPackages)
}
}

fun putTypeUnderConstruction(kClass: KClass<*>) = typeUnderConstruction.add(kClass)

fun isTypeUnderConstruction(kClass: KClass<*>) = typeUnderConstruction.contains(kClass)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import com.expedia.graphql.schema.testSchemaConfig
import com.expedia.graphql.toSchema
import graphql.GraphQL
import graphql.schema.GraphQLObjectType
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertThrows
import java.net.CookieManager
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

@Suppress("Detekt.UnusedPrivateMember", "Detekt.FunctionOnlyReturningConstant")
Expand Down Expand Up @@ -207,6 +209,25 @@ class SchemaGeneratorTest {
}
}

@Test
fun `SchemaGenerator supports type references`() {
val schema = toSchema(queries = listOf(TopLevelObjectDef(QueryWithParentChildRelationship())), config = testSchemaConfig)

val graphQL = GraphQL.newGraphQL(schema).build()
val result = graphQL.execute("{ query { name children { name } } }")
val data = result.getData<Map<String, Map<String, Any>>>()

assertNotNull(data)
val res = data["query"]
assertEquals("Bob", res?.get("name").toString())
val bobChildren = res?.get("children") as? List<Map<String, Any>>
assertNotNull(bobChildren)

val firstChild = bobChildren?.get(0)
assertEquals("Alice", firstChild?.get("name"))
assertNull(firstChild?.get("children"))
}

class QueryObject {
@GraphQLDescription("A GraphQL query method")
fun query(@GraphQLDescription("A GraphQL value") value: Int): Geography = Geography(value, GeoType.CITY, listOf())
Expand Down Expand Up @@ -325,4 +346,13 @@ class SchemaGeneratorTest {
@GraphQLDescription("A second conflicting GraphQL query method")
fun type2() = com.expedia.graphql.conflicts.GeoType.CITY
}

class QueryWithParentChildRelationship {
fun query(): Person {
val children = listOf(Person("Alice"))
return Person("Bob", children)
}
}

data class Person(val name: String, val children: List<Person>? = null)
}

0 comments on commit 75136a5

Please sign in to comment.