diff --git a/docs/server/spring-beans.md b/docs/server/spring-beans.md index 868d7c9917..572581c334 100644 --- a/docs/server/spring-beans.md +++ b/docs/server/spring-beans.md @@ -20,6 +20,7 @@ can be customized by providing custom beans in your application context. See sec | Bean | Description | |:-------------------------------|:------------| +| ContextWebFilter | Default web filter that populates GraphQL context in the reactor subscriber context. | | DataFetcherExceptionHandler | GraphQL exception handler used from the various execution strategies, defaults to [KotlinDataFetcherExceptionHandler](https://github.com/ExpediaGroup/graphql-kotlin/blob/master/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/exception/KotlinDataFetcherExceptionHandler.kt). | | GraphQL | GraphQL query execution engine generated using `GraphQLSchema` with default async execution strategies. GraphQL engine can be customized by optionally providing a list of [Instrumentation](https://www.graphql-java.com/documentation/v13/instrumentation/) beans (which can be ordered by implementing Spring Ordered interface), [ExecutionIdProvider](https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/execution/ExecutionIdProvider.java) and [PreparsedDocumentProvider](https://github.com/graphql-java/graphql-java/blob/master/src/main/java/graphql/execution/preparsed/PreparsedDocumentProvider.java) in the application context. | | DataLoaderRegistryFactory | Factory used to create DataLoaderRegistry instance per query execution. See [graphql-java documentation](https://www.graphql-java.com/documentation/v13/batching/) for more details. | @@ -30,7 +31,6 @@ can be customized by providing custom beans in your application context. See sec The following beans are currently automatically created and cannot be disabled: -* Web filter for generating and populating GraphQL context * Default routes for GraphQL queries/mutations and SDL endpoint * Default route for [Prisma Labs Playground](https://github.com/prisma-labs/graphql-playground), created only if playground is enabled * Default `ApolloSubscriptionProtocolHandler` for handling GraphQL subscriptions, created only if `Subscription` bean is available in the context diff --git a/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/GraphQLAutoConfiguration.kt b/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/GraphQLAutoConfiguration.kt index 63f75767b2..df2c9a2bdb 100644 --- a/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/GraphQLAutoConfiguration.kt +++ b/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/GraphQLAutoConfiguration.kt @@ -40,7 +40,6 @@ import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Import import org.springframework.core.Ordered -import org.springframework.web.server.WebFilter import java.util.Optional /** @@ -119,8 +118,9 @@ class GraphQLAutoConfiguration { fun graphQLContextFactory(): GraphQLContextFactory<*> = EmptyContextFactory @Bean + @ConditionalOnMissingBean fun contextWebFilter( config: GraphQLConfigurationProperties, graphQLContextFactory: GraphQLContextFactory<*> - ): WebFilter = ContextWebFilter(config, graphQLContextFactory) + ): ContextWebFilter = ContextWebFilter(config, graphQLContextFactory) } diff --git a/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/ContextWebFilter.kt b/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/ContextWebFilter.kt index c944b161c2..3326949ce7 100644 --- a/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/ContextWebFilter.kt +++ b/graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/execution/ContextWebFilter.kt @@ -33,9 +33,9 @@ const val GRAPHQL_CONTEXT_FILTER_ODER = 0 /** * Default web filter that populates GraphQL context in the reactor subscriber context. */ -class ContextWebFilter(config: GraphQLConfigurationProperties, private val contextFactory: GraphQLContextFactory) : WebFilter, Ordered { - private val graphQLRoute = "/${config.endpoint}" - private val subscriptionsRoute = "/${config.subscriptions.endpoint}" +open class ContextWebFilter(config: GraphQLConfigurationProperties, private val contextFactory: GraphQLContextFactory) : WebFilter, Ordered { + private val graphQLRoute = enforceAbsolutePath(config.endpoint) + private val subscriptionsRoute = enforceAbsolutePath(config.subscriptions.endpoint) @Suppress("ForbiddenVoid") override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono = @@ -51,6 +51,8 @@ class ContextWebFilter(config: GraphQLConfigurationProperties, private val conte override fun getOrder(): Int = GRAPHQL_CONTEXT_FILTER_ODER - internal fun isApplicable(path: String): Boolean = + open fun isApplicable(path: String): Boolean = graphQLRoute.equals(path, ignoreCase = true) || subscriptionsRoute.equals(path, ignoreCase = true) + + private fun enforceAbsolutePath(path: String) = if (path.startsWith("/")) { path } else { "/$path" } } diff --git a/graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/SchemaConfigurationTest.kt b/graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/SchemaConfigurationTest.kt index 7d3eb0ee75..3870f46136 100644 --- a/graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/SchemaConfigurationTest.kt +++ b/graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/SchemaConfigurationTest.kt @@ -18,6 +18,7 @@ package com.expediagroup.graphql.spring import com.expediagroup.graphql.SchemaGeneratorConfig import com.expediagroup.graphql.TopLevelObject +import com.expediagroup.graphql.spring.execution.ContextWebFilter import com.expediagroup.graphql.spring.execution.DataLoaderRegistryFactory import com.expediagroup.graphql.spring.execution.GraphQLContextFactory import com.expediagroup.graphql.spring.execution.QueryHandler @@ -75,6 +76,7 @@ class SchemaConfigurationTest { assertThat(ctx).hasSingleBean(DataLoaderRegistryFactory::class.java) assertThat(ctx).hasSingleBean(QueryHandler::class.java) + assertThat(ctx).hasSingleBean(ContextWebFilter::class.java) assertThat(ctx).hasSingleBean(GraphQLContextFactory::class.java) } } @@ -85,6 +87,7 @@ class SchemaConfigurationTest { .withPropertyValues("graphql.packages=com.expediagroup.graphql.spring") .run { ctx -> val customConfiguration = ctx.getBean(CustomConfiguration::class.java) + val graphQLProperties = ctx.getBean(GraphQLConfigurationProperties::class.java) assertThat(ctx).hasSingleBean(SchemaGeneratorConfig::class.java) assertThat(ctx).getBean(SchemaGeneratorConfig::class.java) @@ -104,6 +107,10 @@ class SchemaConfigurationTest { assertThat(ctx).hasSingleBean(QueryHandler::class.java) + assertThat(ctx).hasSingleBean(ContextWebFilter::class.java) + assertThat(ctx).getBean(ContextWebFilter::class.java) + .isSameAs(customConfiguration.myCustomContextWebFilter(graphQLProperties, customConfiguration.myCustomContextFactory())) + assertThat(ctx).hasSingleBean(GraphQLContextFactory::class.java) assertThat(ctx).getBean(GraphQLContextFactory::class.java) .isSameAs(customConfiguration.myCustomContextFactory()) @@ -152,6 +159,16 @@ class SchemaConfigurationTest { @Bean fun myDataLoaderRegistryFactory(): DataLoaderRegistryFactory = mockk() + + @Bean + fun myCustomContextWebFilter( + config: GraphQLConfigurationProperties, + graphQLContextFactory: GraphQLContextFactory<*> + ): ContextWebFilter = object : ContextWebFilter(config, graphQLContextFactory) { + private val regex = config.endpoint.toRegex() + + override fun isApplicable(path: String): Boolean = regex.matches(path) + } } class BasicQuery : Query {