diff --git a/swh/graphql/backends/archive.py b/swh/graphql/backends/archive.py index e991c6e..9e07b6f 100644 --- a/swh/graphql/backends/archive.py +++ b/swh/graphql/backends/archive.py @@ -1,47 +1,44 @@ from swh.storage import get_storage class Archive: def __init__(self): # FIXME, setup config self.storage = get_storage( cls="remote", url="http://moma.internal.softwareheritage.org:5002" ) def get_origin(self, url): return self.storage.origin_get([url])[0] def get_origins(self, after=None, first=50): return self.storage.origin_list(page_token=after, limit=first) def get_origin_visits(self, origin_url, after=None, first=50): return self.storage.origin_visit_get(origin_url, page_token=after, limit=first) def get_origin_visit(self, origin_url, visit_id): return self.storage.origin_visit_get_by(origin_url, visit_id) def get_visit_status(self, origin_url, visit_id, after=None, first=50): return self.storage.origin_visit_status_get( origin_url, visit_id, page_token=after, limit=first ) - def get_snapshot(self, snapshot_swhid): - return self.storage.snapshot_get(snapshot_swhid) - def get_snapshot_branches(self, snapshot, after=None, first=50): return self.storage.snapshot_get_branches( snapshot, branches_from=after, branches_count=first ) def get_revision(self, revision_id): return self.storage.revision_get(revision_ids=[revision_id]) def get_release(self, release_id): return self.storage.release_get(releases=[release_id]) def get_directory_entries(self, directory_id): return self.storage.directory_ls(directory_id) def get_content(self, content_id): # FIXME, only for tests return self.storage.content_find({"sha1_git": content_id}) diff --git a/swh/graphql/resolvers/directory.py b/swh/graphql/resolvers/directory.py index 1dd3fb4..d476267 100644 --- a/swh/graphql/resolvers/directory.py +++ b/swh/graphql/resolvers/directory.py @@ -1,32 +1,33 @@ from swh.graphql.utils import utils from .base_node import BaseNode class BaseDirectoryNode(BaseNode): def _get_directory_by_id(self, directory_id): # Now not fetching any data (schema is exposing just id) + # same pattern is used in snapshot resolver # FIXME, use the right API to fetch metadata like name, path return { "id": directory_id, } class DirectoryNode(BaseDirectoryNode): def _get_node_data(self): """ When a directory is requested directly (not from a connection) with an id """ directory_id = utils.str_to_swid(self.kwargs.get("Sha1")) # path = "" return self._get_directory_by_id(directory_id) class RevisionDirectoryNode(BaseDirectoryNode): def _get_node_data(self): """ When a directory is requested from a revision """ directory_id = self.kwargs.get("sha1") return self._get_directory_by_id(directory_id) diff --git a/swh/graphql/resolvers/snapshot.py b/swh/graphql/resolvers/snapshot.py index 38a253b..86301df 100644 --- a/swh/graphql/resolvers/snapshot.py +++ b/swh/graphql/resolvers/snapshot.py @@ -1,42 +1,45 @@ -from swh.graphql.backends import archive from swh.graphql.utils import utils from .base_node import BaseNode class BaseSnapshotNode(BaseNode): def _get_snapshot_by_id(self, snapshot_id): - return archive.Archive().get_snapshot(snapshot_id) + # Now not fetching any data (schema is exposing just id) + # same pattern is used in directory resolver + return { + "id": snapshot_id, + } class SnapshotNode(BaseSnapshotNode): """ For directly accessing a snapshot with swhid """ def _get_node_data(self): """ """ # FIXME, use methods from SWH core snapshot_id = utils.str_to_swid(self.kwargs.get("Sha1")) return self._get_snapshot_by_id(snapshot_id) class VisitSnapshotNode(BaseSnapshotNode): """ For accessing a snapshot from a visitstatus type """ def _get_node_data(self): """ self.obj is visitstatus here - snapshot swhid is avaialbe in the parent (self.obj) + snapshot sha1 is avaialbe in the visit object (self.obj) """ return self._get_snapshot_by_id(self.obj.snapshot) # class SnapshotConnection(BaseConnection): # """ # To get all the snapshots under an origin # """ # _node_class = SnapshotNode diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py index 63edd8e..6ccf1d6 100644 --- a/swh/graphql/resolvers/snapshot_branch.py +++ b/swh/graphql/resolvers/snapshot_branch.py @@ -1,51 +1,63 @@ -# from swh.graphql.backends import archive -# from swh.graphql.utils import utils +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()) class SnapshotBranchConnection(BaseConnection): _node_class = SnapshotBranchNode def _get_paged_result(self): - return self._get_from_parent_node() - - # FIXME making extra query to the storage - # This is not really needed as we have the data - # in the self.obj itself - # Mocking paged data - # result = archive.Archive().get_snapshot_branches( - # utils.str_to_swid(self.obj.id.hex()), - # after=self._get_after_arg(), - # first=self._get_first_arg()) - # return PagedResult(results=result['branches'], - # next_page_token=result['next_branch'].hex()) - - def _get_from_parent_node(self): """ - Branches are avaialble in the snapshot object itself - Not making an extra query + When branches requested from a snapshot + self.obj.id is snapshot_id here + (as returned from resolvers/snapshot.py) """ - results = [ - { - "name": key, - "type": value["target_type"], - "id": "temp-id", - "target": value["target"], - } - for (key, value) in self.obj.branches.items() - ][: self._get_first_arg()] - # FIXME, this pagination is broken, fix it with swh-storage - # Mocking PagedResult obj - return PagedResult(results=results, next_page_token=self.obj.next_branch) - - def total_count(self): - # FIXME, this can be implemented with current swh.storage API - return None + # FIXME, this pagination is not consistent with other connections + # FIX in swh-storage to return PagedResult + result = archive.Archive().get_snapshot_branches( + self.obj.id, after=self._get_after_arg(), first=self._get_first_arg() + ) + # 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 + ) + 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) diff --git a/swh/graphql/utils/utils.py b/swh/graphql/utils/utils.py index c9afb87..4ac5e99 100644 --- a/swh/graphql/utils/utils.py +++ b/swh/graphql/utils/utils.py @@ -1,41 +1,42 @@ import base64 from swh.storage.interface import PagedResult def encode(text): return base64.b64encode(bytes(text, "utf-8")).decode("utf-8") def get_encoded_cursor(cursor): if cursor is None: return None return base64.b64encode(bytes(cursor, "utf-8")).decode("utf-8") def get_decoded_cursor(cursor): if cursor is None: - return 0 + return None return base64.b64decode(cursor).decode("utf-8") def str_to_swid(str_swid): # FIXME, use core function return bytearray.fromhex(str_swid) def paginated(source, first, after=0): """ Pagination at the GraphQL level This is a temporary fix and inefficient. Should eventually be moved to the backend (storage) level """ # FIXME, handle data errors here - end_cursor = int(after) + first - results = source[int(after) : end_cursor] + after = 0 if after is None else int(after) + end_cursor = after + first + results = source[after:end_cursor] next_page_token = None if len(source) > end_cursor: next_page_token = str(end_cursor) return PagedResult(results=results, next_page_token=next_page_token)