Skip to content

Commit e3ad6ca

Browse files
authored
Remove reflections lib in favor of ClassGraph (ExpediaGroup#450)
* Remove reflections lib in favor of ClassGraph Fixes ExpediaGroup#449 There is a securty issue with one of the dependencies of org.reflections:reflection:0.9.11. Instead of resolving the dep issue we should migrate away from this library since the last version was released in 2017. https://github.com/classgraph/classgraph is an active supported library with reported faster implementation of reflection * Remove val property from SubTypeMapper
1 parent 36950bd commit e3ad6ca

File tree

15 files changed

+93
-37
lines changed

15 files changed

+93
-37
lines changed

graphql-kotlin-federation/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
<artifactId>graphql-kotlin-schema-generator</artifactId>
3434
<version>${project.parent.version}</version>
3535
</dependency>
36+
<dependency>
37+
<groupId>io.github.classgraph</groupId>
38+
<artifactId>classgraph</artifactId>
39+
</dependency>
3640
<dependency>
3741
<groupId>org.junit.jupiter</groupId>
3842
<artifactId>junit-jupiter-params</artifactId>

graphql-kotlin-federation/src/main/kotlin/com/expediagroup/graphql/federation/FederatedSchemaGenerator.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import com.expediagroup.graphql.TopLevelObject
2020
import com.expediagroup.graphql.federation.directives.ExtendsDirective
2121
import com.expediagroup.graphql.generator.SchemaGenerator
2222
import graphql.schema.GraphQLSchema
23-
import org.reflections.Reflections
23+
import io.github.classgraph.ClassGraph
2424
import kotlin.reflect.full.createType
25+
import kotlin.reflect.jvm.jvmName
2526

2627
/**
2728
* Generates federated GraphQL schemas based on the specified configuration.
@@ -41,10 +42,12 @@ open class FederatedSchemaGenerator(generatorConfig: FederatedSchemaGeneratorCon
4142
/**
4243
* Scans specified packages for all the federated (extended) types and adds them to the target schema.
4344
*/
45+
@Suppress("Detekt.SpreadOperator")
4446
fun GraphQLSchema.Builder.federation(supportedPackages: List<String>): GraphQLSchema.Builder {
45-
supportedPackages
46-
.map { pkg -> Reflections(pkg).getTypesAnnotatedWith(ExtendsDirective::class.java).map { it.kotlin } }
47-
.flatten()
47+
val scanResult = ClassGraph().enableAllInfo().whitelistPackages(*supportedPackages.toTypedArray()).scan()
48+
49+
scanResult.getClassesWithAnnotation(ExtendsDirective::class.jvmName)
50+
.map { it.loadClass().kotlin }
4851
.map {
4952
val graphQLType = if (it.isAbstract) {
5053
interfaceType(it)

graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/FederatedSchemaGeneratorTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ package com.expediagroup.graphql.federation
1818

1919
import com.expediagroup.graphql.TopLevelObject
2020
import com.expediagroup.graphql.extensions.print
21+
import com.expediagroup.graphql.federation.data.queries.simple.NestedQuery
22+
import com.expediagroup.graphql.federation.data.queries.simple.SimpleQuery
2123
import com.expediagroup.graphql.federation.execution.FederatedTypeRegistry
2224
import graphql.schema.GraphQLUnionType
2325
import org.junit.jupiter.api.Assertions.assertEquals
2426
import org.junit.jupiter.api.Test
25-
import test.data.queries.simple.NestedQuery
26-
import test.data.queries.simple.SimpleQuery
2727
import kotlin.test.assertNotNull
2828
import kotlin.test.assertTrue
2929

@@ -104,7 +104,7 @@ class FederatedSchemaGeneratorTest {
104104
@Test
105105
fun `verify can generate federated schema`() {
106106
val config = FederatedSchemaGeneratorConfig(
107-
supportedPackages = listOf("test.data.queries.federated"),
107+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.federated"),
108108
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
109109
)
110110

@@ -137,7 +137,7 @@ class FederatedSchemaGeneratorTest {
137137
""".trimIndent()
138138

139139
val config = FederatedSchemaGeneratorConfig(
140-
supportedPackages = listOf("test.data.queries.simple"),
140+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.simple"),
141141
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
142142
)
143143

@@ -169,7 +169,7 @@ class FederatedSchemaGeneratorTest {
169169
""".trimIndent()
170170

171171
val config = FederatedSchemaGeneratorConfig(
172-
supportedPackages = listOf("test.data.queries.simple"),
172+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.simple"),
173173
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
174174
)
175175

graphql-kotlin-federation/src/test/kotlin/test/data/TestResolvers.kt renamed to graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/data/TestResolvers.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
package test.data
17+
package com.expediagroup.graphql.federation.data
1818

19+
import com.expediagroup.graphql.federation.data.queries.federated.Book
20+
import com.expediagroup.graphql.federation.data.queries.federated.User
1921
import com.expediagroup.graphql.federation.execution.FederatedTypeResolver
20-
import test.data.queries.federated.Book
21-
import test.data.queries.federated.User
2222

2323
internal class BookResolver : FederatedTypeResolver<Book> {
2424
override suspend fun resolve(representations: List<Map<String, Any>>): List<Book?> {

graphql-kotlin-federation/src/test/kotlin/test/data/TestSchema.kt renamed to graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/data/TestSchema.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package test.data
17+
package com.expediagroup.graphql.federation.data
1818

1919
import com.expediagroup.graphql.federation.FederatedSchemaGeneratorConfig
2020
import com.expediagroup.graphql.federation.FederatedSchemaGeneratorHooks
@@ -25,7 +25,7 @@ import graphql.schema.GraphQLSchema
2525

2626
internal fun federatedTestSchema(federatedTypeResolvers: Map<String, FederatedTypeResolver<*>> = emptyMap()): GraphQLSchema {
2727
val config = FederatedSchemaGeneratorConfig(
28-
supportedPackages = listOf("test.data.queries.federated"),
28+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.federated"),
2929
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(federatedTypeResolvers))
3030
)
3131

graphql-kotlin-federation/src/test/kotlin/test/data/queries/federated/Product.kt renamed to graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/data/queries/federated/Product.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package test.data.queries.federated
17+
package com.expediagroup.graphql.federation.data.queries.federated
1818

1919
import com.expediagroup.graphql.annotations.GraphQLDirective
2020
import com.expediagroup.graphql.annotations.GraphQLIgnore

graphql-kotlin-federation/src/test/kotlin/test/data/queries/simple/NestedQuery.kt renamed to graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/data/queries/simple/NestedQuery.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package test.data.queries.simple
17+
package com.expediagroup.graphql.federation.data.queries.simple
1818

1919
import kotlin.random.Random
2020

graphql-kotlin-federation/src/test/kotlin/test/data/queries/simple/SimpleQuery.kt renamed to graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/data/queries/simple/SimpleQuery.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package test.data.queries.simple
17+
package com.expediagroup.graphql.federation.data.queries.simple
1818

1919
/*
2020
type Query {

graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/execution/EntityQueryResolverTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ import io.mockk.every
2424
import io.mockk.mockk
2525
import io.mockk.spyk
2626
import org.junit.jupiter.api.Test
27-
import test.data.BookResolver
28-
import test.data.UserResolver
29-
import test.data.queries.federated.Book
30-
import test.data.queries.federated.User
27+
import com.expediagroup.graphql.federation.data.BookResolver
28+
import com.expediagroup.graphql.federation.data.UserResolver
29+
import com.expediagroup.graphql.federation.data.queries.federated.Book
30+
import com.expediagroup.graphql.federation.data.queries.federated.User
3131
import kotlin.test.assertEquals
3232

3333
class EntityQueryResolverTest {

graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/execution/FederatedQueryResolverTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ package com.expediagroup.graphql.federation.execution
1919
import graphql.ExecutionInput
2020
import graphql.GraphQL
2121
import org.junit.jupiter.api.Test
22-
import test.data.BookResolver
23-
import test.data.UserResolver
24-
import test.data.federatedTestSchema
22+
import com.expediagroup.graphql.federation.data.BookResolver
23+
import com.expediagroup.graphql.federation.data.UserResolver
24+
import com.expediagroup.graphql.federation.data.federatedTestSchema
2525
import kotlin.test.assertEquals
2626
import kotlin.test.assertFalse
2727
import kotlin.test.assertNotNull

graphql-kotlin-federation/src/test/kotlin/com/expediagroup/graphql/federation/execution/ServiceQueryResolverTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import com.expediagroup.graphql.federation.toFederatedSchema
2323
import graphql.ExecutionInput
2424
import graphql.GraphQL
2525
import org.junit.jupiter.api.Test
26-
import test.data.queries.simple.NestedQuery
27-
import test.data.queries.simple.SimpleQuery
26+
import com.expediagroup.graphql.federation.data.queries.simple.NestedQuery
27+
import com.expediagroup.graphql.federation.data.queries.simple.SimpleQuery
2828
import kotlin.test.assertEquals
2929
import kotlin.test.assertNotNull
3030

@@ -72,7 +72,7 @@ class ServiceQueryResolverTest {
7272
@Test
7373
fun `verify can retrieve SDL using _service query`() {
7474
val config = FederatedSchemaGeneratorConfig(
75-
supportedPackages = listOf("test.data.queries.federated"),
75+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.federated"),
7676
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
7777
)
7878

@@ -101,7 +101,7 @@ class ServiceQueryResolverTest {
101101
@Test
102102
fun `verify can retrieve SDL using _service query for non-federated schemas`() {
103103
val config = FederatedSchemaGeneratorConfig(
104-
supportedPackages = listOf("test.data.queries.simple"),
104+
supportedPackages = listOf("com.expediagroup.graphql.federation.data.queries.simple"),
105105
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
106106
)
107107

graphql-kotlin-schema-generator/pom.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<properties>
1717
<project.root>${project.basedir}/..</project.root>
1818
<rxjava2.version>2.2.12</rxjava2.version>
19+
<guava.version>28.0-jre</guava.version>
1920
</properties>
2021

2122
<dependencies>
@@ -32,8 +33,13 @@
3233
<artifactId>kotlin-reflect</artifactId>
3334
</dependency>
3435
<dependency>
35-
<groupId>org.reflections</groupId>
36-
<artifactId>reflections</artifactId>
36+
<groupId>io.github.classgraph</groupId>
37+
<artifactId>classgraph</artifactId>
38+
</dependency>
39+
<dependency>
40+
<groupId>com.google.guava</groupId>
41+
<artifactId>guava</artifactId>
42+
<version>${guava.version}</version>
3743
</dependency>
3844
<dependency>
3945
<groupId>com.fasterxml.jackson.module</groupId>

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,35 @@
1616

1717
package com.expediagroup.graphql.generator
1818

19-
import org.reflections.Reflections
19+
import io.github.classgraph.ClassGraph
20+
import io.github.classgraph.ClassInfo
21+
import io.github.classgraph.ClassInfoList
2022
import kotlin.reflect.KClass
23+
import kotlin.reflect.jvm.jvmName
2124

2225
internal class SubTypeMapper(supportedPackages: List<String>) {
2326

24-
private val reflections = Reflections(supportedPackages)
27+
@Suppress("Detekt.SpreadOperator")
28+
private val scanResult = ClassGraph()
29+
.enableAllInfo()
30+
.whitelistPackages(*supportedPackages.toTypedArray())
31+
.scan()
2532

26-
fun getSubTypesOf(kclass: KClass<*>): List<KClass<*>> = reflections.getSubTypesOf(kclass.java).filterNot { it.kotlin.isAbstract }.map { it.kotlin }
33+
fun getSubTypesOf(kclass: KClass<*>): List<KClass<*>> {
34+
val classInfo = scanResult.getClassInfo(kclass.jvmName) ?: return emptyList()
35+
36+
return getImplementingClasses(classInfo)
37+
.union(classInfo.subclasses)
38+
.map { it.loadClass().kotlin }
39+
.filterNot { it.isAbstract }
40+
}
41+
42+
@Suppress("Detekt.SwallowedException")
43+
private fun getImplementingClasses(classInfo: ClassInfo) =
44+
try {
45+
classInfo.classesImplementing
46+
} catch (e: IllegalArgumentException) {
47+
// Ignored, just return empty list
48+
ClassInfoList.emptyList()
49+
}
2750
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ import kotlin.test.assertEquals
2222
@Suppress("Detekt.UnusedPrivateClass")
2323
internal class SubTypeMapperTest {
2424

25+
private interface NoSubTypesInterface
26+
27+
private abstract class NoSubTypesClass
28+
2529
private interface MyInterface {
2630
fun getValue(): Int
2731
}
@@ -75,4 +79,20 @@ internal class SubTypeMapperTest {
7579

7680
assertEquals(expected = 0, actual = list.size)
7781
}
82+
83+
@Test
84+
fun `interface with no subtypes`() {
85+
val mapper = SubTypeMapper(listOf("com.expediagroup.graphql"))
86+
val list = mapper.getSubTypesOf(NoSubTypesInterface::class)
87+
88+
assertEquals(expected = 0, actual = list.size)
89+
}
90+
91+
@Test
92+
fun `abstract class with no subtypes`() {
93+
val mapper = SubTypeMapper(listOf("com.expediagroup.graphql"))
94+
val list = mapper.getSubTypesOf(NoSubTypesClass::class)
95+
96+
assertEquals(expected = 0, actual = list.size)
97+
}
7898
}

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
<jackson-module-kotlin.version>2.10.0</jackson-module-kotlin.version>
6767
<kotlin.version>1.3.50</kotlin.version>
6868
<kotlin-coroutines.version>1.3.2</kotlin-coroutines.version>
69-
<reflections.version>0.9.11</reflections.version>
69+
<classgraph.version>4.8.52</classgraph.version>
7070
<spring-boot.version>2.2.0.RELEASE</spring-boot.version>
7171

7272
<!-- Test Dependency Versions -->
@@ -273,9 +273,9 @@
273273
<version>${kotlin.version}</version>
274274
</dependency>
275275
<dependency>
276-
<groupId>org.reflections</groupId>
277-
<artifactId>reflections</artifactId>
278-
<version>${reflections.version}</version>
276+
<groupId>io.github.classgraph</groupId>
277+
<artifactId>classgraph</artifactId>
278+
<version>${classgraph.version}</version>
279279
</dependency>
280280
<dependency>
281281
<groupId>com.fasterxml.jackson.module</groupId>

0 commit comments

Comments
 (0)