You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2021/05/12 09:13:37 UTC
[sling-org-apache-sling-graphql-core] branch SLING-10309/experiment
updated: SLING-10309 - fluent GenericConnection,
makes easier to add more options later.
This is an automated email from the ASF dual-hosted git repository.
bdelacretaz pushed a commit to branch SLING-10309/experiment
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git
The following commit(s) were added to refs/heads/SLING-10309/experiment by this push:
new e555bf6 SLING-10309 - fluent GenericConnection, makes easier to add more options later.
e555bf6 is described below
commit e555bf68d25b105e41f2bb1091c31ba2e71f4386
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Wed May 12 11:12:50 2021 +0200
SLING-10309 - fluent GenericConnection, makes easier to add more options later.
---
.../api/pagination/helpers/GenericConnection.java | 92 ++++++++++++++++++----
.../core/pagination/GenericConnectionTest.java | 60 ++++++++++++--
.../core/pagination/PaginatedHumansTest.java | 5 +-
3 files changed, 135 insertions(+), 22 deletions(-)
diff --git a/src/main/java/org/apache/sling/graphql/api/pagination/helpers/GenericConnection.java b/src/main/java/org/apache/sling/graphql/api/pagination/helpers/GenericConnection.java
index 2d54150..1ebfabf 100644
--- a/src/main/java/org/apache/sling/graphql/api/pagination/helpers/GenericConnection.java
+++ b/src/main/java/org/apache/sling/graphql/api/pagination/helpers/GenericConnection.java
@@ -36,27 +36,43 @@ import org.osgi.annotation.versioning.ConsumerType;
@ConsumerType
public class GenericConnection<T> implements Connection<T>, PageInfo {
+ public static final int DEFAULT_LIMIT = 10;
+
private final List<Edge<T>> edges;
+ private final Iterator<T> dataIterator;
+ private final Function<T, String> cursorStringProvider;
+ private boolean initialized;
+ private Cursor startAfter = null;
private Cursor startCursor = null;
private Cursor endCursor = null;
- private boolean hasPreviousPage;
- private boolean hasNextPage;
+ private Boolean hasPreviousPage;
+ private Boolean hasNextPage;
+ private int limit = DEFAULT_LIMIT;
/** Build a Connection that will output the supplied data, optionally skipping items
* at the beginning and considering a set maximum of items.
*
* @param dataIterator the connection's data - must include the item that startAfter points to
- * @param cursorStringFunction extracts a String from an object of type T to create a Cursor
+ * @param cursorStringProvider extracts a String from an object of type T to create a Cursor
* @param startAfter if not null, data up to and including the item which has this cursor is ignored
* @param maxItemsReturned at most this many items are considered
*/
- public GenericConnection(Iterator<T> dataIterator, Function<T, String> cursorStringFunction, final Cursor startAfter, int maxItemsReturned) {
+ private GenericConnection(Iterator<T> dataIterator, Function<T, String> cursorStringProvider) {
+ edges = new ArrayList<>();
+ this.dataIterator = dataIterator;
+ this.cursorStringProvider = cursorStringProvider;
+ }
+
+ private void initialize() {
+ if(initialized) {
+ throw new IllegalStateException("Already initialized");
+ }
+ initialized = true;
// Need to visit the stream first to setup the PageInfo, which graphql-java
// apparently uses before visiting all the edges
- edges = new ArrayList<>();
boolean inRange = false;
- int itemsToAdd = maxItemsReturned;
+ int itemsToAdd = limit;
while(itemsToAdd > 0 && dataIterator.hasNext()) {
final T node = dataIterator.next();
boolean addThisNode = false;
@@ -64,18 +80,22 @@ public class GenericConnection<T> implements Connection<T>, PageInfo {
if(startAfter == null) {
inRange = true;
addThisNode = true;
- hasPreviousPage = false;
+ if(hasPreviousPage == null) {
+ hasPreviousPage = false;
+ }
} else {
- final String rawCursor = cursorStringFunction.apply(node);
+ final String rawCursor = cursorStringProvider.apply(node);
inRange = startAfter.getRawValue().equals(rawCursor);
- hasPreviousPage = true;
+ if(hasPreviousPage == null) {
+ hasPreviousPage = true;
+ }
}
} else {
addThisNode = true;
}
if(addThisNode) {
- final Edge<T> toAdd = newEdge(node, cursorStringFunction);
+ final Edge<T> toAdd = newEdge(node, cursorStringProvider);
if(startCursor == null) {
startCursor = toAdd.getCursor();
}
@@ -85,13 +105,18 @@ public class GenericConnection<T> implements Connection<T>, PageInfo {
}
}
- if(!inRange && maxItemsReturned > 0) {
+ if(!inRange && limit > 0) {
throw new RuntimeException("Start cursor not found in supplied data:" + startAfter);
}
- hasNextPage = dataIterator.hasNext();
+ if(hasPreviousPage == null) {
+ hasPreviousPage = false;
+ }
+ if(hasNextPage == null) {
+ hasNextPage = dataIterator.hasNext();
+ }
}
- private Edge<T> newEdge(final T node, final Function<T, String> cursorStringFunction) {
+ private Edge<T> newEdge(final T node, final Function<T, String> cursorStringProvider) {
return new Edge<T>() {
@Override
public T getNode() {
@@ -100,7 +125,7 @@ public class GenericConnection<T> implements Connection<T>, PageInfo {
@Override
public Cursor getCursor() {
- return new Cursor(cursorStringFunction.apply(node));
+ return new Cursor(cursorStringProvider.apply(node));
}
};
}
@@ -134,4 +159,43 @@ public class GenericConnection<T> implements Connection<T>, PageInfo {
public boolean isHasNextPage() {
return hasNextPage;
}
+
+ public static class Builder<T> {
+ private final GenericConnection<T> connection;
+
+ public Builder(Iterator<T> dataIterator, Function<T, String> cursorStringProvider) {
+ connection = new GenericConnection<>(dataIterator, cursorStringProvider);
+ }
+
+ public Builder<T> withLimit(int limit) {
+ connection.limit = limit;
+ return this;
+ }
+
+ public Builder<T> withStartAfter(Cursor c) {
+ connection.startAfter = c;
+ return this;
+ }
+
+ /** Force the "has previous page" value, in case the supplied
+ * data doesn't expose that but a new query would find it
+ */
+ public Builder<T> withPreviousPage(boolean b) {
+ connection.hasPreviousPage = b;
+ return this;
+ }
+
+ /** Force the "has next page" value, in case the supplied
+ * data doesn't expose that but a new query would find it
+ */
+ public Builder<T> withNextPage(boolean b) {
+ connection.hasNextPage = b;
+ return this;
+ }
+
+ public Connection<T> build() {
+ connection.initialize();
+ return connection;
+ }
+ }
}
diff --git a/src/test/java/org/apache/sling/graphql/core/pagination/GenericConnectionTest.java b/src/test/java/org/apache/sling/graphql/core/pagination/GenericConnectionTest.java
index a152c93..ce008ec 100644
--- a/src/test/java/org/apache/sling/graphql/core/pagination/GenericConnectionTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/pagination/GenericConnectionTest.java
@@ -68,34 +68,80 @@ public class GenericConnectionTest {
@Test
public void minimalArguments() {
- final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 2);
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(2)
+ .build();
assertValues(c, 1, 2, false, true);
}
@Test
public void zeroLimit() {
- final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 0);
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(0)
+ .build();
assertValues(c, -1, -1, false, true);
}
@Test
public void largeLimit() {
- final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 999);
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(999)
+ .build();
assertValues(c, 1, 5, false, false);
}
@Test
+ public void forcePreviousPage() {
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(999)
+ .withPreviousPage(true)
+ .build();
+ assertValues(c, 1, 5, true, false);
+ }
+
+ @Test
+ public void forceNextPage() {
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(999)
+ .withNextPage(true)
+ .build();
+ assertValues(c, 1, 5, false, true);
+ }
+
+ @Test
public void startAtThree() {
- final Cursor startAfter = new Cursor(cursorize.apply(2));
- final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, startAfter, 2);
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(2)
+ .withStartAfter(new Cursor(cursorize.apply(2)))
+ .build();
assertValues(c, 3, 4, true, true);
}
@Test
+ public void startAtFourLargeLimit() {
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(999)
+ .withStartAfter(new Cursor(cursorize.apply(3)))
+ .build();
+ assertValues(c, 4, 5, true, false);
+ }
+
+ @Test
+ public void justTwo() {
+ final Connection<Integer> c = new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(1)
+ .withStartAfter(new Cursor(cursorize.apply(1)))
+ .build();
+ assertValues(c, 2, 2, true, true);
+ }
+
+ @Test
public void startCursorNotFound() {
- final Cursor startAfter = new Cursor(cursorize.apply(999));
try {
- new GenericConnection<>(data.iterator(), cursorize, startAfter, 2);
+ new GenericConnection.Builder<>(data.iterator(), cursorize)
+ .withLimit(2)
+ .withStartAfter(new Cursor(cursorize.apply(999)))
+ .build();
fail("Expecting a RuntimeException");
} catch(RuntimeException rex) {
assertTrue(rex.getMessage().contains("Start cursor not found"));
diff --git a/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java b/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
index 04b6550..b6a3c6f 100644
--- a/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
@@ -56,7 +56,10 @@ public class PaginatedHumansTest extends ResourceQueryTestBase {
public @Nullable Connection<HumanDTO> get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
final Cursor afterCursor = Cursor.fromEncodedString(e.getArgument("after"));
final int limit = e.getArgument("limit", 2);
- return new GenericConnection<>(humans.iterator(), HumanDTO::getId, afterCursor, limit);
+ return new GenericConnection.Builder<>(humans.iterator(), HumanDTO::getId)
+ .withStartAfter(afterCursor)
+ .withLimit(limit)
+ .build();
}
}