Add strict mode for DataLoaderRegistry by AlexandreCarlton · Pull Request #252 · graphql-java/java-dataloader · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions src/main/java/org/dataloader/DataLoaderRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.dataloader.errors;

import org.dataloader.annotations.PublicApi;

/**
* An exception that is thrown when {@link org.dataloader.DataLoaderRegistry.Builder#strictMode(boolean)} is true and multiple
* DataLoaders are registered to the same key.
*/
@PublicApi
public class StrictModeRegistryException extends RuntimeException {
public StrictModeRegistryException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;
import org.dataloader.annotations.ExperimentalApi;
import org.dataloader.errors.StrictModeRegistryException;
import org.dataloader.impl.Assertions;
import org.dataloader.instrumentation.DataLoaderInstrumentation;
import org.jspecify.annotations.NullMarked;
Expand All @@ -16,6 +17,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static java.lang.String.format;
import static org.dataloader.impl.Assertions.nonNull;

/**
Expand Down Expand Up @@ -69,7 +71,7 @@ public class ScheduledDataLoaderRegistry extends DataLoaderRegistry implements A
private volatile boolean closed;

private ScheduledDataLoaderRegistry(Builder builder) {
super(builder.dataLoaders, builder.instrumentation);
super(builder.dataLoaders, builder.instrumentation, builder.strictMode);
this.scheduledExecutorService = Assertions.nonNull(builder.scheduledExecutorService);
this.defaultExecutorUsed = builder.defaultExecutorUsed;
this.schedule = builder.schedule;
Expand Down Expand Up @@ -120,7 +122,8 @@ public boolean isTickerMode() {
*/
public ScheduledDataLoaderRegistry combine(DataLoaderRegistry registry) {
Builder combinedBuilder = ScheduledDataLoaderRegistry.newScheduledRegistry()
.dispatchPredicate(this.dispatchPredicate);
.dispatchPredicate(this.dispatchPredicate)
.strictMode(this.strictMode);
combinedBuilder.registerAll(this);
combinedBuilder.registerAll(registry);
return combinedBuilder.build();
Expand Down Expand Up @@ -166,6 +169,9 @@ public DispatchPredicate getDispatchPredicate() {
* @return this registry
*/
public ScheduledDataLoaderRegistry register(String key, DataLoader<?, ?> dataLoader, DispatchPredicate dispatchPredicate) {
if (strictMode) {
assertKeyStrictly(key);
}
dataLoaders.put(key, dataLoader);
dataLoaderPredicates.put(dataLoader, dispatchPredicate);
return this;
Expand Down Expand Up @@ -272,6 +278,7 @@ public static class Builder {
private Duration schedule = Duration.ofMillis(10);
private boolean tickerMode = false;
private @Nullable DataLoaderInstrumentation instrumentation;
private boolean strictMode;


/**
Expand All @@ -291,6 +298,18 @@ public Builder schedule(Duration schedule) {
return this;
}

/**
* This puts the builder into strict mode, so if things get defined twice, for example, it
* will throw a {@link org.dataloader.errors.StrictModeRegistryException}.
*
* @param strictMode whether strict mode is enabled
* @return this builder
*/
public Builder strictMode(boolean strictMode) {
this.strictMode = strictMode;
return this;
}

/**
* This will register a new dataloader
*
Expand All @@ -299,6 +318,9 @@ public Builder schedule(Duration schedule) {
* @return this builder for a fluent pattern
*/
public Builder register(String key, DataLoader<?, ?> dataLoader) {
if (strictMode) {
assertKeyStrictly(key);
}
dataLoaders.put(key, dataLoader);
return this;
}
Expand Down Expand Up @@ -326,7 +348,13 @@ public Builder register(String key, DataLoader<?, ?> dataLoader, DispatchPredica
* @return this builder for a fluent pattern
*/
public Builder registerAll(DataLoaderRegistry otherRegistry) {
dataLoaders.putAll(otherRegistry.getDataLoadersMap());
Map<String, DataLoader<?, ?>> otherDataLoaders = otherRegistry.getDataLoadersMap();
if (strictMode) {
otherDataLoaders.forEach((key, dataLoader) -> {
assertKeyStrictly(key);
});
}
dataLoaders.putAll(otherDataLoaders);
if (otherRegistry instanceof ScheduledDataLoaderRegistry) {
ScheduledDataLoaderRegistry other = (ScheduledDataLoaderRegistry) otherRegistry;
dataLoaderPredicates.putAll(other.dataLoaderPredicates);
Expand Down Expand Up @@ -364,6 +392,12 @@ public Builder instrumentation(DataLoaderInstrumentation instrumentation) {
return this;
}

private void assertKeyStrictly(String key) {
if (dataLoaders.containsKey(key)) {
throw new StrictModeRegistryException(format("The key %s already has a DataLoader defined", key));
}
}

/**
* @return the newly built {@link ScheduledDataLoaderRegistry}
*/
Expand Down
46 changes: 46 additions & 0 deletions src/test/java/org/dataloader/DataLoaderRegistryTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.dataloader;

import org.dataloader.errors.StrictModeRegistryException;
import org.dataloader.stats.SimpleStatisticsCollector;
import org.dataloader.stats.Statistics;
import org.junit.jupiter.api.Assertions;
Expand All @@ -14,6 +15,7 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class DataLoaderRegistryTest {
final BatchLoader<Object, Object> identityBatchLoader = CompletableFuture::completedFuture;
Expand Down Expand Up @@ -219,4 +221,48 @@ public void builder_works() {
assertThat(registry.getDataLoader("c"), equalTo(dlC));

}

@Test
public void strictMode_works() {

DataLoader<Object, Object> dlA = newDataLoader(identityBatchLoader);
DataLoader<Object, Object> dlB = newDataLoader(identityBatchLoader);

assertThrows(StrictModeRegistryException.class, () -> {
DataLoaderRegistry.newRegistry()
.strictMode(true)
.register("a", dlA)
.register("a", dlB)
.build();
});
assertThrows(StrictModeRegistryException.class, () -> {
DataLoaderRegistry.newRegistry()
.strictMode(true)
.register("a", dlA)
.registerAll(DataLoaderRegistry.newRegistry()
.register("a", dlB)
.build())
.build();
});

DataLoaderRegistry registry = DataLoaderRegistry.newRegistry()
.strictMode(true)
.build();
registry.register("a", dlA);

assertThrows(StrictModeRegistryException.class, () -> {
registry.register("a", dlB);
});
assertThrows(StrictModeRegistryException.class, () -> {
registry.register(newDataLoader("a", identityBatchLoader));
});
assertThrows(StrictModeRegistryException.class, () -> {
registry.registerAndGet("a", dlB);
});
assertThrows(StrictModeRegistryException.class, () -> {
registry.combine(DataLoaderRegistry.newRegistry()
.register("a", dlB)
.build());
});
}
}