Register batch load function by name

This is an alternative to providing the key and value types, which are
only used to form the DataLoader name.

See gh-130
This commit is contained in:
Rossen Stoyanchev
2021-09-24 10:31:17 +01:00
parent d68edb47c9
commit f8f6a3ed9f
4 changed files with 63 additions and 21 deletions

View File

@@ -44,16 +44,39 @@ import reactor.core.publisher.Mono;
public interface BatchLoaderRegistry {
/**
* Start the registration of a new function for batch loading data values by
* specifying the key and value types.
* @param keyType the type of the key that identifies the value
* @param valueType the type of the data value
* Begin the registration of a new batch load function by specifying the
* types of the keys and values that will be used as input and output.
*
* <p>When this method is used, the name for the
* {@link org.dataloader.DataLoader} is automatically set as defined in
* {@link RegistrationSpec#withName(String)}, and likewise,
* {@code @SchemaMapping} handler methods can transparenly locate and
* inject a {@code DataLoader<T>} argument based on the generic type
* {@code <T>}.
*
* @param keyType the type of keys that will be used as input
* @param valueType the type of value that will be returned as output
* @param <K> the key type
* @param <V> the value type
* @return a spec to complete the registration
*/
<K, V> RegistrationSpec<K, V> forTypePair(Class<K> keyType, Class<V> valueType);
/**
* Begin the registration of a new batch load function by specifying the
* name for the {@link org.dataloader.DataLoader}.
*
* <p><strong>Note:</strong> when this method is used, the parameter name
* of a {@code DataLoader<T>} argument in a {@code @SchemaMapping} handler
* method needs to match the name given here.
*
* @param name the name to use to register a {@code DataLoader}
* @param <K> the type of keys that will be used as input
* @param <V> the type of values that will be used as output
* @return a spec to complete the registration
*/
<K, V> RegistrationSpec<K, V> forName(String name);
/**
* Spec to complete the registration of a batch loading function.
@@ -66,7 +89,8 @@ public interface BatchLoaderRegistry {
/**
* Customize the name under which the {@link org.dataloader.DataLoader}
* is registered and can be accessed in the data layer.
* <p>By default, this is the full class name of the value type.
* <p>By default, this is the full class name of the value type, if the
* value type is specified via {@link #forTypePair(Class, Class)}.
* @param name the name to use
* @return a spec to complete the registration
*/

View File

@@ -33,6 +33,10 @@ import org.dataloader.MappedBatchLoaderWithContext;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A default implementation of {@link BatchLoaderRegistry} that accepts
* registrations, and also an implementation of {@link DataLoaderRegistrar} to
@@ -50,7 +54,12 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry, DataLoad
@Override
public <K, V> RegistrationSpec<K, V> forTypePair(Class<K> keyType, Class<V> valueType) {
return new DefaultRegistrationSpec<>(valueType.getName());
return new DefaultRegistrationSpec<>(valueType);
}
@Override
public <K, V> RegistrationSpec<K, V> forName(String name) {
return new DefaultRegistrationSpec<>(name);
}
@Override
@@ -75,12 +84,21 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry, DataLoad
private class DefaultRegistrationSpec<K, V> implements RegistrationSpec<K, V> {
@Nullable
private final Class<?> valueType;
@Nullable
private String name;
private DataLoaderOptions options = DataLoaderOptions.newOptions();
public DefaultRegistrationSpec(Class<V> valueType) {
this.valueType = valueType;
}
public DefaultRegistrationSpec(String name) {
this.name = name;
this.valueType = null;
}
@Override
@@ -104,13 +122,21 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry, DataLoad
@Override
public void registerBatchLoader(BiFunction<List<K>, BatchLoaderEnvironment, Flux<V>> loader) {
DefaultBatchLoaderRegistry.this.loaders.add(
new ReactorBatchLoader<>(this.name, loader, this.options));
new ReactorBatchLoader<>(initName(), loader, this.options));
}
@Override
public void registerMappedBatchLoader(BiFunction<Set<K>, BatchLoaderEnvironment, Mono<Map<K, V>>> loader) {
DefaultBatchLoaderRegistry.this.mappedLoaders.add(
new ReactorMappedBatchLoader<>(this.name, loader, this.options));
new ReactorMappedBatchLoader<>(initName(), loader, this.options));
}
private String initName() {
if (StringUtils.hasText(this.name)) {
return this.name;
}
Assert.notNull(this.valueType, "Value type not available to select a default DataLoader name.");
return (StringUtils.hasText(this.name) ? this.name : this.valueType.getName());
}
}

View File

@@ -28,7 +28,6 @@ import reactor.core.publisher.Flux;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.graphql.Author;
import org.springframework.graphql.Book;
import org.springframework.graphql.execution.BatchLoaderRegistry;
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
import org.springframework.util.ClassUtils;
@@ -57,6 +56,7 @@ public class DataLoaderArgumentResolverTests {
@Test
void resolveArgument() {
DataFetchingEnvironment environment = initEnvironment(registry ->
registry.forTypePair(Long.class, Author.class).registerBatchLoader((ids, env) -> Flux.empty()));
@@ -67,9 +67,7 @@ public class DataLoaderArgumentResolverTests {
@Test
void resolveArgumentViaParameterName() {
DataFetchingEnvironment environment = initEnvironment(registry ->
registry.forTypePair(Long.class, Author.class)
.withName("namedDataLoader")
.registerBatchLoader((ids, env) -> Flux.empty()));
registry.forName("namedDataLoader").registerBatchLoader((ids, env) -> Flux.empty()));
MethodParameter parameter = initParameter(1);
parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
@@ -90,9 +88,7 @@ public class DataLoaderArgumentResolverTests {
@Test
void resolveArgumentFailureWithoutParameterName() {
DataFetchingEnvironment environment = initEnvironment(registry ->
registry.forTypePair(Long.class, Author.class)
.withName("namedDataLoader")
.registerBatchLoader((ids, env) -> Flux.empty()));
registry.forName("namedDataLoader").registerBatchLoader((ids, env) -> Flux.empty()));
MethodParameter parameter = initParameter(1);
// Skip ParameterNameDiscovery
@@ -104,9 +100,7 @@ public class DataLoaderArgumentResolverTests {
@Test
void resolveArgumentFailureNoMatch() {
DataFetchingEnvironment environment = initEnvironment(registry ->
registry.forTypePair(Long.class, Book.class)
.withName("bookDataLoader")
.registerBatchLoader((ids, env) -> Flux.empty()));
registry.forName("bookDataLoader").registerBatchLoader((ids, env) -> Flux.empty()));
MethodParameter parameter = initParameter(0);
parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

View File

@@ -65,9 +65,7 @@ public class DefaultBatchLoaderRegistryTests {
String name = "myLoader";
StatisticsCollector collector = new NoOpStatisticsCollector();
this.batchLoaderRegistry
.forTypePair(String.class, Book.class)
.withName(name)
this.batchLoaderRegistry.forName(name)
.withOptions(options -> options.setStatisticsCollector(() -> collector))
.registerBatchLoader((keys, environment) -> Flux.empty());