All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.netflix.graphql.dgs.springgraphql.autoconfig.DgsSpringGraphQLAutoConfiguration.kt Maven / Gradle / Ivy

There is a newer version: 10.0.1
Show newest version
/*
 * Copyright 2024 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.springgraphql.autoconfig

import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsQueryExecutor
import com.netflix.graphql.dgs.DgsRuntimeWiring
import com.netflix.graphql.dgs.DgsTypeDefinitionRegistry
import com.netflix.graphql.dgs.context.GraphQLContextContributor
import com.netflix.graphql.dgs.internal.*
import com.netflix.graphql.dgs.internal.method.ArgumentResolver
import com.netflix.graphql.dgs.mvc.internal.method.HandlerMethodArgumentResolverAdapter
import com.netflix.graphql.dgs.reactive.DgsReactiveCustomContextBuilderWithRequest
import com.netflix.graphql.dgs.reactive.DgsReactiveQueryExecutor
import com.netflix.graphql.dgs.reactive.internal.DefaultDgsReactiveGraphQLContextBuilder
import com.netflix.graphql.dgs.reactive.internal.method.SyncHandlerMethodArgumentResolverAdapter
import com.netflix.graphql.dgs.springgraphql.SpringGraphQLDgsQueryExecutor
import com.netflix.graphql.dgs.springgraphql.SpringGraphQLDgsReactiveQueryExecutor
import com.netflix.graphql.dgs.springgraphql.webflux.DgsWebFluxGraphQLInterceptor
import com.netflix.graphql.dgs.springgraphql.webmvc.DgsWebMvcGraphQLInterceptor
import graphql.execution.AsyncExecutionStrategy
import graphql.execution.AsyncSerialExecutionStrategy
import graphql.execution.DataFetcherExceptionHandler
import graphql.execution.ExecutionStrategy
import graphql.execution.preparsed.PreparsedDocumentProvider
import graphql.introspection.Introspection
import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.TypeDefinitionRegistry
import org.reactivestreams.Publisher
import org.springframework.beans.factory.ObjectProvider
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConfigurableBeanFactory
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.AutoConfigureBefore
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.ReactiveAdapterRegistry
import org.springframework.core.env.Environment
import org.springframework.graphql.ExecutionGraphQlService
import org.springframework.graphql.execution.*
import org.springframework.web.bind.support.WebDataBinderFactory
import org.springframework.web.filter.reactive.ServerWebExchangeContextFilter
import org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
import org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
import org.springframework.web.reactive.BindingContext
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.result.method.annotation.CookieValueMethodArgumentResolver
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
import org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory
import java.util.Optional

/**
 * Framework auto configuration based on open source Spring only, without Netflix integrations.
 * This does NOT have logging, tracing, metrics and security integration.
 */
@Suppress("SpringJavaInjectionPointsAutowiringInspection")
@AutoConfiguration
@AutoConfigureBefore(name = ["com.netflix.graphql.dgs.autoconfig.DgsAutoConfiguration"])
@EnableConfigurationProperties(DgsSpringGraphQLConfigurationProperties::class, DgsGraphQLConfigurationProperties::class)
open class DgsSpringGraphQLAutoConfiguration {
    @Bean
    @DgsComponent
    open fun dgsRuntimeWiringConfigurerBridge(configurers: List): DgsRuntimeWiringConfigurerBridge =
        DgsRuntimeWiringConfigurerBridge(configurers)

    class DgsRuntimeWiringConfigurerBridge(
        private val configurers: List,
    ) {
        @DgsRuntimeWiring
        fun runtimeWiring(builder: RuntimeWiring.Builder): RuntimeWiring.Builder {
            configurers.forEach {
                it.configure(builder)
            }
            return builder
        }
    }

    @Bean
    @ConditionalOnProperty("dgs.springgraphql.pagination.enabled", havingValue = "true", matchIfMissing = true)
    @DgsComponent
    open fun dgsTypeDefinitionConfigurerBridge(environment: Environment): DgsTypeDefinitionConfigurerBridge =
        DgsTypeDefinitionConfigurerBridge()

    class DgsTypeDefinitionConfigurerBridge {
        @DgsTypeDefinitionRegistry
        fun typeDefinitionRegistry(typeDefinitionRegistry: TypeDefinitionRegistry): TypeDefinitionRegistry {
            val newTypeDefinitionRegistry = TypeDefinitionRegistry()
            ConnectionTypeDefinitionConfigurer().configure(typeDefinitionRegistry)
            return newTypeDefinitionRegistry
        }
    }

