diff --git a/swh/graphql/backends/archive.py b/swh/graphql/backends/archive.py index c27a32f..c4f1dd8 100644 --- a/swh/graphql/backends/archive.py +++ b/swh/graphql/backends/archive.py @@ -1,34 +1,37 @@ 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]) diff --git a/swh/graphql/resolvers/origin.py b/swh/graphql/resolvers/origin.py index ca98377..9601425 100644 --- a/swh/graphql/resolvers/origin.py +++ b/swh/graphql/resolvers/origin.py @@ -1,20 +1,24 @@ from swh.graphql.backends import archive from .base_connection import BaseConnection from .base_node import BaseNode class OriginNode(BaseNode): def _get_node_data(self): # FIXME, make this call async (not for v1) return archive.Archive().get_origin(self.kwargs.get("url")) + # @property + # def url(self): + # return "test" + class OriginConnection(BaseConnection): _node_class = OriginNode def _get_page_result(self): # FIXME, make this call async (not for v1) return archive.Archive().get_origins( after=self._get_after_arg(), first=self._get_first_arg() ) diff --git a/swh/graphql/resolvers/resolver_factory.py b/swh/graphql/resolvers/resolver_factory.py index 213bae0..7ebf8ff 100644 --- a/swh/graphql/resolvers/resolver_factory.py +++ b/swh/graphql/resolvers/resolver_factory.py @@ -1,46 +1,46 @@ from .origin import OriginConnection, OriginNode from .release import ReleaseNode -from .revision import RevisionNode +from .revision import BranchRevisionNode # , RevisionNode from .snapshot import SnapshotNode, VisitSnapshotNode from .snapshot_branch import SnapshotBranchConnection from .visit import OriginVisitConnection, OriginVisitNode from .visit_status import VisitStatusConnection # def get_mapping_key(info): # """ # Logic to resolve mapping type # """ # # FIXME, move to utils # if info.path.prev: # return f"{info.path.prev.key}_{info.path.key}" # return info.path.key def get_node_resolver(resolver_type): # FIXME, replace with a proper factory method mapping = { "origin": OriginNode, "visit": OriginVisitNode, "visit-snapshot": VisitSnapshotNode, "snapshot": SnapshotNode, - "revision": RevisionNode, - "release": ReleaseNode, + "branch-revision": BranchRevisionNode, + "branch-release": ReleaseNode, } # resolver_type = get_mapping_key(info) # FIXME, get full name if resolver_type not in mapping: raise AttributeError(f"Invalid type request {resolver_type}") return mapping[resolver_type] def get_connection_resolver(resolver_type): # FIXME, replace with a proper factory method mapping = { "origins": OriginConnection, "origin-visits": OriginVisitConnection, "visit-status": VisitStatusConnection, "snapshot-branches": SnapshotBranchConnection, } # resolver_type = get_mapping_key(info) # FIXME, get full name if resolver_type not in mapping: raise AttributeError(f"Invalid type request {resolver_type}") return mapping[resolver_type] diff --git a/swh/graphql/resolvers/resolvers.py b/swh/graphql/resolvers/resolvers.py index 5856213..e0c8f05 100644 --- a/swh/graphql/resolvers/resolvers.py +++ b/swh/graphql/resolvers/resolvers.py @@ -1,102 +1,102 @@ """ 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 model object (eg: models.visit.VisitModel.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") origins = ObjectType("OriginConnection") visit = ObjectType("Visit") visitstatus = ObjectType("VisitStatus") snapshot = ObjectType("Snapshot") branch = ObjectType("Branch") target = UnionType("BranchTarget") # Node resolvers # A node resolver can return a model object or a data structure @query.field("origin") def origin_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("origin") return resolver(obj, info, **kw)() @query.field("visit") def visit_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("visit") return resolver(obj, info, **kw)() @query.field("snapshot") def snapshot_resolver(obj, info, **kw): """ """ resolver = get_node_resolver("snapshot") return resolver(obj, info, **kw)() @visitstatus.field("snapshot") def visit_snapshot(obj, info, **kw): resolver = get_node_resolver("visit-snapshot") return resolver(obj, info, **kw)() @branch.field("target") def branch_target(obj, info, **kw): """ Branch target can be a revision or a release """ - resolver_type = obj.type + resolver_type = "branch-" + obj.type resolver = get_node_resolver(resolver_type) return resolver(obj, info, **kw)() # Connection resolvers # A connection resolver will return a sub class 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)() @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(obj, info, **kw): resolver = get_connection_resolver("snapshot-branches") return resolver(obj, info, **kw)() # Any other type of resolver @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 92820a2..4911f7b 100644 --- a/swh/graphql/resolvers/revision.py +++ b/swh/graphql/resolvers/revision.py @@ -1,10 +1,42 @@ +from swh.graphql.backends import archive + from .base_node import BaseNode class RevisionNode(BaseNode): + """ + When the revision is requested + directly using an id + """ + def _get_node_data(self): + """ + """ + + +class BranchRevisionNode(BaseNode): + """ + When the revision is requested from + a snapshot branch + self.obj is a branch object + self.obj.target is the revision id + """ + + def _get_node_data(self): + """ + self.obj.target is the Revision id + """ # FIXME, make this call async (not for v1) - return {"rev": "test"} + # k = (archive.Archive().get_revision(self.obj.target) or None)[0] + # import pdb; pdb.set_trace() + return (archive.Archive().get_revision(self.obj.target) or None)[0] def is_type_of(self): + """ + is_type_of is required only when + requesting from a connection + + This is for ariadne to return the correct type in schema + """ + # FIXME, this is coupled with the schema return "Revision" diff --git a/swh/graphql/resolvers/snapshot_branch.py b/swh/graphql/resolvers/snapshot_branch.py index e7b6257..cc45af6 100644 --- a/swh/graphql/resolvers/snapshot_branch.py +++ b/swh/graphql/resolvers/snapshot_branch.py @@ -1,55 +1,55 @@ # 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): """ """ def _get_node_data(self): pass class SnapshotBranchConnection(BaseConnection): _node_class = SnapshotBranchNode def _get_page_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 """ results = [ { "name": key, "type": value["target_type"], "id": "temp-id", "target": value["target"], } for (key, value) in self.obj.branches.items() - ][:1] + ][: 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 diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql index a750249..303fd84 100644 --- a/swh/graphql/schema/schema.graphql +++ b/swh/graphql/schema/schema.graphql @@ -1,223 +1,234 @@ scalar SWHId scalar DateTime scalar BinaryText interface Node { id: ID! } interface SWHNode { id: 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 SWHNode { id: SWHId! # FIXME, this is not swhid url: String! visits( first: Int after: String ): VisitConnection! } type VisitConnection { edges: [VisitEdge] nodes: [Visit] pageInfo: PageInfo! totalCount: Int } type VisitEdge { cursor: String! node: Visit } type Visit implements Node { id: ID! date: DateTime! type: String status( first: Int after: String ): VisitStatusConnection # origin: Origin # FIXME, this can be added later } type VisitStatusConnection { edges: [VisitStatusEdge] nodes: [VisitStatus] pageInfo: PageInfo! totalCount: Int } type VisitStatusEdge { cursor: String! node: VisitStatus } type VisitStatus implements Node { id: ID! status: String! date: DateTime! snapshot: Snapshot type: String } # FIXME, add OriginSnapshotConnection type Snapshot implements SWHNode { id: SWHId! branches( first: Int after: String ): BranchConnection # releases( # first: Int # after: String # ): ReleaseConnection # FIXME, add alias type as well } type BranchConnection { edges: [BranchConnectionEdge] nodes: [Branch] pageInfo: PageInfo! totalCount: Int } type BranchConnectionEdge { cursor: String! node: [Branch] } # FIXME, this could be alias or Directory as well union BranchTarget = Revision | Release type Branch implements Node { # FIXME, maybe implement Node is not needed here # As this has no independent existence id: ID! name: BinaryText type: String # FIXME, change to an enum target: BranchTarget } # type RevisionConnection { # } # type RevisionEdge { # } +type Person { + email: BinaryText + name: BinaryText + fullname: BinaryText +} + type Revision implements SWHNode { id: SWHId! - rev: String + message: BinaryText + author: Person + committer: Person + date: DateTime + type: String + directory: SWHId } # type ReleaseConnection { # } # type ReleasEdge { # } type Release implements SWHNode { id: SWHId! rel: String } type Directory implements SWHNode { id: SWHId! } type Content implements SWHNode { id: SWHId! } type Query { """ Get an origin with its url """ # FIXME, find some unique id to help cache # maybe base64 encode the 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 ): OriginConnection """ Get a visit object with its id and/or origin and visit id """ # FIXME, find some unique id to help cache visit( originUrl: String! id: String! ): Visit """ Get a snapshot with SWHId """ snapshot( SWHId: String! ): Snapshot # """ # Get all the snapshot for the given origin # """ # originSnapshot( # originUrl: String! # first: Int # after: String # ): SnapshotConnection # """ # Get the revision with the given swhid # """ # revision() # """ # Get the directory with the given swhid # """ # directory # """ # Get the content with the given swhid # """ # content( # SWHId: String! # ): Content # """ # Search with the given swhid # """ # searchWithSwhid }