Skip to content

Commit 0dc9187

Browse files
authored
Merge pull request #2145 from Netflix/fallback-type-resolver
Support fallback/global type resolver
2 parents da52907 + 09b81de commit 0dc9187

File tree

3 files changed

+92
-2
lines changed

3 files changed

+92
-2
lines changed

graphql-dgs-spring-graphql/src/main/kotlin/com/netflix/graphql/dgs/springgraphql/autoconfig/DgsSpringGraphQLAutoConfiguration.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import graphql.introspection.Introspection
7373
import graphql.schema.DataFetcherFactory
7474
import graphql.schema.DataFetchingEnvironment
7575
import graphql.schema.GraphQLCodeRegistry
76+
import graphql.schema.TypeResolver
7677
import graphql.schema.idl.RuntimeWiring
7778
import graphql.schema.idl.TypeDefinitionRegistry
7879
import io.micrometer.context.ContextRegistry
@@ -291,6 +292,7 @@ open class DgsSpringGraphQLAutoConfiguration(
291292
entityFetcherRegistry: EntityFetcherRegistry,
292293
defaultDataFetcherFactory: Optional<DataFetcherFactory<*>> = Optional.empty(),
293294
methodDataFetcherFactory: MethodDataFetcherFactory,
295+
fallbackTypeResolver: TypeResolver? = null,
294296
): DgsSchemaProvider =
295297
DgsSchemaProvider(
296298
applicationContext = applicationContext,
@@ -304,6 +306,7 @@ open class DgsSpringGraphQLAutoConfiguration(
304306
methodDataFetcherFactory = methodDataFetcherFactory,
305307
schemaWiringValidationEnabled = configProps.schemaWiringValidationEnabled,
306308
enableEntityFetcherCustomScalarParsing = configProps.enableEntityFetcherCustomScalarParsing,
309+
fallbackTypeResolver = fallbackTypeResolver,
307310
)
308311

309312
@Bean

graphql-dgs/src/main/kotlin/com/netflix/graphql/dgs/internal/DgsSchemaProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import graphql.schema.FieldCoordinates
4646
import graphql.schema.GraphQLCodeRegistry
4747
import graphql.schema.GraphQLScalarType
4848
import graphql.schema.GraphQLSchema
49+
import graphql.schema.TypeResolver
4950
import graphql.schema.idl.RuntimeWiring
5051
import graphql.schema.idl.SchemaDirectiveWiring
5152
import graphql.schema.idl.SchemaGenerator
@@ -97,6 +98,7 @@ class DgsSchemaProvider
9798
private val componentFilter: ((Any) -> Boolean)? = null,
9899
private val schemaWiringValidationEnabled: Boolean = true,
99100
private val enableEntityFetcherCustomScalarParsing: Boolean = false,
101+
private val fallbackTypeResolver: TypeResolver? = null,
100102
) {
101103
@Suppress("UNUSED_PARAMETER")
102104
@Deprecated("The mockProviders argument is no longer supported")
@@ -824,7 +826,7 @@ class DgsSchemaProvider
824826
.typeResolver { env: TypeResolutionEnvironment ->
825827
val instance = env.getObject<Any>()
826828
val resolvedType = env.schema.getObjectType(instance::class.java.simpleName)
827-
resolvedType
829+
resolvedType ?: fallbackTypeResolver?.getType(env)
828830
?: throw InvalidTypeResolverException(
829831
"The default type resolver could not find a suitable Java type for GraphQL $typeName type `$it`. Provide a @DgsTypeResolver for `${instance::class.java.simpleName}`.",
830832
)

graphql-dgs/src/test/kotlin/com/netflix/graphql/dgs/DgsSchemaProviderTest.kt

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import graphql.language.TypeName
3636
import graphql.schema.DataFetcher
3737
import graphql.schema.FieldCoordinates
3838
import graphql.schema.GraphQLCodeRegistry
39+
import graphql.schema.TypeResolver
3940
import graphql.schema.idl.TypeDefinitionRegistry
4041
import org.assertj.core.api.Assertions.assertThat
4142
import org.assertj.core.api.Assertions.assertThatNoException
@@ -81,6 +82,7 @@ internal class DgsSchemaProviderTest {
8182
componentFilter: ((Any) -> Boolean)? = null,
8283
schemaWiringValidationEnabled: Boolean = true,
8384
dataFetcherResultProcessors: List<DataFetcherResultProcessor> = emptyList(),
85+
fallbackTypeResolver: TypeResolver? = null,
8486
): DgsSchemaProvider =
8587
DgsSchemaProvider(
8688
applicationContext = applicationContext,
@@ -97,6 +99,7 @@ internal class DgsSchemaProviderTest {
9799
componentFilter = componentFilter,
98100
schemaWiringValidationEnabled = schemaWiringValidationEnabled,
99101
dataFetcherResultProcessors = dataFetcherResultProcessors,
102+
fallbackTypeResolver = fallbackTypeResolver,
100103
)
101104

102105
@DgsComponent
@@ -427,7 +430,89 @@ internal class DgsSchemaProviderTest {
427430
contextRunner.withBean(FetcherWithDefaultResolver::class.java).withBean(VideoFetcher::class.java).run { context ->
428431
assertThatNoException().isThrownBy {
429432
// verify that it should not trigger a build failure
430-
GraphQL.newGraphQL(schemaProvider(applicationContext = context).schema(schema).graphQLSchema).build()
433+
GraphQL
434+
.newGraphQL(
435+
schemaProvider(applicationContext = context).schema(schema).graphQLSchema,
436+
).build()
437+
.execute("{video{title}}")
438+
}
439+
}
440+
}
441+
442+
@Test
443+
fun `Use fallback type resolver when no @DgsTypeResolver is present`() {
444+
val schema =
445+
"""
446+
type Query {
447+
video: Video
448+
}
449+
450+
interface Video {
451+
title: String
452+
}
453+
type Show implements Video {
454+
title: String
455+
}
456+
""".trimIndent()
457+
458+
class MyTypeResolverConfig {
459+
fun myTypeResolver(): TypeResolver = TypeResolver { env -> env.schema.getObjectType("Show") }
460+
}
461+
462+
contextRunner.withBean(VideoFetcher::class.java).run { context ->
463+
assertThatNoException().isThrownBy {
464+
// verify that it should not trigger a build failure
465+
GraphQL
466+
.newGraphQL(
467+
schemaProvider(
468+
applicationContext = context,
469+
fallbackTypeResolver = MyTypeResolverConfig().myTypeResolver(),
470+
).schema(schema).graphQLSchema,
471+
).build()
472+
.execute("{video{title}}")
473+
}
474+
}
475+
}
476+
477+
@Test
478+
fun `@DgsTypeResolver should be preferred over fallback type resolver`() {
479+
val schema =
480+
"""
481+
type Query {
482+
video: Video
483+
}
484+
485+
interface Video {
486+
title: String
487+
}
488+
type Show implements Video {
489+
title: String
490+
}
491+
""".trimIndent()
492+
493+
class MyTypeResolverConfig {
494+
fun myTypeResolver(): TypeResolver = TypeResolver { env -> env.schema.getObjectType("FakeType") }
495+
}
496+
497+
@DgsComponent
498+
class FetcherWithResolver {
499+
@DgsTypeResolver(name = "Video")
500+
fun resolveType(
501+
@Suppress("unused_parameter") type: Any,
502+
): String? = null
503+
}
504+
505+
contextRunner.withBean(FetcherWithResolver::class.java).withBean(VideoFetcher::class.java).run { context ->
506+
assertThatNoException().isThrownBy {
507+
// verify that it should not trigger a build failure
508+
GraphQL
509+
.newGraphQL(
510+
schemaProvider(
511+
applicationContext = context,
512+
fallbackTypeResolver = MyTypeResolverConfig().myTypeResolver(),
513+
).schema(schema).graphQLSchema,
514+
).build()
515+
.execute("{video{title}}")
431516
}
432517
}
433518
}

0 commit comments

Comments
 (0)