Permalink
Browse files

Add a helper class for making asynchronous data fetchers (#798)

* Ignore files generated by Eclipse

* Add a helper class for making asynchronous data fetchers

* Add the ability to set the executor an async data fetcher will run in

* Reinstate original documentation and add further section for new method

* Make the fields of AsynchronousDataFetcher final
  • Loading branch information...
RusticFlare authored and bbakerman committed Oct 27, 2017
1 parent 0255331 commit c59c31ab41387b9bdb173c8772036e3f020bbe03
View
@@ -8,4 +8,8 @@ classes
_site
generated-src/
out
docs/_build/
docs/_build/
/bin/
\.classpath
\.project
\.settings/
View
@@ -345,6 +345,14 @@ The code above is written in long form. With Java 8 lambdas it can be written m
The graphql-java engine ensures that all the ``CompletableFuture`` objects are composed together to provide an execution result
that follows the graphql specification.
There is a helpful shortcut in graphql-java to create asynchronous data fetchers.
Use ``graphql.schema.AsynchronousDataFetcher.async(DataFetcher<T>)`` to wrap a
``DataFetcher``. This can be used with static imports to produce more readable code.
.. code-block:: java
DataFetcher userDataFetcher = async(environment -> fetchUserViaHttp(environment.getArgument("userId")));
Execution Strategies
--------------------
@@ -0,0 +1,69 @@
package graphql.schema;
import static graphql.Assert.assertNotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import graphql.PublicApi;
/**
* A modifier type that indicates the underlying data fetcher is run asynchronously
*/
@PublicApi
public class AsynchronousDataFetcher<T> implements DataFetcher<CompletableFuture<T>> {
/**
* A factory method for creating asynchronous data fetchers so that when used with
* static imports allows more readable code such as:
* <p>
* {@code .dataFetcher(async(fooDataFetcher))}
* <p>
* By default this will run in the {@link ForkJoinPool#commonPool()}. You can set
* your own {@link Executor} with {@link #asyncWithExecutor(DataFetcher, Executor)}
*
* @param wrappedDataFetcher the data fetcher to run asynchronously
*
* @return a {@link DataFetcher} that will run the wrappedDataFetcher asynchronously
*/
public static <T> AsynchronousDataFetcher<T> async(DataFetcher<T> wrappedDataFetcher) {
return new AsynchronousDataFetcher<>(wrappedDataFetcher);
}
/**
* A factory method for creating asynchronous data fetchers and setting the
* {@link Executor} they run in so that when used with static imports allows
* more readable code such as:
* <p>
* {@code .dataFetcher(asyncWithExecutor(fooDataFetcher, fooPool))}
*
* @param wrappedDataFetcher the data fetcher to run asynchronously
* @param executor to run the asynchronous data fetcher in
*
* @return a {@link DataFetcher} that will run the wrappedDataFetcher asynchronously in
* the given {@link Executor}
*/
public static <T> AsynchronousDataFetcher<T> asyncWithExecutor(DataFetcher<T> wrappedDataFetcher,
Executor executor) {
return new AsynchronousDataFetcher<>(wrappedDataFetcher, executor);
}
private final DataFetcher<T> wrappedDataFetcher;
private final Executor executor;
public AsynchronousDataFetcher(DataFetcher<T> wrappedDataFetcher) {
this(wrappedDataFetcher, ForkJoinPool.commonPool());
}
public AsynchronousDataFetcher(DataFetcher<T> wrappedDataFetcher, Executor executor) {
this.wrappedDataFetcher = assertNotNull(wrappedDataFetcher, "wrappedDataFetcher can't be null");
this.executor = assertNotNull(executor, "executor can't be null");
}
@Override
public CompletableFuture<T> get(DataFetchingEnvironment environment) {
return CompletableFuture.supplyAsync(() -> wrappedDataFetcher.get(environment), executor);
}
}
@@ -0,0 +1,32 @@
package graphql.schema
import graphql.GraphQL
import graphql.StarWarsData
import graphql.TestUtil
import graphql.execution.FieldCollector
import graphql.language.AstPrinter
import graphql.language.Field
import graphql.schema.idl.MapEnumValuesProvider
import graphql.schema.idl.RuntimeWiring
import spock.lang.Specification
import java.util.concurrent.CompletableFuture
import java.util.stream.Collectors
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring
class AsynchronousDataFetcherTest extends Specification {
def "A data fetcher can be made asynchronous with AsynchronousDataFetcher#async"() {
given:
DataFetchingEnvironment environment = Mock(DataFetchingEnvironment)
when:
DataFetcher asyncDataFetcher = AsynchronousDataFetcher.async({ env -> "value" })
then:
asyncDataFetcher.get(environment) instanceof CompletableFuture
asyncDataFetcher.get(environment).get() == "value"
}
}

0 comments on commit c59c31a

Please sign in to comment.