Ferranostra
12/07/2022, 10:10 PMDariusz Kuc
12/08/2022, 12:36 AMgraphql-kotlin
supports automatic context propagation since v6 (see https://github.com/ExpediaGroup/graphql-kotlin/releases/tag/6.0.0) so if you add something to the Reactor context in the WebFilter, it will be accessible from the GraphQL context.
GraphQL context is not a Spring bean so it cannot be autowired but you can access it through DataFetchingEnvironment
- see https://opensource.expediagroup.com/graphql-kotlin/docs/schema-generator/execution/contextual-data#graphql-context-mapFerranostra
12/08/2022, 12:03 PMorg.springframework.web.server.WebFilter
and reuse it in any place like
Mono.deferContextual(...)
?
Use case is that I have to read header parameters and put it inside webclient’s header.
Graphql context looks overloaded. I can put it only on Mutation/Query level and then send in each function as a parameter which is not the best way.Dariusz Kuc
12/08/2022, 2:02 PMFerranostra
12/08/2022, 2:03 PMFerranostra
12/08/2022, 2:04 PMFerranostra
12/08/2022, 2:04 PM@Component
class UserWebFilter : WebFilter {
override fun filter(serverWebExchange: ServerWebExchange, webFilterChain: WebFilterChain): Mono<Void> =
webFilterChain
.filter(serverWebExchange)
.contextWrite {
context(it, serverWebExchange)
}.then()
private fun context(
it: Context,
serverWebExchange: ServerWebExchange
): Context {
val context = putHeaderToContext(
putHeaderToContext(it, serverWebExchange, X_USER_ID, UUID::fromString),
serverWebExchange,
AUTHORIZATION
) { a -> a!! }
return context
}
private fun getHeader(serverWebExchange: ServerWebExchange, headerName: String) =
serverWebExchange
.request
.headers
.getFirst(headerName)
private fun putHeaderToContext(
context: Context,
serverWebExchange: ServerWebExchange,
headerName: String,
transformFunction: Function<String?, Any>
): Context {
val headerValue: String? = getHeader(serverWebExchange, headerName)
return if (ObjectUtils.isNotEmpty(headerValue))
context.put(
headerName,
transformFunction.apply(headerValue)
) else context
// throw ForbiddenRequestException(MISSED_REQUIRED_HEADER_PARAMETERS)
}
}
Dariusz Kuc
12/08/2022, 2:04 PMFerranostra
12/08/2022, 2:05 PM<graphql-kotlin-spring-server.version>6.3.0</graphql-kotlin-spring-server.version>
Ferranostra
12/08/2022, 2:05 PMDariusz Kuc
12/08/2022, 2:05 PMDariusz Kuc
12/08/2022, 2:06 PMFerranostra
12/08/2022, 2:06 PM@Component
class BoardQuery(
private val boardService: BoardService
) : Query {
fun getBoardFull(
id: UUID
) = Mono.deferContextual { c -> boardService.getBoard(id, c)}
context
under deferCotextual
is emptyFerranostra
12/08/2022, 2:07 PM@Component
class ContextualQuery : Query {
@GraphQLDescription("query that uses GraphQLContext context")
fun contextualQuery(
@GraphQLDescription("some value that will be returned to the user")
value: Int,
env: DataFetchingEnvironment
): ContextualResponse = ContextualResponse(value, env.graphQlContext.getOrDefault("myCustomValue", "defaultValue"))
}
Ferranostra
12/08/2022, 2:08 PMWebflux Context
instead of DataFetchingEnvironment
Ferranostra
12/08/2022, 2:12 PMFerranostra
12/08/2022, 2:14 PMDariusz Kuc
12/08/2022, 3:54 PMAs long as you are running coroutines then yes it should have access to the reactor context (due to reactor <-> coroutine interop)
Default data fetcher logic runs the coroutines in the original scope (https://github.com/ExpediaGroup/graphql-kotlin/blob/master/generator/graphql-kotli[…]expediagroup/graphql/generator/execution/FunctionDataFetcher.kt).
Your method (fun getBoardFull
) is NOT a coroutine hence it won't get the context set. You will either have to use custom data fetcher that wraps up the Mono execution OR switch to coroutines.Dariusz Kuc
12/08/2022, 3:55 PMDataFetchingEnvironment
is automatic for both coroutine and regular functionsFerranostra
12/08/2022, 4:07 PMFerranostra
12/09/2022, 7:46 PMimport com.expediagroup.graphql.server.spring.execution.SpringDataFetcher
...
/**
* Custom function data fetcher that adds support for Reactor Mono.
*/
class CustomFunctionDataFetcher(
target: Any?,
fn: KFunction<*>,
appContext: ApplicationContext
) : SpringDataFetcher(target, fn, appContext) {
override fun get(environment: DataFetchingEnvironment): Any? {
val context = environment.getContext<GraphQLRequestContext>()
return when (val result = super.get(environment)) {
is Mono<*> -> result.contextWrite { c ->
c.put(X_USER_ID, context.xUserId)
.put(AUTHORIZATION, context.authorization)
}.toFuture()
else -> result
}
}
}
Ferranostra
12/13/2022, 1:40 PM