Skip to content

Commit c37b557

Browse files
smyricksmyrick
authored and
smyrick
committed
Fix nested federation (ExpediaGroup#316)
Fixes ExpediaGroup#310 While I don't think it is possible to have an input type extended we should still be able to have nested types that are not part of the federated entities be generated properly. This can happen when there are lists of nullalbes or double wrapped elements
1 parent 48c6228 commit c37b557

File tree

9 files changed

+69
-8
lines changed

9 files changed

+69
-8
lines changed

examples/federation/base-app/src/main/kotlin/com/expedia/graphql/sample/Application.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class Application {
2929
private val logger = LoggerFactory.getLogger(Application::class.java)
3030

3131
@Bean
32-
fun hooks() = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(emptyMap()))
32+
fun hooks() = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
3333

3434
@Bean
3535
fun dataFetcherFactoryProvider(springDataFetcherFactory: SpringDataFetcherFactory, hooks: SchemaGeneratorHooks) =
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.expedia.graphql.sample.query
2+
3+
import org.springframework.stereotype.Component
4+
import kotlin.random.Random
5+
6+
@Component
7+
class NestedQuery : Query {
8+
fun getSimpleNestedObject(): List<SelfReferenceObject?> = listOf(SelfReferenceObject())
9+
}
10+
11+
class SelfReferenceObject {
12+
val description: String? = "SelfReferenceObject"
13+
val id = Random.nextInt()
14+
fun nextObject(): List<SelfReferenceObject?> = listOf(SelfReferenceObject())
15+
}

graphql-kotlin-federation/src/main/kotlin/com/expedia/graphql/federation/execution/FederatedTypeRegistry.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package com.expedia.graphql.federation.execution
33
/**
44
* Simple registry that holds mapping of all registered federated GraphQL types and their corresponding resolvers.
55
*/
6-
class FederatedTypeRegistry(private val federatedTypeResolvers: Map<String, FederatedTypeResolver<*>>) {
6+
class FederatedTypeRegistry(private val federatedTypeResolvers: Map<String, FederatedTypeResolver<*>> = emptyMap()) {
77

88
/**
99
* Retrieve target federated resolver for the specified GraphQL type.

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

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.expedia.graphql.federation.execution.FederatedTypeRegistry
66
import graphql.schema.GraphQLUnionType
77
import org.junit.jupiter.api.Assertions.assertEquals
88
import org.junit.jupiter.api.Test
9+
import test.data.queries.simple.NestedQuery
910
import test.data.queries.simple.SimpleQuery
1011
import kotlin.test.assertNotNull
1112
import kotlin.test.assertTrue
@@ -85,7 +86,7 @@ class FederatedSchemaGeneratorTest {
8586
fun `verify can generate federated schema`() {
8687
val config = FederatedSchemaGeneratorConfig(
8788
supportedPackages = listOf("test.data.queries.federated"),
88-
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(emptyMap()))
89+
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
8990
)
9091

9192
val schema = toFederatedSchema(config)
@@ -118,10 +119,42 @@ class FederatedSchemaGeneratorTest {
118119

119120
val config = FederatedSchemaGeneratorConfig(
120121
supportedPackages = listOf("test.data.queries.simple"),
121-
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(emptyMap()))
122+
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
122123
)
123124

124125
val schema = toFederatedSchema(config, listOf(TopLevelObject(SimpleQuery())))
125126
assertEquals(expectedSchema, schema.print(includeDirectives = false).trim())
126127
}
128+
129+
@Test
130+
fun `verify a nested federated schema still works`() {
131+
val expectedSchema = """
132+
schema {
133+
query: Query
134+
}
135+
136+
type Query {
137+
_service: _Service
138+
getSimpleNestedObject: [SelfReferenceObject]!
139+
}
140+
141+
type SelfReferenceObject {
142+
description: String
143+
id: Int!
144+
nextObject: [SelfReferenceObject]
145+
}
146+
147+
type _Service {
148+
sdl: String!
149+
}
150+
""".trimIndent()
151+
152+
val config = FederatedSchemaGeneratorConfig(
153+
supportedPackages = listOf("test.data.queries.simple"),
154+
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
155+
)
156+
157+
val schema = toFederatedSchema(config, listOf(TopLevelObject(NestedQuery())))
158+
assertEquals(expectedSchema, schema.print(includeDirectives = false).trim())
159+
}
127160
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class EntityQueryResolverTest {
4343

4444
@Test
4545
fun `verify federated entity resolver returns GraphQLError if __typename cannot be resolved`() {
46-
val resolver = EntityResolver(FederatedTypeRegistry(emptyMap()))
46+
val resolver = EntityResolver(FederatedTypeRegistry())
4747
val representations = listOf(mapOf<String, Any>("__typename" to "User", "userId" to 123, "name" to "testName"))
4848
val env = mockk<DataFetchingEnvironment> {
4949
every { getArgument<Any>(any()) } returns representations

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ServiceQueryResolverTest {
4040
fun `verify can retrieve SDL using _service query`() {
4141
val config = FederatedSchemaGeneratorConfig(
4242
supportedPackages = listOf("test.data.queries.federated"),
43-
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(emptyMap()))
43+
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
4444
)
4545

4646
val schema = toFederatedSchema(config)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.data.queries.simple
2+
3+
import kotlin.random.Random
4+
5+
class NestedQuery {
6+
fun getSimpleNestedObject(): List<SelfReferenceObject?> = listOf(SelfReferenceObject())
7+
}
8+
9+
class SelfReferenceObject {
10+
val description: String? = "SelfReferenceObject"
11+
val id = Random.nextInt()
12+
fun nextObject(): List<SelfReferenceObject?> = listOf(SelfReferenceObject())
13+
}

graphql-kotlin-schema-generator/src/main/kotlin/com/expedia/graphql/generator/TypeBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal open class TypeBuilder constructor(protected val generator: SchemaGener
3030
?: objectFromReflection(type, inputType)
3131

3232
// Do not call the hook on GraphQLTypeReference as we have not generated the type yet
33-
val unwrappedType = GraphQLTypeUtil.unwrapNonNull(graphQLType)
33+
val unwrappedType = GraphQLTypeUtil.unwrapType(graphQLType).lastElement()
3434
if (unwrappedType !is GraphQLTypeReference) {
3535
val typeWithNullability = graphQLType.wrapInNonNull(type)
3636
return config.hooks.didGenerateGraphQLType(type, typeWithNullability)

graphql-kotlin-schema-generator/src/test/kotlin/com/expedia/graphql/test/integration/NodeGraphTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ data class Node(
3636
val id: Int,
3737
val value: String,
3838
val parent: Node? = null,
39-
var children: List<Node> = emptyList()
39+
var children: List<Node?> = emptyList()
4040
)
4141

4242
class NodeQuery {

0 commit comments

Comments
 (0)