diff --git a/swh/graphql/resolvers/base_connection.py b/swh/graphql/resolvers/base_connection.py index cbb8818..617d2a7 100644 --- a/swh/graphql/resolvers/base_connection.py +++ b/swh/graphql/resolvers/base_connection.py @@ -1,119 +1,107 @@ from abc import ABC, abstractmethod -from typing import Any +from dataclasses import dataclass +from typing import Optional, Type from swh.graphql.utils import utils -# from dataclasses import dataclass +from .base_node import BaseNode -# @dataclass -# class PageInfo: -# nex_page_token: str - -# class Arguments: -# """ -# dataclass -# """ -# after # Elements that come after the specified cursor -# first # Returns the first n elements +@dataclass +class PageInfo: + hasNextPage: bool + endCursor: str class BaseConnection(ABC): """ Base class for all the connection resolvers """ - _node_class: Any = None + _node_class: Optional[Type[BaseNode]] = None _page_size = 50 # default page size def __init__(self, obj, info, paged_data=None, **kwargs): self.obj = obj self.info = info self.kwargs = kwargs self._paged_data = paged_data def __call__(self, *args, **kw): return self @property def edges(self): return self._get_edges() @property def nodes(self): """ - Override if needed - return a list of objects + Override if needed; return a list of objects - If a node class is set, - return a list of its (Node) instances + If a node class is set, return a list of its (Node) instances else a list of raw results """ - if self._node_class is not None: return [ self._node_class(self.obj, self.info, node_data=result, **self.kwargs) for result in self.get_paged_data().results ] return self.get_paged_data().results @property def pageInfo(self): # To support the schema naming convention - # FIXME Replace with a dataclass - # return PageInfo(self.page_data.next_page_token) # FIXME, add more details like startCursor - return { - "hasNextPage": bool(self.get_paged_data().next_page_token), - "endCursor": utils.get_encoded_cursor( - self.get_paged_data().next_page_token - ), - } + return PageInfo( + hasNextPage=bool(self.get_paged_data().next_page_token), + endCursor=utils.get_encoded_cursor(self.get_paged_data().next_page_token), + ) @property def totalCount(self): # To support the schema naming convention return self._get_total_count() def _get_total_count(self): """ Will be None for most of the connections override if needed/possible """ return None def get_paged_data(self): """ Cache to avoid multiple calls to the backend (_get_paged_result) return a PagedResult object """ if self._paged_data is None: # FIXME, make this call async (not for v1) self._paged_data = self._get_paged_result() return self._paged_data @abstractmethod def _get_paged_result(self): """ Override for desired behaviour return a PagedResult object """ # FIXME, make this call async (not for v1) 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] def _get_after_arg(self): """ Return the decoded next page token override to use a specific token """ return utils.get_decoded_cursor(self.kwargs.get("after")) def _get_first_arg(self): """ page_size is set to 50 by default """ return self.kwargs.get("first", self._page_size) diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py index c9390c2..c35586d 100644 --- a/swh/graphql/resolvers/snapshot_branch.py +++ b/swh/graphql/resolvers/snapshot_branch.py @@ -1,70 +1,70 @@ from collections import namedtuple from swh.graphql.backends import archive from swh.graphql.utils import utils from swh.storage.interface import PagedResult from .base_connection import BaseConnection from .base_node import BaseNode class SnapshotBranchNode(BaseNode): """ target field for this Node is a UNION in the schema It is resolved in resolvers.resolvers.py """ def _get_node_from_data(self, node_data): """ node_data is not a dict in this case overriding to support this special data structure """ branch_name, branch_obj = node_data node = { "name": branch_name, "type": branch_obj.target_type.value, "target": branch_obj.target, } return namedtuple("NodeObj", node.keys())(*node.values()) @property def targetId(self): # To support the schema naming convention return self._node.target class SnapshotBranchConnection(BaseConnection): _node_class = SnapshotBranchNode def _get_paged_result(self): """ When branches requested from a snapshot self.obj.id is snapshot_id here (as returned from resolvers/snapshot.py) """ result = archive.Archive().get_snapshot_branches( self.obj.id, after=self._get_after_arg(), first=self._get_first_arg(), - target_types=self.kwargs.get("types", None), + target_types=self.kwargs.get("types"), ) # FIXME Cursor must be a hex to be consistent with # the base class, hack to make that work end_cusrsor = ( result["next_branch"].hex() if result["next_branch"] is not None else None ) # FIXME, this pagination is not consistent with other connections # FIX in swh-storage to return PagedResult return PagedResult( results=result["branches"].items(), next_page_token=end_cusrsor ) def _get_after_arg(self): """ Snapshot branch is using a different cursor; logic to handle that """ # FIXME Cursor must be a hex to be consistent with # the base class, hack to make that work after = utils.get_decoded_cursor(self.kwargs.get("after", "")) return bytes.fromhex(after)