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/11 16:44:22 UTC
[sling-org-apache-sling-graphql-core] branch SLING-10309/experiment
updated: SLING-10309 - move pagination logic to GenericConnection
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 01bb8e1 SLING-10309 - move pagination logic to GenericConnection
01bb8e1 is described below
commit 01bb8e1bb1f49ab6bf11fe5164f893caa24d9fb4
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Tue May 11 18:43:55 2021 +0200
SLING-10309 - move pagination logic to GenericConnection
---
.../{ResultsPage.java => Connection.java} | 12 +-
.../sling/graphql/api/pagination/Cursor.java | 16 ++
.../api/pagination/helpers/GenericConnection.java | 140 ++++++++++++++
.../sling/graphql/core/engine/CursorTest.java | 15 ++
.../graphql/core/engine/PaginatedHumansTest.java | 136 ++++++++++++++
.../sling/graphql/core/engine/PaginationTest.java | 201 ---------------------
src/test/resources/paginated-humans-schema.txt | 50 +++++
src/test/resources/test-schema.txt | 19 --
8 files changed, 362 insertions(+), 227 deletions(-)
diff --git a/src/main/java/org/apache/sling/graphql/api/pagination/ResultsPage.java b/src/main/java/org/apache/sling/graphql/api/pagination/Connection.java
similarity index 75%
rename from src/main/java/org/apache/sling/graphql/api/pagination/ResultsPage.java
rename to src/main/java/org/apache/sling/graphql/api/pagination/Connection.java
index db8a433..b2c2998 100644
--- a/src/main/java/org/apache/sling/graphql/api/pagination/ResultsPage.java
+++ b/src/main/java/org/apache/sling/graphql/api/pagination/Connection.java
@@ -17,17 +17,15 @@
* under the License.
*/
- package org.apache.sling.graphql.api.pagination;
-
-import java.util.List;
+package org.apache.sling.graphql.api.pagination;
import org.osgi.annotation.versioning.ConsumerType;
-/** This is what https://relay.dev/graphql/connections.htm calls a "connection".
- * It actually represents a page of results, so why not call it that?
+/** As per https://relay.dev/graphql/connections.htm a "connection"
+ * is a page of results for a paginated query.
*/
@ConsumerType
-public interface ResultsPage<T> {
- List<Edge<T>> getEdges();
+public interface Connection<T> {
+ Iterable<Edge<T>> getEdges();
PageInfo getPageInfo();
}
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 c4509da..24311cb 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
@@ -22,6 +22,7 @@
import java.util.Base64;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.osgi.annotation.versioning.ProviderType;
/** Cursor for our paginated results.
@@ -33,11 +34,26 @@ public class Cursor {
private final String rawValue;
private final String encoded;
+ /** These constants can be used to identify uninitialized cursors */
+ public static final String INVALID_CURSOR_KEY = "$INVALID$CURSOR$" + Cursor.class.getName();
+ public static final String ENCODED_INVALID_CURSOR_KEY = Cursor.encode(INVALID_CURSOR_KEY);
+
public Cursor(String rawValue) {
this.rawValue = rawValue == null ? "" : rawValue;
this.encoded = encode(this.rawValue);
}
+ public static Cursor fromEncodedString(@Nullable String encoded) {
+ if(encoded == null) {
+ return null;
+ }
+ encoded = encoded.trim();
+ if(encoded.length() == 0) {
+ return null;
+ }
+ return new Cursor(decode(encoded));
+ }
+
public boolean isEmpty() {
return rawValue == null || rawValue.length() == 0;
}
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
new file mode 100644
index 0000000..8cd8aa0
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/api/pagination/helpers/GenericConnection.java
@@ -0,0 +1,140 @@
+/*
+ * 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.api.pagination.helpers;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+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.PageInfo;
+import org.osgi.annotation.versioning.ConsumerType;
+
+/** As per https://relay.dev/graphql/connections.htm a "connection"
+ * is a page of results for a paginated query.
+*/
+@ConsumerType
+public class GenericConnection<T> implements Connection<T> {
+
+ 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));
+ }
+ };
+ }
+
+ /** 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 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) {
+
+ // 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;
+ while(itemsToAdd > 0 && dataIterator.hasNext()) {
+ final T node = dataIterator.next();
+ boolean addThisNode = false;
+ if(!inRange) {
+ if(startAfter == null) {
+ inRange = true;
+ addThisNode = true;
+ pageInfo.hasPreviousPage = false;
+ } else {
+ final String rawCursor = cursorStringFunction.apply(node);
+ inRange = startAfter.getRawValue().equals(rawCursor);
+ pageInfo.hasPreviousPage = true;
+ }
+ } else {
+ addThisNode = true;
+ }
+
+ if(addThisNode) {
+ final Edge<T> toAdd = newEdge(node, cursorStringFunction);
+ if(pageInfo.startCursor == null) {
+ pageInfo.startCursor = toAdd.getCursor();
+ }
+ pageInfo.endCursor = toAdd.getCursor();
+ edges.add(toAdd);
+ itemsToAdd--;
+ }
+ }
+
+ if(!inRange) {
+ throw new RuntimeException("Start cursor not found in supplied data:" + startAfter);
+ }
+ pageInfo.hasNextPage = dataIterator.hasNext();
+ }
+
+ public Iterable<Edge<T>> getEdges() {
+ return edges::iterator;
+ }
+
+ public PageInfo getPageInfo() {
+ return pageInfo;
+ }
+}
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 db62524..7f83d1e 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
@@ -21,6 +21,7 @@ package org.apache.sling.graphql.core.engine;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
import java.util.UUID;
@@ -63,4 +64,18 @@ public class CursorTest {
public void testEmptyValue() {
assertWithValue("", true);
}
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testInvalidValueDecoding() {
+ Cursor.decode("This is not base64");
+ }
+
+ @Test
+ public void testfromEncodedString() {
+ final String key = UUID.randomUUID().toString();
+ final String encoded = Cursor.encode(key);
+ assertEquals(encoded, Cursor.fromEncodedString(" " + encoded + " ").toString());
+ assertNull(Cursor.fromEncodedString(null));
+ assertNull(Cursor.fromEncodedString("\t\n "));
+ }
}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java b/src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java
new file mode 100644
index 0000000..e3a134c
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/core/engine/PaginatedHumansTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.engine;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertTrue;
+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.mocks.HumanDTO;
+import org.apache.sling.graphql.api.SlingDataFetcher;
+import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
+import org.apache.sling.graphql.api.pagination.Connection;
+import org.apache.sling.graphql.api.pagination.Cursor;
+import org.apache.sling.graphql.api.pagination.helpers.GenericConnection;
+import org.junit.Test;
+
+public class PaginatedHumansTest extends ResourceQueryTestBase {
+
+ protected String getTestSchemaName() {
+ return "paginated-humans-schema";
+ }
+
+ static class HumansPageFetcher implements SlingDataFetcher<Connection<HumanDTO>> {
+ private List<HumanDTO> humans = new ArrayList<>();
+
+ HumansPageFetcher(List<HumanDTO> humans) {
+ this.humans = humans;
+ }
+
+ @Override
+ 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);
+ }
+ }
+
+ @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));
+ }
+
+ private void assertPageInfo(String json, Cursor startCursor, Cursor endCursor, Boolean hasPreviousPage, Boolean hasNextPage) {
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.pageInfo.startCursor", equalTo(startCursor.toString())));
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.pageInfo.endCursor", equalTo(endCursor.toString())));
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.pageInfo.hasPreviousPage", equalTo(hasPreviousPage)));
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.pageInfo.hasNextPage", equalTo(hasNextPage)));
+ }
+
+ private void assertEdges(String json, int startIndex, int endIndex) {
+ int dataIndex = 0;
+ for(int i=startIndex; i <= endIndex; i++) {
+ final String id = "human-" + i;
+ final Cursor c = new Cursor(id);
+ final String name = "Luke-" + i;
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.edges[" + dataIndex + "].node.id", equalTo(id)));
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.edges[" + dataIndex + "].node.name", equalTo(name)));
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.edges[" + dataIndex + "].cursor", equalTo(c.toString())));
+ dataIndex++;
+ }
+ final int count = endIndex - startIndex + 1;
+ assertThat(json, hasJsonPath("$.data.paginatedHumans.edges.length()", equalTo(count)));
+ }
+
+ @Test
+ public void noArguments() throws Exception {
+ final String json = queryJSON("{ paginatedHumans {"
+ + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
+ + " edges { cursor node { id name }}"
+ +"}}");
+ assertEdges(json, 1, 2);
+ assertPageInfo(json, new Cursor("human-1"), new Cursor("human-2"), false, true );
+ }
+
+ @Test
+ public void startCursorAndLimit() throws Exception {
+ final Cursor start = new Cursor("human-5");
+ final String json = queryJSON("{ paginatedHumans(after:\"" + start + "\", limit:6) {"
+ + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
+ + " edges { cursor node { id name }}"
+ +"}}");
+ assertEdges(json, 6, 11);
+ assertPageInfo(json, new Cursor("human-6"), new Cursor("human-11"), true, true);
+ }
+
+ @Test
+ public void startCursorNearEnd() throws Exception {
+ final Cursor start = new Cursor("human-94");
+ final String json = queryJSON("{ paginatedHumans(after:\"" + start + "\", limit:60) {"
+ + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
+ + " edges { cursor node { id name }}"
+ +"}}");
+ assertEdges(json, 95, 99);
+ assertPageInfo(json, new Cursor("human-95"), new Cursor("human-99"), true, false);
+ }
+
+ @Test
+ public void afterCursorNotFound() throws Exception {
+ final Cursor notInDataSet = new Cursor("This is not a key from our data set");
+ final String json = queryJSON("{ paginatedHumans(after:\"" + notInDataSet + "\", limit:60) {"
+ + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
+ + " edges { cursor node { id name }}"
+ +"}}");
+ assertThat(json, hasJsonPath("errors"));
+ assertTrue(json.contains("Start cursor not found"));
+ }
+}
diff --git a/src/test/java/org/apache/sling/graphql/core/engine/PaginationTest.java b/src/test/java/org/apache/sling/graphql/core/engine/PaginationTest.java
deleted file mode 100644
index 26d9448..0000000
--- a/src/test/java/org/apache/sling/graphql/core/engine/PaginationTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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.engine;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath;
-
-import static org.hamcrest.Matchers.equalTo;
-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.mocks.CharacterTypeResolver;
-import org.apache.sling.graphql.core.mocks.HumanDTO;
-import org.apache.sling.graphql.api.SlingDataFetcher;
-import org.apache.sling.graphql.api.SlingDataFetcherEnvironment;
-import org.apache.sling.graphql.api.pagination.Cursor;
-import org.apache.sling.graphql.api.pagination.Edge;
-import org.apache.sling.graphql.api.pagination.PageInfo;
-import org.apache.sling.graphql.api.pagination.ResultsPage;
-import org.junit.Test;
-
-public class PaginationTest extends ResourceQueryTestBase {
-
- static class HumansPageFetcher implements SlingDataFetcher<Object> {
- private List<HumanDTO> humans = new ArrayList<>();
-
- HumansPageFetcher(List<HumanDTO> humans) {
- this.humans = humans;
- }
-
- @Override
- public @Nullable Object get(@NotNull SlingDataFetcherEnvironment e) throws Exception {
- final String cursor = e.getArgument("after", "");
- final int limit = e.getArgument("limit", 2);
- return new HumansResultPage(humans, cursor, limit);
- }
- }
-
- // TODO this class should be generalized so that data can be provided as
- // a data source (Stream?) with "skip after cursor" functionality, and the
- // rest of this class is then generic.
- static class HumansResultPage implements ResultsPage<HumanDTO>,PageInfo {
-
- private final Cursor startCursor;
- private Cursor endCursor;
- private boolean hasPreviousPage;
- private boolean hasNextPage;
- private final List<Edge<HumanDTO>> edges = new ArrayList<>();
-
- HumansResultPage(List<HumanDTO> humans, String cursor, int limit) {
- this.startCursor = new Cursor(Cursor.decode(cursor));
-
- boolean inRange = false;
- int remaining = limit;
- for(HumanDTO dto : humans) {
- if(remaining <= 0) {
- hasNextPage = true;
- break;
- } else if(inRange) {
- if(remaining -- <= 0) {
- inRange = false;
- }
- edges.add(new Edge<HumanDTO>() {
- @Override
- public HumanDTO getNode() {
- return dto;
- }
-
- @Override
- public Cursor getCursor() {
- return new Cursor(dto.getId());
- }
-
- });
- endCursor = new Cursor(dto.getId());
- } else if(startCursor.isEmpty()) {
- inRange = true;
- hasPreviousPage = false;
- } else if(startCursor.getRawValue().equals(dto.getId())) {
- inRange = true;
- hasPreviousPage = true;
- }
- }
- }
-
- @Override
- public List<Edge<HumanDTO>> getEdges() {
- return edges;
- }
-
- @Override
- public PageInfo getPageInfo() {
- 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;
- }
-}
-
- @Override
- protected void setupAdditionalServices() {
- final List<HumanDTO> humans = new ArrayList<>();
-
- for(int i=0 ; i < 100 ; i++) {
- humans.add(new HumanDTO("human-" + i, "Luke-" + i, "Tatooine"));
- }
- TestUtil.registerSlingTypeResolver(context.bundleContext(), "character/resolver", new CharacterTypeResolver());
- TestUtil.registerSlingDataFetcher(context.bundleContext(), "humans/paginated", new HumansPageFetcher(humans));
- }
-
- private void assertPageInfo(String json, Cursor startCursor, Cursor endCursor, Boolean hasPreviousPage, Boolean hasNextPage) {
- assertThat(json, hasJsonPath("$.data.paginatedQuery.pageInfo.startCursor", equalTo(startCursor.toString())));
- assertThat(json, hasJsonPath("$.data.paginatedQuery.pageInfo.endCursor", equalTo(endCursor.toString())));
- assertThat(json, hasJsonPath("$.data.paginatedQuery.pageInfo.hasPreviousPage", equalTo(hasPreviousPage)));
- assertThat(json, hasJsonPath("$.data.paginatedQuery.pageInfo.hasNextPage", equalTo(hasNextPage)));
- }
-
- private void assertEdges(String json, int startIndex, int endIndex) {
- int dataIndex = 0;
- for(int i=startIndex; i <= endIndex; i++) {
- final String id = "human-" + i;
- final Cursor c = new Cursor(id);
- final String name = "Luke-" + i;
- assertThat(json, hasJsonPath("$.data.paginatedQuery.edges[" + dataIndex + "].cursor", equalTo(c.toString())));
- assertThat(json, hasJsonPath("$.data.paginatedQuery.edges[" + dataIndex + "].node.id", equalTo(id)));
- assertThat(json, hasJsonPath("$.data.paginatedQuery.edges[" + dataIndex + "].node.name", equalTo(name)));
- dataIndex++;
- }
- final int count = endIndex - startIndex + 1;
- assertThat(json, hasJsonPath("$.data.paginatedQuery.edges.length()", equalTo(count)));
- }
-
- @Test
- public void noArguments() throws Exception {
- final String json = queryJSON("{ paginatedQuery {"
- + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
- + " edges { cursor node { id name }}"
- +"}}");
- assertPageInfo(json, new Cursor(null), new Cursor("human-2"), false, true );
- assertEdges(json, 1, 2);
- }
-
- @Test
- public void startCursorAndLimit() throws Exception {
- final Cursor start = new Cursor("human-5");
- final String json = queryJSON("{ paginatedQuery(after:\"" + start + "\", limit:6) {"
- + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
- + " edges { cursor node { id name }}"
- +"}}");
- assertPageInfo(json, new Cursor("human-5"), new Cursor("human-11"), true, true);
- assertEdges(json, 6, 11);
- }
-
- @Test
- public void startCursorNearEnd() throws Exception {
- final Cursor start = new Cursor("human-94");
- final String json = queryJSON("{ paginatedQuery(after:\"" + start + "\", limit:60) {"
- + " pageInfo { startCursor endCursor hasPreviousPage hasNextPage }"
- + " edges { cursor node { id name }}"
- +"}}");
- assertPageInfo(json, new Cursor("human-94"), new Cursor("human-99"), true, false);
- assertEdges(json, 95, 99);
- }
-}
diff --git a/src/test/resources/paginated-humans-schema.txt b/src/test/resources/paginated-humans-schema.txt
new file mode 100644
index 0000000..b222cd6
--- /dev/null
+++ b/src/test/resources/paginated-humans-schema.txt
@@ -0,0 +1,50 @@
+# * 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.
+
+# This directive maps fields to our Sling data fetchers
+directive @fetcher(
+ name : String,
+ options : String = "",
+ source : String = ""
+) on FIELD_DEFINITION
+
+type Query {
+ paginatedHumans (after : String, limit : Int) : PaginatedHumans @fetcher(name:"humans/paginated")
+}
+
+type Human {
+ id: ID!
+ name: String!
+ address: String
+}
+
+type PageInfo {
+ startCursor : String
+ endCursor : String
+ hasPreviousPage : Boolean
+ hasNextPage : Boolean
+}
+
+type HumanEdge {
+ cursor: String
+ node: Human
+}
+
+type PaginatedHumans {
+ edges : [HumanEdge]
+ pageInfo : PageInfo
+}
\ No newline at end of file
diff --git a/src/test/resources/test-schema.txt b/src/test/resources/test-schema.txt
index f9fb5d7..e7fb1b0 100644
--- a/src/test/resources/test-schema.txt
+++ b/src/test/resources/test-schema.txt
@@ -44,9 +44,6 @@ type Query {
# Test interface query
interfaceQuery: CharactersAsInterface @fetcher(name:"character/fetcher")
-
- # Test pagination
- paginatedQuery (after : String, limit : Int) : PaginatedHumans @fetcher(name:"humans/paginated")
}
interface CharacterInterface @resolver(name:"character/resolver" source:"CharacterInterface") {
@@ -112,19 +109,3 @@ type Droid implements CharacterInterface {
primaryFunction: String
}
-type PageInfo {
- startCursor : String
- endCursor : String
- hasPreviousPage : Boolean
- hasNextPage : Boolean
-}
-
-type HumanEdge {
- cursor: String
- node: Human
-}
-
-type PaginatedHumans {
- edges : [HumanEdge]
- pageInfo : PageInfo
-}
\ No newline at end of file