Skip to content

Commit

Permalink
Polymorphic ignore (#633)
Browse files Browse the repository at this point in the history
* Fix typo in the function name

* Fix transitive property of `@GraphQLIgnore` for polymorphic types
  • Loading branch information
Guillaume Scheibel authored Mar 3, 2020
1 parent bd530a8 commit 46a0f02
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ internal fun KClass<*>.getValidSuperclasses(hooks: SchemaGeneratorHooks): List<K
this.superclasses.flatMap { it.getValidSuperclasses(hooks) }
}

internal fun KClass<*>.findConstructorParamter(name: String): KParameter? =
internal fun KClass<*>.findConstructorParameter(name: String): KParameter? =
this.primaryConstructor?.findParameterByName(name)

internal fun KClass<*>.isInterface(): Boolean = this.java.isInterface || this.isAbstract
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,4 @@ internal fun KProperty<*>.getPropertyName(parentClass: KClass<*>): String? =
internal fun KProperty<*>.getPropertyAnnotations(parentClass: KClass<*>): List<Annotation> =
this.annotations.union(getConstructorParameter(parentClass)?.annotations.orEmpty()).toList()

private fun KProperty<*>.getConstructorParameter(parentClass: KClass<*>) = parentClass.findConstructorParamter(this.name)
private fun KProperty<*>.getConstructorParameter(parentClass: KClass<*>) = parentClass.findConstructorParameter(this.name)
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@

package com.expediagroup.graphql.generator.filters

import com.expediagroup.graphql.generator.extensions.isGraphQLIgnored
import com.expediagroup.graphql.generator.extensions.isPropertyGraphQLIgnored
import com.expediagroup.graphql.generator.extensions.isPublic
import com.expediagroup.graphql.generator.extensions.qualifiedName
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.jvmErasure

private typealias PropertyFilter = (KProperty<*>, KClass<*>) -> Boolean

Expand All @@ -29,5 +32,20 @@ private val blacklistTypes: List<String> = listOf("kotlin.reflect.KClass")
private val isPropertyPublic: PropertyFilter = { prop, _ -> prop.isPublic() }
private val isPropertyNotGraphQLIgnored: PropertyFilter = { prop, parentClass -> prop.isPropertyGraphQLIgnored(parentClass).not() }
private val isNotBlacklistedType: PropertyFilter = { prop, _ -> blacklistTypes.contains(prop.returnType.qualifiedName).not() }
private val isNotIgnoredFromSuperClass: PropertyFilter = { prop, parentClass ->
val superPropsIgnored = parentClass.supertypes
.flatMap { superType ->
superType.jvmErasure.memberProperties
.filter { superProp -> basicPropertyFilters.all { it.invoke(superProp, superType::class) } }
.filter { it.isGraphQLIgnored() }
}

internal val propertyFilters: List<PropertyFilter> = listOf(isPropertyPublic, isNotBlacklistedType, isPropertyNotGraphQLIgnored)
superPropsIgnored.none { superProp ->
superProp.name == prop.name &&
superProp.returnType == prop.returnType &&
superProp.visibility == prop.visibility
}
}

