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();
         }
     }