graphql-java-tools icon indicating copy to clipboard operation
graphql-java-tools copied to clipboard

Co-routine support in directives.

Open alacoste opened this issue 6 years ago • 1 comments

First of all thanks for the great library, it has been working amazing for us so far!

We were happily surprised when graphql-java-kickstart worked immediately with suspend resolvers. We would now like to use GraphQL directives and are hoping there is similar support for it, however I couldn't find the way to make it work. I also didn't find the equivalent of https://github.com/graphql-java-kickstart/graphql-java-tools/issues/104 for suspending directives.

Is our use case already supported?

Reasons we would like to make the directive logic suspending: we want to use directives for authorization and would like to make on-the-fly database calls when we need to validate permissions in the directive.

alacoste avatar Aug 23 '19 11:08 alacoste

For anyone looking at this, we are currently using the following workaround:

// Returns a modified DataFetcher that runs code before the normal resolution. This is typically useful to verify
// sufficient permissions before performing an operation.
//
// This is the pendant of DataFetcherFactories.wrapDataFetcher that allows to run code after the normal resolution to
// modify the returned object.
fun DataFetcher<*>.withPreprocessing(
    block: suspend (DataFetchingEnvironment) -> Unit
): (DataFetchingEnvironment) -> CompletableFuture<Any?> = { env: DataFetchingEnvironment ->
    GlobalScope.async {
        block(env)
        [email protected](env).awaitIfCompletableFuture()
    }.asCompletableFuture()
}

// If the receiver object is a CompletableFuture, awaits it. Otherwise passes the object through.
suspend fun Any?.awaitIfCompletableFuture(): Any? = if (this is CompletableFuture<*>) {
    this.await()
} else {
    this
}

// Example directive object.
object MyDirective : SchemaDirectiveWiring {
    override fun onField(directiveEnv: SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition>): GraphQLFieldDefinition {
        val originalDataFetcher: DataFetcher<*> = directiveEnv.fieldDataFetcher

        directiveEnv.setFieldDataFetcher(originalDataFetcher.withPreprocessing { env: DataFetchingEnvironment -> /* your custom suspending logic here */ })
        return directiveEnv.element
    }
}

alacoste avatar Oct 02 '19 14:10 alacoste