private val basicPropertyFilters = listOf(isPropertyPublic, isNotBlacklistedType)
internal val propertyFilters: List<PropertyFilter> = basicPropertyFilters + isPropertyNotGraphQLIgnored + isNotIgnoredFromSuperClass
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.expediagroup.graphql.generator.extensions.getSimpleName
import com.expediagroup.graphql.generator.extensions.getValidFunctions
import com.expediagroup.graphql.generator.extensions.getValidProperties
import com.expediagroup.graphql.generator.extensions.getValidSuperclasses
import com.expediagroup.graphql.generator.extensions.isGraphQLIgnored
import com.expediagroup.graphql.generator.extensions.safeCast
import graphql.TypeResolutionEnvironment
import graphql.schema.GraphQLInterfaceType
Expand Down Expand Up @@ -58,6 +59,7 @@ internal class InterfaceBuilder(generator: SchemaGenerator) : TypeBuilder(genera
.forEach { builder.field(generator.function(it, kClass.getSimpleName(), abstract = true)) }

subTypeMapper.getSubTypesOf(kClass)
.filter { it.isGraphQLIgnored().not() }
.map { graphQLTypeOf(it.createType()) }
.forEach {
// Do not add objects currently under construction to the additional types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.expediagroup.graphql.generator

import com.expediagroup.graphql.TopLevelObject
import com.expediagroup.graphql.annotations.GraphQLIgnore
import com.expediagroup.graphql.annotations.GraphQLName
import com.expediagroup.graphql.exceptions.InvalidInputFieldTypeException
import com.expediagroup.graphql.testSchemaConfig
Expand All @@ -31,6 +32,7 @@ import org.junit.jupiter.api.Test
import kotlin.random.Random
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

internal class PolymorphicTests {
Expand Down Expand Up @@ -146,6 +148,33 @@ internal class PolymorphicTests {
assertEquals("StrawberryCake", strawberryCakeResolver.name)
}

@Test
fun `Interface implementations are not computed when marked with GraphQLIgnore annotation`() {
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithIgnoredInfo())), config = testSchemaConfig)
val service = schema.getType("Service") as? GraphQLInterfaceType
assertNotNull(service)

val webService = schema.getType("WebService") as? GraphQLObjectType
assertNotNull(webService)

val microService = schema.getType("MicroService") as? GraphQLObjectType
assertNull(microService)
}

@Test
fun `Ignored interface properties should not appear in the subtype`() {
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithIgnoredInfo())), config = testSchemaConfig)
val service = schema.getType("Service") as? GraphQLInterfaceType
assertNotNull(service)
val interfaceIgnoredField = service.getFieldDefinition("shouldNotBeInTheSchema")
assertNull(interfaceIgnoredField)

val webService = schema.getType("WebService") as? GraphQLObjectType
assertNotNull(webService)
val subtypeIgnoredField = webService.getFieldDefinition("shouldNotBeInTheSchema")
assertNull(subtypeIgnoredField)
}

private fun mockTypeResolutionEnvironment(target: Any, schema: GraphQLSchema): TypeResolutionEnvironment =
TypeResolutionEnvironment(target, emptyMap(), null, null, schema, null)
}
Expand Down Expand Up @@ -270,3 +299,20 @@ class Cheesecake : Cake {
interface Dessert

class IceCream : Dessert

class QueryWithIgnoredInfo {
fun webservice(): Service = WebService("gql-kotlin-service")
fun microservice(): Service = MicroService("micro-gql-kotlin-service")
}

interface Service {
val name: String

@GraphQLIgnore
val shouldNotBeInTheSchema: Boolean
}

data class WebService(override val name: String, override val shouldNotBeInTheSchema: Boolean = false) : Service

@GraphQLIgnore
data class MicroService(override val name: String, override val shouldNotBeInTheSchema: Boolean = true) : Service
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,10 @@ open class KClassExtensionsTest {

@Test
fun `test findConstructorParamter`() {
assertNotNull(MyTestClass::class.findConstructorParamter("publicProperty"))
assertNull(MyTestClass::class.findConstructorParamter("foobar"))
assertNull(EmptyConstructorClass::class.findConstructorParamter("id"))
assertNull(TestInterface::class.findConstructorParamter("foobar"))
assertNotNull(MyTestClass::class.findConstructorParameter("publicProperty"))
assertNull(MyTestClass::class.findConstructorParameter("foobar"))
assertNull(EmptyConstructorClass::class.findConstructorParameter("id"))
assertNull(TestInterface::class.findConstructorParameter("foobar"))
}

@Test
Expand Down

0 comments on commit 46a0f02

Please sign in to comment.