Skip to content

Commit 8089c8e

Browse files
author
Guillaume Scheibel
authored
Polymorphic ignore 2.x (ExpediaGroup#634)
* Fix typo in the function name * Fix transitive property of `@GraphQLIgnore` for polymorphic types * Add more test * Remove unecessary check as the visilbiity cannot be changed between a interface and an implementation
1 parent ca0d4a0 commit 8089c8e

File tree

7 files changed

+82
-8
lines changed

7 files changed

+82
-8
lines changed

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/extensions/kClassExtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal fun KClass<*>.getValidSuperclasses(hooks: SchemaGeneratorHooks): List<K
5555
this.superclasses.flatMap { it.getValidSuperclasses(hooks) }
5656
}
5757

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

6161
internal fun KClass<*>.isInterface(): Boolean = this.java.isInterface || this.isAbstract

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/extensions/kPropertyExtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,4 @@ internal fun KProperty<*>.getPropertyName(parentClass: KClass<*>): String? =
4848
internal fun KProperty<*>.getPropertyAnnotations(parentClass: KClass<*>): List<Annotation> =
4949
this.annotations.union(getConstructorParameter(parentClass)?.annotations.orEmpty()).toList()
5050

51-
private fun KProperty<*>.getConstructorParameter(parentClass: KClass<*>) = parentClass.findConstructorParamter(this.name)
51+
private fun KProperty<*>.getConstructorParameter(parentClass: KClass<*>) = parentClass.findConstructorParameter(this.name)

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/filters/propertyFilters.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
package com.expediagroup.graphql.generator.filters
1818

19+
import com.expediagroup.graphql.generator.extensions.isGraphQLIgnored
1920
import com.expediagroup.graphql.generator.extensions.isPropertyGraphQLIgnored
2021
import com.expediagroup.graphql.generator.extensions.isPublic
2122
import com.expediagroup.graphql.generator.extensions.qualifiedName
2223
import kotlin.reflect.KClass
2324
import kotlin.reflect.KProperty
25+
import kotlin.reflect.full.memberProperties
26+
import kotlin.reflect.jvm.jvmErasure
2427

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

@@ -30,4 +33,19 @@ private val isPropertyPublic: PropertyFilter = { prop, _ -> prop.isPublic() }
3033
private val isPropertyNotGraphQLIgnored: PropertyFilter = { prop, parentClass -> prop.isPropertyGraphQLIgnored(parentClass).not() }
3134
private val isNotBlacklistedType: PropertyFilter = { prop, _ -> blacklistTypes.contains(prop.returnType.qualifiedName).not() }
3235

33-
internal val propertyFilters: List<PropertyFilter> = listOf(isPropertyPublic, isNotBlacklistedType, isPropertyNotGraphQLIgnored)
36+
private val isNotIgnoredFromSuperClass: PropertyFilter = { prop, parentClass ->
37+
val superPropsIgnored = parentClass.supertypes
38+
.flatMap { superType ->
39+
superType.jvmErasure.memberProperties
40+
.filter { superProp -> basicPropertyFilters.all { it.invoke(superProp, superType::class) } }
41+
.filter { it.isGraphQLIgnored() }
42+
}
43+
44+
superPropsIgnored.none { superProp ->
45+
superProp.name == prop.name &&
46+
superProp.returnType == prop.returnType
47+
}
48+
}
49+
50+
private val basicPropertyFilters = listOf(isPropertyPublic, isNotBlacklistedType)
51+
internal val propertyFilters: List<PropertyFilter> = basicPropertyFilters + isPropertyNotGraphQLIgnored + isNotIgnoredFromSuperClass

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.expediagroup.graphql.generator.extensions.getSimpleName
2323
import com.expediagroup.graphql.generator.extensions.getValidFunctions
2424
import com.expediagroup.graphql.generator.extensions.getValidProperties
2525
import com.expediagroup.graphql.generator.extensions.getValidSuperclasses
26+
import com.expediagroup.graphql.generator.extensions.isGraphQLIgnored
2627
import com.expediagroup.graphql.generator.extensions.safeCast
2728
import graphql.TypeResolutionEnvironment
2829
import graphql.schema.GraphQLInterfaceType
@@ -51,6 +52,7 @@ internal fun generateInterface(generator: SchemaGenerator, kClass: KClass<*>): G
5152
.forEach { builder.field(generateFunction(generator, it, kClass.getSimpleName(), null, abstract = true)) }
5253

5354
generator.classScanner.getSubTypesOf(kClass)
55+
.filter { it.isGraphQLIgnored().not() }
5456
.forEach { generator.additionalTypes.add(it.createType()) }
5557

5658
val interfaceType = builder.build()

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/PolymorphicTests.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.expediagroup.graphql.generator
1818

1919
import com.expediagroup.graphql.TopLevelObject
20+
import com.expediagroup.graphql.annotations.GraphQLIgnore
2021
import com.expediagroup.graphql.annotations.GraphQLName
2122
import com.expediagroup.graphql.exceptions.InvalidInputFieldTypeException
2223
import com.expediagroup.graphql.testSchemaConfig
@@ -31,6 +32,7 @@ import org.junit.jupiter.api.Test
3132
import kotlin.random.Random
3233
import kotlin.test.assertEquals
3334
import kotlin.test.assertNotNull
35+
import kotlin.test.assertNull
3436
import kotlin.test.assertTrue
3537

