diff --git a/swh/graphql/app.py b/swh/graphql/app.py index f322478..b10c7bc 100644 --- a/swh/graphql/app.py +++ b/swh/graphql/app.py @@ -1,20 +1,19 @@ from ariadne import gql, load_schema_from_path, make_executable_schema from .resolvers import resolvers, scalars type_defs = gql(load_schema_from_path("swh/graphql/schema/schema.graphql")) schema = make_executable_schema( type_defs, resolvers.query, resolvers.origin, - # resolvers.origins, resolvers.visit, resolvers.visitstatus, resolvers.snapshot, resolvers.branch, resolvers.target, scalars.datetime_scalar, scalars.swhid_scalar, scalars.binary_text_scalar, ) diff --git a/swh/graphql/resolvers/base_node.py b/swh/graphql/resolvers/base_node.py index f5d6fd0..52f65c5 100644 --- a/swh/graphql/resolvers/base_node.py +++ b/swh/graphql/resolvers/base_node.py @@ -1,51 +1,50 @@ -from abc import ABC, abstractmethod +from abc import ABC from collections import namedtuple class BaseNode(ABC): """ Base class for all the Node resolvers """ def __init__(self, obj, info, node_data=None, **kwargs): self.obj = obj self.info = info self.kwargs = kwargs self._set_node(node_data) def _set_node(self, node_data): if node_data is None: node_data = self._get_node_data() self._node = self._get_node_from_data(node_data) def _get_node_from_data(self, node_data): """ Create an object from the dict Override to support complex data structures """ if type(node_data) is dict: return namedtuple("NodeObj", node_data.keys())(*node_data.values()) return node_data def __call__(self, *args, **kw): return self - @abstractmethod def _get_node_data(self): """ Override for desired behaviour This will be called only when node_data is not available """ # FIXME, make this call async (not for v1) return None def __getattr__(self, name): """ Any property defined in the sub-class will get precedence over the _node attributes """ return getattr(self._node, name) def is_type_of(self): return self.__class__.__name__ diff --git a/swh/graphql/resolvers/person.py b/swh/graphql/resolvers/person.py index 1fa9a01..c378389 100644 --- a/swh/graphql/resolvers/person.py +++ b/swh/graphql/resolvers/person.py @@ -1,10 +1,6 @@ from .base_node import BaseNode class PersonNode(BaseNode): """ """ - - def _get_node_data(self): - """ - """ diff --git a/swh/graphql/resolvers/release.py b/swh/graphql/resolvers/release.py index 4f88e06..81eb160 100644 --- a/swh/graphql/resolvers/release.py +++ b/swh/graphql/resolvers/release.py @@ -1,46 +1,46 @@ from swh.graphql.backends import archive from swh.graphql.utils import utils from .base_node import BaseNode class BaseReleaseNode(BaseNode): def _get_release_by_id(self, release_id): return (archive.Archive().get_release(release_id) or None)[0] @property def author(self): # return a PersoneNode object return self._node.author class ReleaseNode(BaseReleaseNode): """ When the release is requested directly (not from a connection) with an id """ def _get_node_data(self): release_id = utils.str_to_swid(self.kwargs.get("SWHId")) return self._get_release_by_id(release_id) class BranchReleaseNode(BaseReleaseNode): """ When the release is requested from a snapshot branch self.obj is a branch object self.obj.target is the release id """ def _get_node_data(self): return self._get_release_by_id(self.obj.target) def is_type_of(self): """ - is_type_of is required only when - requesting from a connection + is_type_of is required only when requesting + from a connection - This is for ariadne to return the correct type in schema + This is for ariadne to return the right type """ return "Release" diff --git a/swh/graphql/resolvers/snapshot.py b/swh/graphql/resolvers/snapshot.py index 5348119..6d210dd 100644 --- a/swh/graphql/resolvers/snapshot.py +++ b/swh/graphql/resolvers/snapshot.py @@ -1,41 +1,43 @@ from swh.graphql.backends import archive from swh.graphql.utils import utils from .base_node import BaseNode -class SnapshotNode(BaseNode): +class BaseSnapshotNode(BaseNode): + def _get_snapshot_by_id(self, snapshot_id): + return archive.Archive().get_snapshot(snapshot_id) + + +class SnapshotNode(BaseSnapshotNode): """ For directly accessing a snapshot with swhid """ def _get_node_data(self): """ """ # FIXME, use methods from SWH core - snapshot_swhid = utils.str_to_swid(self.kwargs.get("SWHId")) - return archive.Archive().get_snapshot(snapshot_swhid) + snapshot_id = utils.str_to_swid(self.kwargs.get("SWHId")) + return self._get_snapshot_by_id(snapshot_id) class VisitSnapshotNode(BaseNode): - # FIXME, maybe it is a good idea to make a - # common function for both Node classes (for handling exceptions) """ For accessing a snapshot from a visitstatus type """ - node_class = SnapshotNode def _get_node_data(self): """ self.obj is visitstatus here snapshot swhid is avaialbe in the parent (self.obj) """ - return archive.Archive().get_snapshot(self.obj.snapshot) + 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 d104145..9067013 100644 --- a/swh/graphql/resolvers/snapshot_branch.py +++ b/swh/graphql/resolvers/snapshot_branch.py @@ -1,63 +1,52 @@ # 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): - """ - This Node is instantiated only from a - connection (SnapshotBranchConnection). - Since node_data is always available, - there is no reason to make a storage - query. Hence this function will never - be called. This stub is to make the - abstract base class work. - """ - 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 """ 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 diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql index 456f801..6b844d9 100644 --- a/swh/graphql/schema/schema.graphql +++ b/swh/graphql/schema/schema.graphql @@ -1,246 +1,244 @@ 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! message: BinaryText author: Person committer: Person date: DateTime type: String directory: SWHId } # type ReleaseConnection { # } # type ReleasEdge { # } type Release implements SWHNode { id: SWHId! name: BinaryText message: BinaryText author: Person date: DateTime } 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( SWHId: String! ): Revision """ Get the release with the given swhid """ release( SWHId: String! ): Release # """ # 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 }