diff --git a/swh/graphql/resolvers/content.py b/swh/graphql/resolvers/content.py index 71e91ca..e11a1c5 100644 --- a/swh/graphql/resolvers/content.py +++ b/swh/graphql/resolvers/content.py @@ -1,93 +1,98 @@ # 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 typing import Union from .base_node import BaseSWHNode from .directory_entry import BaseDirectoryEntryNode from .release import BaseReleaseNode +from .search import SearchResultNode from .snapshot_branch import BaseSnapshotBranchNode class BaseContentNode(BaseSWHNode): """ Base resolver for all the content nodes """ def _get_content_by_hash(self, checksums: dict): content = self.archive.get_contents(checksums) # in case of a conflict, return the first element return content[0] if content else None @property def checksum(self): # FIXME, use a Node instead return {k: v.hex() for (k, v) in self._node.hashes().items()} @property def id(self): return self._node.sha1_git @property def data(self): # FIXME, return a Node object # FIXME, add more ways to retrieve data like binary string archive_url = "https://archive.softwareheritage.org/api/1/" content_sha1 = self._node.hashes()["sha1"] return { "url": f"{archive_url}content/sha1:{content_sha1.hex()}/raw/", } @property def fileType(self): # FIXME, fetch data from the indexers return None @property def language(self): # FIXME, fetch data from the indexers return None @property def license(self): # FIXME, fetch data from the indexers return None def is_type_of(self): # is_type_of is required only when resolving a UNION type # This is for ariadne to return the right type return "Content" class ContentNode(BaseContentNode): """ Node resolver for a content requested directly with its SWHID """ def _get_node_data(self): checksums = {"sha1_git": self.kwargs.get("swhid").object_id} return self._get_content_by_hash(checksums) class HashContentNode(BaseContentNode): """ Node resolver for a content requested with one or more checksums """ def _get_node_data(self): checksums = dict(self.kwargs.get("checksums")) return self._get_content_by_hash(checksums) class TargetContentNode(BaseContentNode): """ Node resolver for a content requested as a target - This request could be from directory entry, release or a branch """ - obj: Union[BaseDirectoryEntryNode, BaseReleaseNode, BaseSnapshotBranchNode] + obj: Union[ + SearchResultNode, + BaseDirectoryEntryNode, + BaseReleaseNode, + BaseSnapshotBranchNode, + ] def _get_node_data(self): return self._get_content_by_hash(checksums={"sha1_git": self.obj.target_hash}) diff --git a/swh/graphql/resolvers/directory.py b/swh/graphql/resolvers/directory.py index 1776ae6..1846f4c 100644 --- a/swh/graphql/resolvers/directory.py +++ b/swh/graphql/resolvers/directory.py @@ -1,71 +1,77 @@ # 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 typing import Union from swh.model.model import Directory from swh.model.swhids import ObjectType from .base_node import BaseSWHNode from .release import BaseReleaseNode from .revision import BaseRevisionNode +from .search import SearchResultNode from .snapshot_branch import BaseSnapshotBranchNode class BaseDirectoryNode(BaseSWHNode): """ Base resolver for all the directory nodes """ def _get_directory_by_id(self, directory_id): # Return a Directory model object # entries is initialized as empty # Same pattern is used in snapshot return Directory(id=directory_id, entries=()) def is_type_of(self): return "Directory" class DirectoryNode(BaseDirectoryNode): """ Node resolver for a directory requested directly with its SWHID """ def _get_node_data(self): swhid = self.kwargs.get("swhid") if ( swhid.object_type == ObjectType.DIRECTORY and self.archive.is_object_available(swhid.object_id, swhid.object_type) ): # _get_directory_by_id is not making any backend call # hence the is_directory_available validation return self._get_directory_by_id(swhid.object_id) return None class RevisionDirectoryNode(BaseDirectoryNode): """ Node resolver for a directory requested from a revision """ obj: BaseRevisionNode def _get_node_data(self): # self.obj.directory_hash is the requested directory Id return self._get_directory_by_id(self.obj.directory_hash) class TargetDirectoryNode(BaseDirectoryNode): """ Node resolver for a directory requested as a target """ from .directory_entry import BaseDirectoryEntryNode - obj: Union[BaseSnapshotBranchNode, BaseReleaseNode, BaseDirectoryEntryNode] + obj: Union[ + BaseSnapshotBranchNode, + BaseReleaseNode, + BaseDirectoryEntryNode, + SearchResultNode, + ] def _get_node_data(self): return self._get_directory_by_id(self.obj.target_hash) diff --git a/swh/graphql/resolvers/release.py b/swh/graphql/resolvers/release.py index b678ef0..3e507bb 100644 --- a/swh/graphql/resolvers/release.py +++ b/swh/graphql/resolvers/release.py @@ -1,52 +1,53 @@ # 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 typing import Union from .base_node import BaseSWHNode +from .search import SearchResultNode from .snapshot_branch import BaseSnapshotBranchNode class BaseReleaseNode(BaseSWHNode): """ Base resolver for all the release nodes """ def _get_release_by_id(self, release_id): return self.archive.get_releases([release_id])[0] @property def target_hash(self): return self._node.target @property def targetType(self): # To support the schema naming convention return self._node.target_type.value def is_type_of(self): # is_type_of is required only when resolving a UNION type # This is for ariadne to return the right type return "Release" class ReleaseNode(BaseReleaseNode): """ Node resolver for a release requested directly with its SWHID """ def _get_node_data(self): return self._get_release_by_id(self.kwargs.get("swhid").object_id) class TargetReleaseNode(BaseReleaseNode): """ Node resolver for a release requested as a target """ - obj: Union[BaseSnapshotBranchNode, BaseReleaseNode] + obj: Union[BaseSnapshotBranchNode, BaseReleaseNode, SearchResultNode] def _get_node_data(self): # self.obj.target_hash is the requested release id return self._get_release_by_id(self.obj.target_hash) diff --git a/swh/graphql/resolvers/revision.py b/swh/graphql/resolvers/revision.py index 7870011..0fd8bd2 100644 --- a/swh/graphql/resolvers/revision.py +++ b/swh/graphql/resolvers/revision.py @@ -1,110 +1,116 @@ # 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 typing import Union from swh.graphql.utils import utils from swh.model.model import Revision from swh.model.swhids import CoreSWHID, ObjectType from swh.storage.interface import PagedResult from .base_connection import BaseConnection from .base_node import BaseSWHNode from .directory_entry import BaseDirectoryEntryNode from .release import BaseReleaseNode +from .search import SearchResultNode from .snapshot_branch import BaseSnapshotBranchNode class BaseRevisionNode(BaseSWHNode): """ Base resolver for all the revision nodes """ def _get_revision_by_id(self, revision_id): return self.archive.get_revisions([revision_id])[0] @property def parent_swhids(self): # for ParentRevisionConnection resolver return [ CoreSWHID(object_type=ObjectType.REVISION, object_id=parent_id) for parent_id in self._node.parents ] @property def directory_hash(self): # for RevisionDirectoryNode resolver return self._node.directory @property def type(self): return self._node.type.value def is_type_of(self): # is_type_of is required only when resolving a UNION type # This is for ariadne to return the right type return "Revision" class RevisionNode(BaseRevisionNode): """ Node resolver for a revision requested directly with its SWHID """ def _get_node_data(self): return self._get_revision_by_id(self.kwargs.get("swhid").object_id) class TargetRevisionNode(BaseRevisionNode): """ Node resolver for a revision requested as a target """ - obj: Union[BaseSnapshotBranchNode, BaseReleaseNode, BaseDirectoryEntryNode] + obj: Union[ + BaseSnapshotBranchNode, + BaseReleaseNode, + BaseDirectoryEntryNode, + SearchResultNode, + ] def _get_node_data(self): # self.obj.target_hash is the requested revision id return self._get_revision_by_id(self.obj.target_hash) class ParentRevisionConnection(BaseConnection): """ Connection resolver for parent revisions in a revision """ obj: BaseRevisionNode _node_class = BaseRevisionNode def _get_paged_result(self) -> PagedResult: # self.obj is the current(child) revision # self.obj.parent_swhids is the list of parent SWHIDs # FIXME, using dummy(local) pagination, move pagination to backend # To remove localpagination, just drop the paginated call # STORAGE-TODO (pagination) parents = self.archive.get_revisions( [x.object_id for x in self.obj.parent_swhids] ) return utils.paginated(parents, self._get_first_arg(), self._get_after_arg()) class LogRevisionConnection(BaseConnection): """ Connection resolver for the log (list of revisions) in a revision """ obj: BaseRevisionNode _node_class = BaseRevisionNode def _get_paged_result(self) -> PagedResult: log = self.archive.get_revision_log([self.obj.swhid.object_id]) # Storage is returning a list of dicts instead of model objects # Following loop is to reverse that operation # STORAGE-TODO; remove to_dict from storage.revision_log log = [Revision.from_dict(rev) for rev in log] # FIXME, using dummy(local) pagination, move pagination to backend # To remove localpagination, just drop the paginated call # STORAGE-TODO (pagination) return utils.paginated(log, self._get_first_arg(), self._get_after_arg()) diff --git a/swh/graphql/resolvers/snapshot.py b/swh/graphql/resolvers/snapshot.py index 52f536e..011aab7 100644 --- a/swh/graphql/resolvers/snapshot.py +++ b/swh/graphql/resolvers/snapshot.py @@ -1,93 +1,94 @@ # 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 typing import Union from swh.graphql.utils import utils from swh.model.model import Snapshot from swh.model.swhids import ObjectType from swh.storage.interface import PagedResult from .base_connection import BaseConnection from .base_node import BaseSWHNode from .origin import OriginNode +from .search import SearchResultNode from .visit_status import BaseVisitStatusNode class BaseSnapshotNode(BaseSWHNode): """ Base resolver for all the snapshot nodes """ def _get_snapshot_by_id(self, snapshot_id): # Return a Snapshot model object # branches is initialized as empty # Same pattern is used in directory return Snapshot(id=snapshot_id, branches={}) def is_type_of(self): # is_type_of is required only when resolving a UNION type # This is for ariadne to return the right type return "Snapshot" class SnapshotNode(BaseSnapshotNode): """ Node resolver for a snapshot requested directly with its SWHID """ def _get_node_data(self): """ """ swhid = self.kwargs.get("swhid") if ( swhid.object_type == ObjectType.SNAPSHOT and self.archive.is_object_available(swhid.object_id, swhid.object_type) ): return self._get_snapshot_by_id(swhid.object_id) return None class VisitSnapshotNode(BaseSnapshotNode): """ Node resolver for a snapshot requested from a visit-status """ obj: BaseVisitStatusNode def _get_node_data(self): snapshot_id = self.obj.snapshotSWHID.object_id return self._get_snapshot_by_id(snapshot_id) class TargetSnapshotNode(BaseSnapshotNode): """ Node resolver for a snapshot requested as a target """ from .snapshot_branch import BaseSnapshotBranchNode - obj: Union[BaseVisitStatusNode, BaseSnapshotBranchNode] + obj: Union[SearchResultNode, BaseSnapshotBranchNode] def _get_node_data(self): snapshot_id = self.obj.target_hash return self._get_snapshot_by_id(snapshot_id) class OriginSnapshotConnection(BaseConnection): """ Connection resolver for the snapshots in an origin """ obj: OriginNode _node_class = BaseSnapshotNode def _get_paged_result(self) -> PagedResult: results = self.archive.get_origin_snapshots(self.obj.url) snapshots = [Snapshot(id=snapshot, branches={}) for snapshot in results] # FIXME, using dummy(local) pagination, move pagination to backend # To remove localpagination, just drop the paginated call # STORAGE-TODO return utils.paginated(snapshots, self._get_first_arg(), self._get_after_arg())