3638
internal class PolymorphicTests {
@@ -146,6 +148,33 @@ internal class PolymorphicTests {
146148
assertEquals("StrawberryCake", strawberryCakeResolver.name)
147149
}
148150

151+
@Test
152+
fun `Interface implementations are not computed when marked with GraphQLIgnore annotation`() {
153+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithIgnoredInfo())), config = testSchemaConfig)
154+
val service = schema.getType("Service") as? GraphQLInterfaceType
155+
assertNotNull(service)
156+
157+
val webService = schema.getType("WebService") as? GraphQLObjectType
158+
assertNotNull(webService)
159+
160+
val microService = schema.getType("MicroService") as? GraphQLObjectType
161+
assertNull(microService)
162+
}
163+
164+
@Test
165+
fun `Ignored interface properties should not appear in the subtype`() {
166+
val schema = toSchema(queries = listOf(TopLevelObject(QueryWithIgnoredInfo())), config = testSchemaConfig)
167+
val service = schema.getType("Service") as? GraphQLInterfaceType
168+
assertNotNull(service)
169+
val interfaceIgnoredField = service.getFieldDefinition("shouldNotBeInTheSchema")
170+
assertNull(interfaceIgnoredField)
171+
172+
val webService = schema.getType("WebService") as? GraphQLObjectType
173+
assertNotNull(webService)
174+
val subtypeIgnoredField = webService.getFieldDefinition("shouldNotBeInTheSchema")
175+
assertNull(subtypeIgnoredField)
176+
}
177+
149178
private fun mockTypeResolutionEnvironment(target: Any, schema: GraphQLSchema): TypeResolutionEnvironment =
150179
TypeResolutionEnvironment(target, emptyMap(), null, null, schema, null)
151180
}
@@ -273,3 +302,20 @@ interface Dessert
273302
class IceCream : Dessert {
274303
fun flavor(): String = "chocolate"
275304
}
305+
306+
class QueryWithIgnoredInfo {
307+
fun webservice(): Service = WebService("gql-kotlin-service")
308+
fun microservice(): Service = MicroService("micro-gql-kotlin-service")
309+
}
310+
311+
interface Service {
312+
val name: String
313+
314+
@GraphQLIgnore
315+
val shouldNotBeInTheSchema: Boolean
316+
}
317+
318+
data class WebService(override val name: String, override val shouldNotBeInTheSchema: Boolean = false) : Service
319+
320+
@GraphQLIgnore
321+
data class MicroService(override val name: String, override val shouldNotBeInTheSchema: Boolean = true) : Service

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/extensions/KClassExtensionsTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,10 @@ open class KClassExtensionsTest {
234234

235235
@Test
236236
fun `test findConstructorParamter`() {
237-
assertNotNull(MyTestClass::class.findConstructorParamter("publicProperty"))
238-
assertNull(MyTestClass::class.findConstructorParamter("foobar"))
239-
assertNull(EmptyConstructorClass::class.findConstructorParamter("id"))
240-
assertNull(TestInterface::class.findConstructorParamter("foobar"))
237+
assertNotNull(MyTestClass::class.findConstructorParameter("publicProperty"))
238+
assertNull(MyTestClass::class.findConstructorParameter("foobar"))
239+
assertNull(EmptyConstructorClass::class.findConstructorParameter("id"))
240+
assertNull(TestInterface::class.findConstructorParameter("foobar"))
241241
}
242242

243243
@Test

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/filters/PropertyFiltersTest.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ internal class PropertyFiltersTest {
3333
assertFalse(isValidProperty(MyClass::kClass, MyDataClass::class))
3434
assertTrue(isValidProperty(MyDataClass::id, MyDataClass::class))
3535
assertFalse(isValidProperty(MyDataClass::ignoredProperty, MyDataClass::class))
36+
assertFalse(isValidProperty(MyDataClass::ignoredProperty, MyDataClass::class))
37+
assertFalse(isValidProperty(MyClass::foo, MyClass::class))
3638
}
3739

3840
internal data class MyDataClass(
@@ -42,7 +44,12 @@ internal class PropertyFiltersTest {
4244
internal val ignoredProperty: Int = 0
4345
)
4446

45-
internal class MyClass {
47+
interface MyInterface {
48+
@GraphQLIgnore
49+
val foo: Int
50+
}
51+
52+
internal class MyClass : MyInterface {
4653

4754
val publicProperty: Int = 0
4855

@@ -52,6 +59,7 @@ internal class PropertyFiltersTest {
5259

5360
@GraphQLIgnore
5461
internal val ignoredProperty: Int = 0
62+
override val foo: Int = 5
5563
}
5664

5765
private fun isValidProperty(property: KProperty<*>, parentClass: KClass<*>): Boolean = propertyFilters.all { it(property, parentClass) }

0 commit comments

Comments
 (0)