Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for defining a default DataFetcherFactory via autoconfig #979

Merged
merged 3 commits into from Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -33,6 +33,7 @@ import graphql.execution.ExecutionStrategy
import graphql.execution.instrumentation.ChainedInstrumentation
import graphql.execution.instrumentation.Instrumentation
import graphql.execution.preparsed.PreparsedDocumentProvider
import graphql.schema.DataFetcherFactory
import graphql.schema.GraphQLCodeRegistry
import graphql.schema.GraphQLSchema
import graphql.schema.idl.TypeDefinitionRegistry
Expand Down Expand Up @@ -85,8 +86,10 @@ open class DgsAutoConfiguration(
preparsedDocumentProvider: PreparsedDocumentProvider,
queryValueCustomizer: QueryValueCustomizer
): DgsQueryExecutor {
val queryExecutionStrategy = providedQueryExecutionStrategy.orElse(AsyncExecutionStrategy(dataFetcherExceptionHandler))
val mutationExecutionStrategy = providedMutationExecutionStrategy.orElse(AsyncSerialExecutionStrategy(dataFetcherExceptionHandler))
val queryExecutionStrategy =
providedQueryExecutionStrategy.orElse(AsyncExecutionStrategy(dataFetcherExceptionHandler))
val mutationExecutionStrategy =
providedMutationExecutionStrategy.orElse(AsyncSerialExecutionStrategy(dataFetcherExceptionHandler))
return DefaultDgsQueryExecutor(
schema,
schemaProvider,
Expand Down Expand Up @@ -158,7 +161,8 @@ open class DgsAutoConfiguration(
dataFetcherExceptionHandler: Optional<DataFetcherExceptionHandler> = Optional.empty(),
cookieValueResolver: Optional<CookieValueResolver> = Optional.empty(),
inputObjectMapper: Optional<InputObjectMapper> = Optional.empty(),
entityFetcherRegistry: EntityFetcherRegistry
entityFetcherRegistry: EntityFetcherRegistry,
defaultDataFetcherFactory: Optional<DataFetcherFactory<*>> = Optional.empty()
): DgsSchemaProvider {
return DgsSchemaProvider(
applicationContext,
Expand All @@ -170,7 +174,8 @@ open class DgsAutoConfiguration(
dataFetcherExceptionHandler,
cookieValueResolver,
inputObjectMapper.orElse(DefaultInputObjectMapper()),
entityFetcherRegistry
entityFetcherRegistry,
defaultDataFetcherFactory
)
}

Expand Down
Expand Up @@ -18,6 +18,8 @@ package com.netflix.graphql.dgs.autoconfig

import com.netflix.graphql.dgs.DgsQueryExecutor
import com.netflix.graphql.dgs.autoconfig.testcomponents.CustomContextBuilderConfig
import com.netflix.graphql.dgs.autoconfig.testcomponents.CustomDataFetcherFactory
import com.netflix.graphql.dgs.autoconfig.testcomponents.CustomDataFetcherFactoryTest
import com.netflix.graphql.dgs.autoconfig.testcomponents.CustomInputObjectMapperConfig
import com.netflix.graphql.dgs.autoconfig.testcomponents.DataFetcherWithInputObject
import com.netflix.graphql.dgs.autoconfig.testcomponents.DataLoaderConfig
Expand All @@ -31,7 +33,8 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner
import org.springframework.core.io.ClassPathResource

class DgsAutoConfigurationTest {
private val context = WebApplicationContextRunner().withConfiguration(AutoConfigurations.of(DgsAutoConfiguration::class.java))!!
private val context =
WebApplicationContextRunner().withConfiguration(AutoConfigurations.of(DgsAutoConfiguration::class.java))!!

@Test
fun noSchemaException() {
Expand All @@ -40,6 +43,18 @@ class DgsAutoConfigurationTest {
}
}

@Test
fun setUpCustomDataFetcherFactory() {
context.withUserConfiguration(CustomDataFetcherFactory::class.java, CustomDataFetcherFactoryTest::class.java)
.run { ctx ->
assertThat(ctx).getBean(DgsQueryExecutor::class.java).extracting {
val executeQuery =
it.executeAndExtractJsonPath<String>("query {simpleNested{hello}}", "data.simpleNested.hello")
assertThat(executeQuery).isEqualTo("not world")
}
}
}

@Test
fun setsUpQueryExecutorWithDataFetcher() {
context.withUserConfiguration(HelloDataFetcherConfig::class.java).run { ctx ->
Expand Down
@@ -0,0 +1,50 @@
/*
* Copyright 2022 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.graphql.dgs.autoconfig.testcomponents

import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsData
import graphql.schema.DataFetcher
import graphql.schema.DataFetcherFactories
import graphql.schema.DataFetcherFactory
import graphql.schema.DataFetchingEnvironment
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

object TestDataFetcher : DataFetcher<Any> {
override fun get(environment: DataFetchingEnvironment): Any {
return "not world"
}
}

@Configuration
open class CustomDataFetcherFactory {
@Bean
open fun coolDataFetcherFactory(): DataFetcherFactory<*> {
return DataFetcherFactories.useDataFetcher(TestDataFetcher)
}
}

data class SimpleNested(val hello: String)

@DgsComponent
class CustomDataFetcherFactoryTest {
@DgsData(parentType = "Query", field = "simpleNested")
fun hello(dfe: DataFetchingEnvironment): SimpleNested {
return SimpleNested("world")
}
}
Expand Up @@ -21,6 +21,12 @@ type Query {
withIgnoredField(input: Input): Output

withIgnoredFieldNested(nestedInput: NestedInput): Output

simpleNested: SimpleNested!
}

type SimpleNested {
hello: String!
}

input Input {
Expand Down
Expand Up @@ -31,6 +31,7 @@ import graphql.language.TypeName
import graphql.language.UnionTypeDefinition
import graphql.schema.Coercing
import graphql.schema.DataFetcher
import graphql.schema.DataFetcherFactory
import graphql.schema.FieldCoordinates
import graphql.schema.GraphQLCodeRegistry
import graphql.schema.GraphQLScalarType
Expand Down Expand Up @@ -72,7 +73,8 @@ class DgsSchemaProvider(
private val dataFetcherExceptionHandler: Optional<DataFetcherExceptionHandler> = Optional.empty(),
private val cookieValueResolver: Optional<CookieValueResolver> = Optional.empty(),
private val inputObjectMapper: InputObjectMapper = DefaultInputObjectMapper(),
private val entityFetcherRegistry: EntityFetcherRegistry = EntityFetcherRegistry()
private val entityFetcherRegistry: EntityFetcherRegistry = EntityFetcherRegistry(),
private val defaultDataFetcherFactory: Optional<DataFetcherFactory<*>> = Optional.empty()
) {

val dataFetcherInstrumentationEnabled = mutableMapOf<String, Boolean>()
Expand Down Expand Up @@ -102,11 +104,20 @@ class DgsSchemaProvider(
}

val federationResolverInstance =
federationResolver.orElseGet { DefaultDgsFederationResolver(entityFetcherRegistry, dataFetcherExceptionHandler) }
federationResolver.orElseGet {
DefaultDgsFederationResolver(
entityFetcherRegistry,
dataFetcherExceptionHandler
)
}

val entityFetcher = federationResolverInstance.entitiesFetcher()
val typeResolver = federationResolverInstance.typeResolver()
val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry().fieldVisibility(fieldVisibility)
if (defaultDataFetcherFactory.isPresent) {
codeRegistryBuilder.defaultDataFetcher(defaultDataFetcherFactory.get())
}

val runtimeWiringBuilder =
RuntimeWiring.newRuntimeWiring().codeRegistry(codeRegistryBuilder).fieldVisibility(fieldVisibility)

Expand Down Expand Up @@ -318,7 +329,14 @@ class DgsSchemaProvider(
private fun createBasicDataFetcher(method: Method, dgsComponent: Any, isSubscription: Boolean): DataFetcher<Any?> {
return DataFetcher<Any?> { environment ->
val dfe = DgsDataFetchingEnvironment(environment)
val result = DataFetcherInvoker(cookieValueResolver, defaultParameterNameDiscoverer, dfe, dgsComponent, method, inputObjectMapper).invokeDataFetcher()
val result = DataFetcherInvoker(
cookieValueResolver,
defaultParameterNameDiscoverer,
dfe,
dgsComponent,
method,
inputObjectMapper
).invokeDataFetcher()
when {
isSubscription -> {
result
Expand Down