    @Bean
    open fun sourceBuilderCustomizer(
        preparsedDocumentProvider: Optional,
        @Qualifier("query") providedQueryExecutionStrategy: Optional,
        @Qualifier("mutation") providedMutationExecutionStrategy: Optional,
        dataFetcherExceptionHandler: DataFetcherExceptionHandler,
    ): GraphQlSourceBuilderCustomizer {
        val queryExecutionStrategy =
            providedQueryExecutionStrategy.orElse(AsyncExecutionStrategy(dataFetcherExceptionHandler))
        val mutationExecutionStrategy =
            providedMutationExecutionStrategy.orElse(AsyncSerialExecutionStrategy(dataFetcherExceptionHandler))

        return GraphQlSourceBuilderCustomizer { builder ->
            builder.configureGraphQl { graphQlBuilder ->
                if (preparsedDocumentProvider.isPresent) {
                    graphQlBuilder
                        .preparsedDocumentProvider(preparsedDocumentProvider.get())
                }
                graphQlBuilder
                    .queryExecutionStrategy(queryExecutionStrategy)
                    .mutationExecutionStrategy(mutationExecutionStrategy)
            }
        }
    }

    @Bean
    @ConditionalOnProperty(
        prefix = "spring.graphql.schema.introspection",
        name = ["enabled"],
        havingValue = "false",
        matchIfMissing = false,
    )
    open fun disableIntrospectionContextContributor(): GraphQLContextContributor =
        GraphQLContextContributor {
                builder,
                _,
                _,
            ->
            builder.put(Introspection.INTROSPECTION_DISABLED, true)
        }

