Page MenuHomeSoftware Heritage

D7957.id28656.diff
No OneTemporary

D7957.id28656.diff

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

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

Event Timeline