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 08:46:02 UTC

[sling-org-apache-sling-graphql-core] branch SLING-10309/experiment updated: SLING-10309 - rename a few things and add GenericConnectionTest

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 b9aaf40  SLING-10309 - rename a few things and add GenericConnectionTest
b9aaf40 is described below

commit b9aaf40b34d04e3ab7838b38963551f3f725f8cb
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Wed May 12 10:45:19 2021 +0200

    SLING-10309 - rename a few things and add GenericConnectionTest
---
 .../sling/graphql/api/pagination/Cursor.java       |  14 +++
 .../api/pagination/helpers/GenericConnection.java  |  99 ++++++++++----------
 .../sling/graphql/core/engine/CursorTest.java      |  14 +++
 .../core/pagination/GenericConnectionTest.java     | 104 +++++++++++++++++++++
 .../PaginatedHumansTest.java                       |   6 +-
 src/test/resources/paginated-humans-schema.txt     |   8 +-
 6 files changed, 189 insertions(+), 56 deletions(-)

diff --git a/src/main/java/org/apache/sling/graphql/api/pagination/Cursor.java b/src/main/java/org/apache/sling/graphql/api/pagination/Cursor.java
index bc391e4..ae455a8 100644
--- a/src/main/java/org/apache/sling/graphql/api/pagination/Cursor.java
+++ b/src/main/java/org/apache/sling/graphql/api/pagination/Cursor.java
@@ -39,6 +39,20 @@ public class Cursor {
         this.encoded = encode(this.rawValue);
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if(!(obj instanceof Cursor)) {
+            return false;
+        }
+        final Cursor other = (Cursor)obj;
+        return other.rawValue.equals(this.rawValue);
+    }
+
+    @Override
+    public int hashCode() {
+        return rawValue == null ? null : rawValue.hashCode();
+    }
+
     public static Cursor fromEncodedString(@Nullable String encoded) {
         if(encoded == null) {
             return null;
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 06527b3..2d54150 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
@@ -34,52 +34,13 @@ import org.osgi.annotation.versioning.ConsumerType;
  *  is a page of results for a paginated query.
 */
 @ConsumerType
-public class GenericConnection<T> implements Connection<T> {
+public class GenericConnection<T> implements Connection<T>, PageInfo {
 
     private final List<Edge<T>> edges;
-
-    static class LocalPageInfo implements PageInfo {
-        Cursor startCursor = null;
-        Cursor endCursor = null;
-        boolean hasPreviousPage;
-        boolean hasNextPage;
-
-        @Override
-        public Cursor getStartCursor() {
-            return startCursor;
-        }
-
-        @Override
-        public Cursor getEndCursor() {
-            return endCursor;
-        }
-
-        @Override
-        public boolean isHasPreviousPage() {
-            return hasPreviousPage;
-        }
-
-        @Override
-        public boolean isHasNextPage() {
-            return hasNextPage;
-        }
-    };
-
-    private final LocalPageInfo pageInfo = new LocalPageInfo();
-
-    private Edge<T> newEdge(final T node, final Function<T, String> cursorStringFunction) {
-        return new Edge<T>() {
-            @Override
-            public T getNode() {
-                return node;
-            }
-
-            @Override
-            public Cursor getCursor() {
-                return new Cursor(cursorStringFunction.apply(node));
-            }
-        };
-    }
+    private Cursor startCursor = null;
+    private Cursor endCursor = null;
+    private boolean hasPreviousPage;
+    private boolean hasNextPage;
 
     /** Build a Connection that will output the supplied data, optionally skipping items
      *  at the beginning and considering a set maximum of items.
@@ -103,11 +64,11 @@ public class GenericConnection<T> implements Connection<T> {
                 if(startAfter == null) {
                     inRange = true;
                     addThisNode = true;
-                    pageInfo.hasPreviousPage = false;
+                    hasPreviousPage = false;
                 } else {
                     final String rawCursor = cursorStringFunction.apply(node);
                     inRange = startAfter.getRawValue().equals(rawCursor);
-                    pageInfo.hasPreviousPage = true;
+                    hasPreviousPage = true;
                 }
             } else {
                 addThisNode = true;
@@ -115,10 +76,10 @@ public class GenericConnection<T> implements Connection<T> {
 
             if(addThisNode) {
                 final Edge<T> toAdd = newEdge(node, cursorStringFunction);
-                if(pageInfo.startCursor == null) {
-                    pageInfo.startCursor = toAdd.getCursor();
+                if(startCursor == null) {
+                    startCursor = toAdd.getCursor();
                 }
-                pageInfo.endCursor = toAdd.getCursor();
+                endCursor = toAdd.getCursor();
                 edges.add(toAdd);
                 itemsToAdd--;
             }
@@ -127,14 +88,50 @@ public class GenericConnection<T> implements Connection<T> {
         if(!inRange && maxItemsReturned > 0) {
             throw new RuntimeException("Start cursor not found in supplied data:" + startAfter);
         }
-        pageInfo.hasNextPage = dataIterator.hasNext();
+        hasNextPage = dataIterator.hasNext();
     }
 
+    private Edge<T> newEdge(final T node, final Function<T, String> cursorStringFunction) {
+        return new Edge<T>() {
+            @Override
+            public T getNode() {
+                return node;
+            }
+
+            @Override
+            public Cursor getCursor() {
+                return new Cursor(cursorStringFunction.apply(node));
+            }
+        };
+    }
+
+    @Override
     public Iterable<Edge<T>> getEdges() {
         return edges::iterator;
     }
 
+    @Override
     public PageInfo getPageInfo() {
-        return pageInfo;
+        return this;
+    }
+
+    @Override
+    public Cursor getStartCursor() {
+        return startCursor;
+    }
+
+    @Override
+    public Cursor getEndCursor() {
+        return endCursor;
+    }
+
+    @Override
+    public boolean isHasPreviousPage() {
+        return hasPreviousPage;
+    }
+
+    @Override
+    public boolean isHasNextPage() {
+        return hasNextPage;
     }
 }
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/CursorTest.java b/src/test/java/org/apache/sling/graphql/core/engine/CursorTest.java
index 7f83d1e..72bd83e 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/CursorTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/engine/CursorTest.java
@@ -78,4 +78,18 @@ public class CursorTest {
         assertNull(Cursor.fromEncodedString(null));
         assertNull(Cursor.fromEncodedString("\t\n  "));
     }
+
+    @Test
+    public void testEquals() {
+        final String key = UUID.randomUUID().toString();
+        assertEquals(new Cursor(key), new Cursor(key));
+        assertNotEquals(new Cursor(key), new Cursor("something else"));
+    }
+
+    @Test
+    public void testHashCode() {
+        final String key = UUID.randomUUID().toString();
+        final Cursor c = new Cursor(key);
+        assertEquals(key.hashCode(), c.hashCode());
+    }
 }
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
new file mode 100644
index 0000000..a152c93
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/core/pagination/GenericConnectionTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.graphql.core.pagination;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.stream.StreamSupport;
+
+import org.apache.sling.graphql.api.pagination.Connection;
+import org.apache.sling.graphql.api.pagination.Cursor;
+import org.apache.sling.graphql.api.pagination.Edge;
+import org.apache.sling.graphql.api.pagination.helpers.GenericConnection;
+import org.junit.Test;
+
+public class GenericConnectionTest {
+    private static final List<Integer> data = Arrays.asList(1,2,3,4,5 );
+    private static final Function<Integer, String> cursorize = (i) -> "cursor-" + i;
+
+    private static void assertValues(Connection<Integer> data, int start, int end, boolean hasPreviousPage, boolean hasNextPage)  {
+        // assert edge values
+        final AtomicInteger current = new AtomicInteger(start);
+        StreamSupport.stream(data.getEdges().spliterator(), false).map(edge -> edge.getNode()).forEach(actual -> {
+            assertEquals(Integer.valueOf(current.get()), actual);
+            if(actual > end) {
+                fail("Got a value after expected end: " + actual);
+            }
+            current.incrementAndGet();
+        });
+
+        // cursors and previous/next page
+        final Iterator<Edge<Integer>> it = data.getEdges().iterator();
+        if(it.hasNext()) {
+            final Cursor startCursor = new Cursor(cursorize.apply(it.next().getNode()));
+            assertEquals("Expecting start cursor " + startCursor, startCursor, data.getPageInfo().getStartCursor());
+            final Cursor endCursor = new Cursor(cursorize.apply(end));
+            assertEquals("Expecting end cursor " + endCursor, endCursor, data.getPageInfo().getEndCursor());
+        } else {
+            // Empty data stream
+            assertEquals(null, data.getPageInfo().getStartCursor());
+            assertEquals(null, data.getPageInfo().getEndCursor());
+        }
+        assertEquals("Expecting hasNextPage=" + hasNextPage, hasNextPage, data.getPageInfo().isHasNextPage());
+        assertEquals("Expecting hasPreviousPage=" + hasPreviousPage, hasPreviousPage, data.getPageInfo().isHasPreviousPage());
+    }
+
+    @Test
+    public void minimalArguments() {
+        final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 2);
+        assertValues(c, 1, 2, false, true);
+    }
+
+    @Test
+    public void zeroLimit() {
+        final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 0);
+        assertValues(c, -1, -1, false, true);
+    }
+
+    @Test
+    public void largeLimit() {
+        final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, null, 999);
+        assertValues(c, 1, 5, false, false);
+    }
+
+    @Test
+    public void startAtThree() {
+        final Cursor startAfter = new Cursor(cursorize.apply(2));
+        final GenericConnection<Integer> c = new GenericConnection<>(data.iterator(), cursorize, startAfter, 2);
+        assertValues(c, 3, 4, true, true);
+    }
+
+    @Test
+    public void startCursorNotFound() {
+        final Cursor startAfter = new Cursor(cursorize.apply(999));
+        try {
+            new GenericConnection<>(data.iterator(), cursorize, startAfter, 2);
+            fail("Expecting a RuntimeException");
+        } catch(RuntimeException rex) {
+            assertTrue(rex.getMessage().contains("Start cursor not found"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java b/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
similarity index 97%
rename from src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java
rename to src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
index 6e4dd3d..04b6550 100644
--- a/src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java
+++ b/src/test/java/org/apache/sling/graphql/core/pagination/PaginatedHumansTest.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.graphql.core.engine;
+package org.apache.sling.graphql.core.pagination;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -30,6 +30,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import org.apache.sling.graphql.core.mocks.TestUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.apache.sling.graphql.core.engine.ResourceQueryTestBase;
 import org.apache.sling.graphql.core.mocks.HumanDTO;
 import org.apache.sling.graphql.api.SlingDataFetcher;
 import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
@@ -62,11 +63,10 @@ public class PaginatedHumansTest extends ResourceQueryTestBase {
     @Override
     protected void setupAdditionalServices() {
         final List<HumanDTO> humans = new ArrayList<>();
-
         for(int i=1 ; i < 100 ; i++) {
             humans.add(new HumanDTO("human-" + i, "Luke-" + i, "Tatooine"));
         }
-        TestUtil.registerSlingDataFetcher(context.bundleContext(), "humans/paginated", new HumansPageFetcher(humans));
+        TestUtil.registerSlingDataFetcher(context.bundleContext(), "humans/connection", new HumansPageFetcher(humans));
     }
 
     private void assertPageInfo(String json, Cursor startCursor, Cursor endCursor, Boolean hasPreviousPage, Boolean hasNextPage) {
diff --git a/src/test/resources/paginated-humans-schema.txt b/src/test/resources/paginated-humans-schema.txt
index b222cd6..62c1e40 100644
--- a/src/test/resources/paginated-humans-schema.txt
+++ b/src/test/resources/paginated-humans-schema.txt
@@ -23,7 +23,7 @@ directive @fetcher(
 ) on FIELD_DEFINITION
 
 type Query {
-    paginatedHumans (after : String, limit : Int) : PaginatedHumans @fetcher(name:"humans/paginated")
+    paginatedHumans (after : String, limit : Int) : HumanConnection @fetcher(name:"humans/connection")
 }
 
 type Human {
@@ -32,6 +32,10 @@ type Human {
   address: String
 }
 
+# The connection-specific parts of the schema might be
+# generated based on a schema directive later, but
+# writing them "by hand" also works
+
 type PageInfo {
     startCursor : String
     endCursor : String
@@ -44,7 +48,7 @@ type HumanEdge {
     node: Human
 }
 
-type PaginatedHumans {
+type HumanConnection {
     edges : [HumanEdge]
     pageInfo : PageInfo
 }
\ No newline at end of file