    @Bean
    open fun springGraphQLDgsQueryExecutor(
        executionService: ExecutionGraphQlService,
        dgsContextBuilder: DefaultDgsGraphQLContextBuilder,
        dgsDataLoaderProvider: DgsDataLoaderProvider,
        requestCustomizer: ObjectProvider,
    ): DgsQueryExecutor =
        SpringGraphQLDgsQueryExecutor(
            executionService,
            dgsContextBuilder,
            dgsDataLoaderProvider,
            requestCustomizer = requestCustomizer.getIfAvailable(DgsQueryExecutorRequestCustomizer::DEFAULT_REQUEST_CUSTOMIZER),
        )

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    open class WebMvcConfiguration(
        private val dgsSpringGraphQLConfigurationProperties: DgsSpringGraphQLConfigurationProperties,
    ) {
        @Bean
        open fun dgsGraphQlInterceptor(
            dgsDataLoaderProvider: DgsDataLoaderProvider,
            dgsDefaultContextBuilder: DefaultDgsGraphQLContextBuilder,
        ): DgsWebMvcGraphQLInterceptor =
            DgsWebMvcGraphQLInterceptor(
                dgsDataLoaderProvider,
                dgsDefaultContextBuilder,
                dgsSpringGraphQLConfigurationProperties,
            )
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    open class WebMvcArgumentHandlerConfiguration {
        @Qualifier
        private annotation class Dgs

        @Bean
        @Dgs
        open fun dgsWebDataBinderFactory(adapter: ObjectProvider): WebDataBinderFactory =
            ServletRequestDataBinderFactory(listOf(), adapter.ifAvailable?.webBindingInitializer)

        @Bean
        open fun requestHeaderMapResolver(
            @Dgs dataBinderFactory: WebDataBinderFactory,
        ): ArgumentResolver = HandlerMethodArgumentResolverAdapter(RequestHeaderMapMethodArgumentResolver(), dataBinderFactory)

        @Bean
        open fun requestHeaderResolver(
            beanFactory: ConfigurableBeanFactory,
            @Dgs dataBinderFactory: WebDataBinderFactory,
        ): ArgumentResolver =
            HandlerMethodArgumentResolverAdapter(
                RequestHeaderMethodArgumentResolver(beanFactory),
                dataBinderFactory,
            )

        @Bean
        open fun requestParamResolver(
            @Dgs dataBinderFactory: WebDataBinderFactory,
        ): ArgumentResolver = HandlerMethodArgumentResolverAdapter(RequestParamMethodArgumentResolver(false), dataBinderFactory)

        @Bean
        open fun requestParamMapResolver(
            @Dgs dataBinderFactory: WebDataBinderFactory,
        ): ArgumentResolver = HandlerMethodArgumentResolverAdapter(RequestParamMapMethodArgumentResolver(), dataBinderFactory)

        @Bean
        open fun cookieValueResolver(
            beanFactory: ConfigurableBeanFactory,
            @Dgs dataBinderFactory: WebDataBinderFactory,
        ): ArgumentResolver =
            HandlerMethodArgumentResolverAdapter(
                ServletCookieValueMethodArgumentResolver(beanFactory),
                dataBinderFactory,
            )
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(Publisher::class, ServerRequest::class)
    open class ReactiveConfiguration {
        @Bean
        open fun springGraphQLDgsReactiveQueryExecutor(
            executionService: ExecutionGraphQlService,
            dgsContextBuilder: DefaultDgsReactiveGraphQLContextBuilder,
            dgsDataLoaderProvider: DgsDataLoaderProvider,
        ): DgsReactiveQueryExecutor = SpringGraphQLDgsReactiveQueryExecutor(executionService, dgsContextBuilder, dgsDataLoaderProvider)

        @Bean
        @ConditionalOnMissingBean
        open fun reactiveGraphQlContextBuilder(
            dgsReactiveCustomContextBuilderWithRequest: Optional>,
        ): DefaultDgsReactiveGraphQLContextBuilder = DefaultDgsReactiveGraphQLContextBuilder(dgsReactiveCustomContextBuilderWithRequest)

        @Bean
        @ConditionalOnMissingBean
        open fun serverWebExchangeContextFilter(): ServerWebExchangeContextFilter = ServerWebExchangeContextFilter()
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    open class WebFluxConfiguration {
        @Bean
        open fun webFluxDgsGraphQLInterceptor(
            dgsDataLoaderProvider: DgsDataLoaderProvider,
            defaultDgsReactiveGraphQLContextBuilder: DefaultDgsReactiveGraphQLContextBuilder,
        ): DgsWebFluxGraphQLInterceptor =
            DgsWebFluxGraphQLInterceptor(
                dgsDataLoaderProvider,
                defaultDgsReactiveGraphQLContextBuilder,
            )
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
    open class WebFluxArgumentHandlerConfiguration {
        @Qualifier
        private annotation class Dgs

        @Dgs
        @Bean
        open fun dgsBindingContext(
            adapter: ObjectProvider,
        ): BindingContext = BindingContext(adapter.ifAvailable?.webBindingInitializer)

        @Bean
        open fun cookieValueArgumentResolver(
            beanFactory: ConfigurableBeanFactory,
            registry: ReactiveAdapterRegistry,
            @Dgs bindingContext: BindingContext,
        ): ArgumentResolver =
            SyncHandlerMethodArgumentResolverAdapter(
                CookieValueMethodArgumentResolver(beanFactory, registry),
                bindingContext,
            )

        @Bean
        open fun requestHeaderMapArgumentResolver(
            registry: ReactiveAdapterRegistry,
            @Dgs bindingContext: BindingContext,
        ): ArgumentResolver =
            SyncHandlerMethodArgumentResolverAdapter(
                org.springframework.web.reactive.result.method.annotation.RequestHeaderMapMethodArgumentResolver(
                    registry,
                ),
                bindingContext,
            )

        @Bean
        open fun requestHeaderArgumentResolver(
            beanFactory: ConfigurableBeanFactory,
            registry: ReactiveAdapterRegistry,
            @Dgs bindingContext: BindingContext,
        ): ArgumentResolver =
            SyncHandlerMethodArgumentResolverAdapter(
                org.springframework.web.reactive.result.method.annotation.RequestHeaderMethodArgumentResolver(
                    beanFactory,
                    registry,
                ),
                bindingContext,
            )

        @Bean
        open fun requestParamArgumentResolver(
            beanFactory: ConfigurableBeanFactory,
            registry: ReactiveAdapterRegistry,
            @Dgs bindingContext: BindingContext,
        ): ArgumentResolver =
            SyncHandlerMethodArgumentResolverAdapter(
                org.springframework.web.reactive.result.method.annotation.RequestParamMethodArgumentResolver(
                    beanFactory,
                    registry,
                    false,
                ),
                bindingContext,
            )

        @Bean
        open fun requestParamMapArgumentResolver(
            beanFactory: ConfigurableBeanFactory,
            registry: ReactiveAdapterRegistry,
            @Dgs bindingContext: BindingContext,
        ): ArgumentResolver =
            SyncHandlerMethodArgumentResolverAdapter(
                org.springframework.web.reactive.result.method.annotation
                    .RequestParamMapMethodArgumentResolver(registry),
                bindingContext,
            )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy