Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F11023682
D7957.id28656.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Subscribers
None
D7957.id28656.diff
View Options
diff --git a/swh/graphql/resolvers/base_connection.py b/swh/graphql/resolvers/base_connection.py
--- a/swh/graphql/resolvers/base_connection.py
+++ b/swh/graphql/resolvers/base_connection.py
@@ -5,7 +5,7 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass
-from typing import Optional, Type
+from typing import Any, Optional, Type
from swh.graphql.utils import utils
@@ -18,6 +18,12 @@
endCursor: str
+@dataclass
+class ConnectionEdge:
+ node: Any
+ cursor: str
+
+
class BaseConnection(ABC):
"""
Base resolver for all the connections
@@ -94,9 +100,13 @@
return None
def _get_edges(self):
- # FIXME, make cursor work per item
- # Cursor can't be None here
- return [{"cursor": "dummy", "node": node} for node in self.nodes]
+ """
+ Return the list of connection edges, each with a cursor
+ """
+ return [
+ ConnectionEdge(node=node, cursor=self._get_index_cursor(index, node))
+ for (index, node) in enumerate(self.nodes)
+ ]
def _get_after_arg(self):
"""
@@ -110,3 +120,13 @@
page_size is set to 50 by default
"""
return self.kwargs.get("first", self._page_size)
+
+ def _get_index_cursor(self, index: int, node: Any):
+ """
+ Get the cursor to the given item index
+ """
+ # default implementation which works with swh-storage pagaination
+ # override this function to support other types (eg: SnapshotBranchConnection)
+ offset_index = self._get_after_arg() or "0"
+ index_cursor = int(offset_index) + index
+ return utils.get_encoded_cursor(str(index_cursor))
diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py
--- a/swh/graphql/resolvers/snapshot_branch.py
+++ b/swh/graphql/resolvers/snapshot_branch.py
@@ -77,3 +77,7 @@
# the base class, hack to make that work
after = utils.get_decoded_cursor(self.kwargs.get("after", ""))
return bytes.fromhex(after)
+
+ def _get_index_cursor(self, index: int, node: SnapshotBranchNode):
+ # Snapshot branch is using a different cursor, hence the override
+ return utils.get_encoded_cursor(node.name.hex())
diff --git a/swh/graphql/tests/functional/test_pagination.py b/swh/graphql/tests/functional/test_pagination.py
new file mode 100644
--- /dev/null
+++ b/swh/graphql/tests/functional/test_pagination.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2022 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from ..data import get_origins
+from .utils import get_query_response
+
+
+# Using Origin object to run functional tests for pagination
+def test_pagination(client):
+ # requesting the max number of nodes available
+ # endCursor must be None
+ query_str = f"""
+ {{
+ origins(first: {len(get_origins())}) {{
+ nodes {{
+ id
+ }}
+ pageInfo {{
+ hasNextPage
+ endCursor
+ }}
+ }}
+ }}
+ """
+
+ data, _ = get_query_response(client, query_str)
+ assert len(data["origins"]["nodes"]) == len(get_origins())
+ assert data["origins"]["pageInfo"] == {"hasNextPage": False, "endCursor": None}
+
+
+def get_first_node(client):
+ query_str = """
+ {
+ origins(first: 1) {
+ nodes {
+ id
+ }
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ }
+ }
+ """
+ data, _ = get_query_response(client, query_str)
+ return data["origins"]
+
+
+def test_first_arg(client):
+ origins = get_first_node(client)
+ assert len(origins["nodes"]) == 1
+ assert origins["pageInfo"]["hasNextPage"] is True
+
+
+def test_after_arg(client):
+ origins = get_first_node(client)
+ end_cursor = origins["pageInfo"]["endCursor"]
+ query_str = f"""
+ {{
+ origins(first: 1, after: "{end_cursor}") {{
+ nodes {{
+ id
+ }}
+ pageInfo {{
+ hasNextPage
+ endCursor
+ }}
+ }}
+ }}
+ """
+ data, _ = get_query_response(client, query_str)
+ assert len(data["origins"]["nodes"]) == 1
+ assert data["origins"]["pageInfo"] == {"hasNextPage": False, "endCursor": None}
+
+
+def test_edge_cursor(client):
+ origins = get_first_node(client)
+ # end cursor here must be the item cursor for the second item
+ end_cursor = origins["pageInfo"]["endCursor"]
+
+ query_str = f"""
+ {{
+ origins(first: 1, after: "{end_cursor}") {{
+ edges {{
+ cursor
+ node {{
+ id
+ }}
+ }}
+ nodes {{
+ id
+ }}
+ }}
+ }}
+ """
+ data, _ = get_query_response(client, query_str)
+ origins = data["origins"]
+ # nodes in list node fields in edges must be the same
+ assert [edge["node"] for edge in origins["edges"]] == origins["nodes"]
+ assert origins["edges"][0]["cursor"] == end_cursor
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Sep 17, 4:56 PM (21 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3222702
Attached To
D7957: Add an item cursor to the connection edge
Event Timeline
Log In to Comment