diff --git a/swh/graphql/resolvers/content.py b/swh/graphql/resolvers/content.py index 408cea6..d123371 100644 --- a/swh/graphql/resolvers/content.py +++ b/swh/graphql/resolvers/content.py @@ -1,44 +1,44 @@ from swh.graphql.backends import archive from .base_node import BaseSWHNode class BaseContentNode(BaseSWHNode): """ """ def _get_content_by_id(self, content_id): content = archive.Archive().get_content(content_id) return content[0] if content else None @property def checksum(self): # FIXME, return a Node object return self._node.hashes() @property def id(self): return self._node.sha1_git def is_type_of(self): return "Content" class ContentNode(BaseContentNode): def _get_node_data(self): """ When a content is requested directly - with an id + with its SWHID """ return self._get_content_by_id(self.kwargs.get("SWHID").object_id) class TargetContentNode(BaseContentNode): def _get_node_data(self): """ When a content is requested from a directory entry or from a release target - content id is obj.target here + content id is obj.targetHash here """ - content_id = self.obj.target + content_id = self.obj.targetHash return self._get_content_by_id(content_id) diff --git a/swh/graphql/resolvers/directory.py b/swh/graphql/resolvers/directory.py index 0c18611..68c5d78 100644 --- a/swh/graphql/resolvers/directory.py +++ b/swh/graphql/resolvers/directory.py @@ -1,50 +1,50 @@ from swh.graphql.backends import archive from swh.model.model import Directory from .base_node import BaseSWHNode class BaseDirectoryNode(BaseSWHNode): 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): def _get_node_data(self): """ - When a directory is requested directly with an id + When a directory is requested directly with its SWHID """ directory_id = self.kwargs.get("SWHID").object_id # path = "" if archive.Archive().is_directory_available([directory_id]): return self._get_directory_by_id(directory_id) return None class RevisionDirectoryNode(BaseDirectoryNode): def _get_node_data(self): """ When a directory is requested from a revision self.obj is revision here - self.obj.directoryId is the required dir id + self.obj.directorySWHID is the required dir SWHID (set from resolvers.revision.py:BaseRevisionNode) """ - directory_id = self.obj.directoryId + directory_id = self.obj.directorySWHID.object_id return self._get_directory_by_id(directory_id) class TargetDirectoryNode(BaseDirectoryNode): def _get_node_data(self): """ When a directory is requested as a target self.obj can be a Release or a DirectoryEntry - obj.target is the requested directory id here + obj.targetHash is the requested directory id here """ - return self._get_directory_by_id(self.obj.target) + return self._get_directory_by_id(self.obj.targetHash) diff --git a/swh/graphql/resolvers/directory_entry.py b/swh/graphql/resolvers/directory_entry.py index f1cd249..b4b9712 100644 --- a/swh/graphql/resolvers/directory_entry.py +++ b/swh/graphql/resolvers/directory_entry.py @@ -1,33 +1,34 @@ from swh.graphql.backends import archive from swh.graphql.utils import utils from .base_connection import BaseConnection from .base_node import BaseNode class DirectoryEntryNode(BaseNode): """ """ @property - def targetId(self): # To support the schema naming convention + def targetHash(self): # To support the schema naming convention return self._node.target class DirectoryEntryConnection(BaseConnection): _node_class = DirectoryEntryNode def _get_paged_result(self): """ When entries requested from a directory - self.obj.id is the directory_id here - (as returned from resolvers/directory.py) + self.obj.SWHID is the directory SWHID here This is not paginated from swh-storgae using dummy pagination """ # FIXME, using dummy(local) pagination, move pagination to backend # To remove localpagination, just drop the paginated call # STORAGE-TODO - entries = archive.Archive().get_directory_entries(self.obj.id).results + entries = ( + archive.Archive().get_directory_entries(self.obj.SWHID.object_id).results + ) return utils.paginated(entries, self._get_first_arg(), self._get_after_arg()) diff --git a/swh/graphql/resolvers/release.py b/swh/graphql/resolvers/release.py index 07b3087..767b197 100644 --- a/swh/graphql/resolvers/release.py +++ b/swh/graphql/resolvers/release.py @@ -1,45 +1,45 @@ from swh.graphql.backends import archive from .base_node import BaseSWHNode class BaseReleaseNode(BaseSWHNode): def _get_release_by_id(self, release_id): return (archive.Archive().get_releases([release_id]) or None)[0] @property - def targetId(self): # To support the schema naming convention + def targetHash(self): # To support the schema naming convention 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): """ - When the release is requested directly with an id + When the release is 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): """ When a release is requested as a target self.obj could be a snapshotbranch or a release - self.obj.target is the requested release id here + self.obj.targetHash is the requested release id here """ def _get_node_data(self): - return self._get_release_by_id(self.obj.target) + return self._get_release_by_id(self.obj.targetHash) diff --git a/swh/graphql/resolvers/resolvers.py b/swh/graphql/resolvers/resolvers.py index feb76d4..20392b5 100644 --- a/swh/graphql/resolvers/resolvers.py +++ b/swh/graphql/resolvers/resolvers.py @@ -1,199 +1,199 @@ """ High level resolvers Any schema attribute can be resolved by any of the following ways and in the following priority order - In this module using an annotation (eg: @visitstatus.field("snapshot")) - As a property in the Node object (eg: resolvers.visit.OriginVisitNode.id) - As an attribute/item in the object/dict returned by the backend (eg: Origin.url) """ from ariadne import ObjectType, UnionType from .resolver_factory import get_connection_resolver, get_node_resolver query = ObjectType("Query") origin = ObjectType("Origin") visit = ObjectType("Visit") visit_status = ObjectType("VisitStatus") snapshot = ObjectType("Snapshot") snapshot_branch = ObjectType("Branch") revision = ObjectType("Revision") release = ObjectType("Release") directory = ObjectType("Directory") directory_entry = ObjectType("DirectoryEntry") branch_target = UnionType("BranchTarget") release_target = UnionType("ReleaseTarget") directory_entry_target = UnionType("DirectoryEntryTarget") # Node resolvers # A node resolver should return an instance of BaseNode @query.field("origin") def origin_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("origin") return resolver(obj, info, **kw)() @origin.field("latestVisit") def latest_visit_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("latest-visit") return resolver(obj, info, **kw)() @query.field("visit") def visit_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("visit") return resolver(obj, info, **kw)() @visit.field("latestStatus") def latest_visit_status_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("latest-status") return resolver(obj, info, **kw)() @query.field("snapshot") def snapshot_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("snapshot") return resolver(obj, info, **kw)() @visit_status.field("snapshot") def visit_snapshot_resolver(obj, info, **kw): resolver = get_node_resolver("visit-snapshot") return resolver(obj, info, **kw)() @snapshot_branch.field("target") def snapshot_branch_target_resolver(obj, info, **kw): """ Snapshot branch target can be a revision or a release """ resolver_type = f"branch-{obj.type}" resolver = get_node_resolver(resolver_type) return resolver(obj, info, **kw)() @query.field("revision") def revision_resolver(obj, info, **kw): resolver = get_node_resolver("revision") return resolver(obj, info, **kw)() @revision.field("directory") def revision_directory_resolver(obj, info, **kw): resolver = get_node_resolver("revision-directory") return resolver(obj, info, **kw)() @query.field("release") def release_resolver(obj, info, **kw): resolver = get_node_resolver("release") return resolver(obj, info, **kw)() @release.field("target") def release_target_resolver(obj, info, **kw): """ release target can be a release, revision, directory or content obj is release here, target type is obj.target_type """ resolver_type = f"release-{obj.target_type.value}" resolver = get_node_resolver(resolver_type) return resolver(obj, info, **kw)() @query.field("directory") def directory_resolver(obj, info, **kw): resolver = get_node_resolver("directory") return resolver(obj, info, **kw)() @directory_entry.field("target") def directory_entry_target_resolver(obj, info, **kw): """ directory entry target can be a directory or a content """ resolver_type = f"dir-entry-{obj.type}" resolver = get_node_resolver(resolver_type) return resolver(obj, info, **kw)() @query.field("content") def content_resolver(obj, info, **kw): resolver = get_node_resolver("content") return resolver(obj, info, **kw)() # Connection resolvers # A connection resolver should return an instance of BaseConnection @query.field("origins") def origins_resolver(obj, info, **kw): resolver = get_connection_resolver("origins") return resolver(obj, info, **kw)() @origin.field("visits") def visits_resolver(obj, info, **kw): resolver = get_connection_resolver("origin-visits") return resolver(obj, info, **kw)() @origin.field("snapshots") def origin_snapshots_resolver(obj, info, **kw): """ """ resolver = get_connection_resolver("origin-snapshots") return resolver(obj, info, **kw)() @visit.field("status") def visitstatus_resolver(obj, info, **kw): resolver = get_connection_resolver("visit-status") return resolver(obj, info, **kw)() @snapshot.field("branches") def snapshot_branches_resolver(obj, info, **kw): resolver = get_connection_resolver("snapshot-branches") return resolver(obj, info, **kw)() @revision.field("parents") def revision_parents_resolver(obj, info, **kw): resolver = get_connection_resolver("revision-parents") return resolver(obj, info, **kw)() -@revision.field("revisionLog") -def revision_log_resolver(obj, info, **kw): - resolver = get_connection_resolver("revision-log") - return resolver(obj, info, **kw)() +# @revision.field("revisionLog") +# def revision_log_resolver(obj, info, **kw): +# resolver = get_connection_resolver("revision-log") +# return resolver(obj, info, **kw)() @directory.field("entries") def directory_entry_resolver(obj, info, **kw): resolver = get_connection_resolver("directory-entries") return resolver(obj, info, **kw)() # Any other type of resolver @release_target.type_resolver @directory_entry_target.type_resolver @branch_target.type_resolver def union_resolver(obj, *_): """ Generic resolver for all the union types """ return obj.is_type_of() diff --git a/swh/graphql/resolvers/revision.py b/swh/graphql/resolvers/revision.py index cb545a6..8cb6cd1 100644 --- a/swh/graphql/resolvers/revision.py +++ b/swh/graphql/resolvers/revision.py @@ -1,92 +1,97 @@ from swh.graphql.backends import archive from swh.graphql.utils import utils +from swh.model.swhids import CoreSWHID, ObjectType from .base_connection import BaseConnection from .base_node import BaseSWHNode class BaseRevisionNode(BaseSWHNode): def _get_revision_by_id(self, revision_id): return (archive.Archive().get_revisions([revision_id]) or None)[0] @property - def parentIds(self): # To support the schema naming convention - return self._node.parents + def parentSWHIDs(self): # To support the schema naming convention + return [ + CoreSWHID(object_type=ObjectType.REVISION, object_id=parent_id) + for parent_id in self._node.parents + ] @property - def directoryId(self): # To support the schema naming convention + def directorySWHID(self): # To support the schema naming convention """ """ - return self._node.directory + return CoreSWHID( + object_type=ObjectType.DIRECTORY, object_id=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): """ - When the revision is requested directly with its id (hash) + When the revision is 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): """ When a revision is requested as a target self.obj could be a snapshotbranch or a release - self.obj.target is the requested revision id here + self.obj.targetHash is the requested revision id here """ def _get_node_data(self): - """ - self.obj.target is the Revision id - """ - return self._get_revision_by_id(self.obj.target) + return self._get_revision_by_id(self.obj.targetHash) class ParentRevisionConnection(BaseConnection): """ - When parent revisions requested from a + When parent revisions is requested from a revision self.obj is the current(child) revision - self.obj.parentIds is the list of - requested revisions + self.obj.parentSWHIDs is the list of + parent SWHIDs """ _node_class = BaseRevisionNode def _get_paged_result(self): # FIXME, using dummy(local) pagination, move pagination to backend # To remove localpagination, just drop the paginated call # STORAGE-TODO (pagination) - parents = archive.Archive().get_revisions(self.obj.parentIds) + parents = archive.Archive().get_revisions( + [x.object_id for x in self.obj.parentSWHIDs] + ) return utils.paginated(parents, self._get_first_arg(), self._get_after_arg()) class LogRevisionConnection(BaseConnection): """ When revisionslog is requested from a revision self.obj is the current revision id """ _node_class = BaseRevisionNode def _get_paged_result(self): # STORAGE-TODO (date in revisionlog is a dict) - log = archive.Archive().get_revision_log([self.obj.id]) + log = archive.Archive().get_revision_log([self.obj.SWHID.object_id]) # 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 effa686..568a636 100644 --- a/swh/graphql/resolvers/snapshot.py +++ b/swh/graphql/resolvers/snapshot.py @@ -1,53 +1,54 @@ from swh.graphql.backends import archive from swh.graphql.utils import utils from swh.model.model import Snapshot from .base_connection import BaseConnection from .base_node import BaseSWHNode class BaseSnapshotNode(BaseSWHNode): 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={}) class SnapshotNode(BaseSnapshotNode): """ - For directly accessing a snapshot with an Id + For directly accessing a snapshot with its SWHID """ def _get_node_data(self): """ """ snapshot_id = self.kwargs.get("SWHID").object_id if archive.Archive().is_snapshot_available([snapshot_id]): return self._get_snapshot_by_id(snapshot_id) return None class VisitSnapshotNode(BaseSnapshotNode): """ For accessing a snapshot from a visitstatus type """ def _get_node_data(self): """ self.obj is visitstatus here - self.obj.snapshot is the requested snapshot id + self.obj.snapshotSWHID is the requested snapshot SWHID """ - return self._get_snapshot_by_id(self.obj.snapshot) + snapshot_id = self.obj.snapshotSWHID.object_id + return self._get_snapshot_by_id(snapshot_id) class OriginSnapshotConnection(BaseConnection): _node_class = BaseSnapshotNode def _get_paged_result(self): """ """ results = archive.Archive().get_origin_snapshots(self.obj.url) - snapshots = [{"id": snapshot} for snapshot in results] + 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()) diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py index ad57303..2c49e24 100644 --- a/swh/graphql/resolvers/snapshot_branch.py +++ b/swh/graphql/resolvers/snapshot_branch.py @@ -1,72 +1,73 @@ 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 """ + # STORAGE-TODO; return an object in the normal format 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 + def targetHash(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 + self.obj.SWHID is the snapshot SWHID here (as returned from resolvers/snapshot.py) """ result = archive.Archive().get_snapshot_branches( - self.obj.id, + self.obj.SWHID.object_id, after=self._get_after_arg(), first=self._get_first_arg(), target_types=self.kwargs.get("types"), name_include=self.kwargs.get("nameInclude"), ) # 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 # STORAGE-TODO 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/resolvers/visit_status.py b/swh/graphql/resolvers/visit_status.py index ac70411..970a69f 100644 --- a/swh/graphql/resolvers/visit_status.py +++ b/swh/graphql/resolvers/visit_status.py @@ -1,41 +1,43 @@ from swh.graphql.backends import archive +from swh.model.swhids import CoreSWHID, ObjectType from .base_connection import BaseConnection from .base_node import BaseNode class BaseVisitStatusNode(BaseNode): """ """ @property - def snapshotId(self): # To support the schema naming convention - return self._node.snapshot + def snapshotSWHID(self): # To support the schema naming convention + return CoreSWHID(object_type=ObjectType.SNAPSHOT, object_id=self._node.snapshot) class LatestVisitStatusNode(BaseVisitStatusNode): """ Get the latest visit status for a visit self.obj is the visit object here self.obj.origin is the origin URL """ def _get_node_data(self): return archive.Archive().get_latest_visit_status( self.obj.origin, self.obj.visitId ) class VisitStatusConnection(BaseConnection): """ self.obj is the visit object + self.obj.origin is the origin URL """ _node_class = BaseVisitStatusNode def _get_paged_result(self): return archive.Archive().get_visit_status( self.obj.origin, self.obj.visitId, after=self._get_after_arg(), first=self._get_first_arg(), ) diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql index 8499a54..bfccc27 100644 --- a/swh/graphql/schema/schema.graphql +++ b/swh/graphql/schema/schema.graphql @@ -1,341 +1,341 @@ scalar SWHID scalar HashValue scalar DateTime interface Node { id: ID! } interface SWHNode { SWHID: SWHID! } type PageInfo { endCursor: String hasNextPage: Boolean! } type OriginConnection { edges: [OriginEdge] nodes: [Origin] pageInfo: PageInfo! totalCount: Int } type OriginEdge { cursor: String! node: Origin } type Origin implements Node { id: ID! url: String! visits( first: Int! after: String ): VisitConnection! latestVisit: Visit snapshots( first: Int! after: String ): SnapshotConnection } type VisitConnection { edges: [VisitEdge] nodes: [Visit] pageInfo: PageInfo! totalCount: Int } type VisitEdge { cursor: String! node: Visit } type Visit implements Node { id: ID! visitId: Int date: DateTime! type: String status( first: Int after: String ): VisitStatusConnection latestStatus: VisitStatus } type VisitStatusConnection { edges: [VisitStatusEdge] nodes: [VisitStatus] pageInfo: PageInfo! totalCount: Int } type VisitStatusEdge { cursor: String! node: VisitStatus } type VisitStatus { status: String! date: DateTime! - snapshotId: HashValue + snapshotSWHID: SWHID snapshot: Snapshot type: String } type SnapshotConnection { edges: [SnapshotEdge] nodes: [Snapshot] pageInfo: PageInfo! totalCount: Int } type SnapshotEdge { cursor: String! node: Snapshot } type Snapshot implements SWHNode & Node { id: ID! SWHID: SWHID! branches( first: Int! after: String types: [BranchTypes] nameInclude: String ): BranchConnection } type BranchConnection { edges: [BranchConnectionEdge] nodes: [Branch] pageInfo: PageInfo! totalCount: Int } type BranchConnectionEdge { cursor: String! node: Branch } type Person { email: String name: String fullname: String } # FIXME, this can be Content, Directory, Snapshot, or Alias as well union BranchTarget = Revision | Release enum BranchTypes { revision release alias content directory snapshot } type Branch { name: String type: BranchTypes - targetId: HashValue + targetHash: HashValue target: BranchTarget } type RevisionConnection { edges: [RevisionEdge] nodes: [Revision] pageInfo: PageInfo! totalCount: Int } type RevisionEdge { cursor: String! node: Revision } type Revision implements SWHNode & Node { id: ID! SWHID: SWHID! message: String author: Person committer: Person date: DateTime type: String # Revision type: FIXME, change to an enum - directoryId: HashValue + directorySWHID: SWHID directory: Directory - parentIds: [HashValue] + parentSWHIDs: [SWHID] parents( first: Int after: String ): RevisionConnection - revisionLog( - first: Int! - after: String - ): RevisionConnection + # revisionLog( + # first: Int! + # after: String + # ): RevisionConnection } union ReleaseTarget = Release | Revision | Directory | Content enum ReleaseTargetType { release revision content directory } type Release implements SWHNode & Node { id: ID! SWHID: SWHID! name: String message: String author: Person date: DateTime - targetId: HashValue + targetHash: HashValue targetType: ReleaseTargetType target: ReleaseTarget } type DirectoryEntryConnection { edges: [DirectoryEntryEdge] nodes: [DirectoryEntry] pageInfo: PageInfo! totalCount: Int } type DirectoryEntryEdge { cursor: String! node: DirectoryEntry } union DirectoryEntryTarget = Directory | Content enum DirectoryEntryType { dir file rev } type DirectoryEntry { name: String type: DirectoryEntryType - targetId: HashValue + targetHash: HashValue target: DirectoryEntryTarget } type Directory implements SWHNode & Node { id: ID! SWHID: SWHID! entries( first: Int after: String ): DirectoryEntryConnection } type ContentChecksum { # FIXME, temp types blake2s256: HashValue sha1: HashValue sha1_git: HashValue sha256: HashValue } # type ContentType { # test: String # } # type ContentLanguage { # test: String # } # type ContentLicense { # test: String # } type Content implements SWHNode & Node { id: ID! SWHID: SWHID! checksum: ContentChecksum # data: # filetype: ContentType # language: ContentLanguage # license: ContentLicense length: Int status: String data: String } type Query { """ Get an origin with its url """ origin( url: String! ): Origin """ Get a list of origins matching the given filters Can also be used to search for an origin """ # FIMXE, use Input types to make this cleaner origins( first: Int! after: String urlPattern: String ): OriginConnection """ Get a visit object with its id and/or origin and visit id """ visit( originUrl: String! visitId: Int! ): Visit """ Get a snapshot with Sha1 """ snapshot( SWHID: SWHID! ): Snapshot """ Get the revision with the given Sha1 """ revision( SWHID: SWHID! ): Revision """ Get the release with the given Sha1 """ release( SWHID: SWHID! ): Release """ Get the directory with the given Sha1 """ directory( SWHID: SWHID! ): Directory """ Get the content with the given Sha1 """ content( SWHID: SWHID! ): Content # """ # Search with the given swhid # """ # searchWithSwhid } diff --git a/swh/graphql/tests/unit/resolvers/test_resolvers.py b/swh/graphql/tests/unit/resolvers/test_resolvers.py index b19795e..6ac5464 100644 --- a/swh/graphql/tests/unit/resolvers/test_resolvers.py +++ b/swh/graphql/tests/unit/resolvers/test_resolvers.py @@ -1,115 +1,115 @@ import pytest from swh.graphql import resolvers from swh.graphql.resolvers import resolvers as rs class TestResolvers: """ """ @pytest.fixture def dummy_node(self): return {"test": "test"} @pytest.mark.parametrize( "resolver_func, node_cls", [ (rs.origin_resolver, resolvers.origin.OriginNode), (rs.visit_resolver, resolvers.visit.OriginVisitNode), (rs.latest_visit_resolver, resolvers.visit.LatestVisitNode), ( rs.latest_visit_status_resolver, resolvers.visit_status.LatestVisitStatusNode, ), (rs.snapshot_resolver, resolvers.snapshot.SnapshotNode), (rs.visit_snapshot_resolver, resolvers.snapshot.VisitSnapshotNode), (rs.revision_resolver, resolvers.revision.RevisionNode), (rs.revision_directory_resolver, resolvers.directory.RevisionDirectoryNode), (rs.release_resolver, resolvers.release.ReleaseNode), (rs.directory_resolver, resolvers.directory.DirectoryNode), (rs.content_resolver, resolvers.content.ContentNode), ], ) def test_node_resolver(self, mocker, dummy_node, resolver_func, node_cls): mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node) node_obj = resolver_func(None, None) # assert the _get_node method is called on the right object assert isinstance(node_obj, node_cls) assert mock_get.assert_called @pytest.mark.parametrize( "resolver_func, connection_cls", [ (rs.origins_resolver, resolvers.origin.OriginConnection), (rs.visits_resolver, resolvers.visit.OriginVisitConnection), (rs.origin_snapshots_resolver, resolvers.snapshot.OriginSnapshotConnection), (rs.visitstatus_resolver, resolvers.visit_status.VisitStatusConnection), ( rs.snapshot_branches_resolver, resolvers.snapshot_branch.SnapshotBranchConnection, ), (rs.revision_parents_resolver, resolvers.revision.ParentRevisionConnection), - (rs.revision_log_resolver, resolvers.revision.LogRevisionConnection), + # (rs.revision_log_resolver, resolvers.revision.LogRevisionConnection), ( rs.directory_entry_resolver, resolvers.directory_entry.DirectoryEntryConnection, ), ], ) def test_connection_resolver(self, resolver_func, connection_cls): connection_obj = resolver_func(None, None) # assert the right object is returned assert isinstance(connection_obj, connection_cls) @pytest.mark.parametrize( "branch_type, node_cls", [ ("revision", resolvers.revision.TargetRevisionNode), ("release", resolvers.release.TargetReleaseNode), ], ) def test_snapshot_branch_target_resolver( self, mocker, dummy_node, branch_type, node_cls ): obj = mocker.Mock(type=branch_type) mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node) node_obj = rs.snapshot_branch_target_resolver(obj, None) assert isinstance(node_obj, node_cls) assert mock_get.assert_called @pytest.mark.parametrize( "target_type, node_cls", [ ("revision", resolvers.revision.TargetRevisionNode), ("release", resolvers.release.TargetReleaseNode), ("directory", resolvers.directory.TargetDirectoryNode), ("content", resolvers.content.TargetContentNode), ], ) def test_release_target_resolver(self, mocker, dummy_node, target_type, node_cls): obj = mocker.Mock(target_type=(mocker.Mock(value=target_type))) mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node) node_obj = rs.release_target_resolver(obj, None) assert isinstance(node_obj, node_cls) assert mock_get.assert_called @pytest.mark.parametrize( "target_type, node_cls", [ ("dir", resolvers.directory.TargetDirectoryNode), ("file", resolvers.content.TargetContentNode), ], ) def test_directory_entry_target_resolver( self, mocker, dummy_node, target_type, node_cls ): obj = mocker.Mock(type=target_type) mock_get = mocker.patch.object(node_cls, "_get_node", return_value=dummy_node) node_obj = rs.directory_entry_target_resolver(obj, None) assert isinstance(node_obj, node_cls) assert mock_get.assert_called def test_unit_resolver(self, mocker): obj = mocker.Mock() obj.is_type_of.return_value = "test" assert rs.union_resolver(obj) == "test"