Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9697769
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Subscribers
None
View Options
diff --git a/swh/graphql/resolvers/base_connection.py b/swh/graphql/resolvers/base_connection.py
index fa233d9..7be1e8f 100644
--- a/swh/graphql/resolvers/base_connection.py
+++ b/swh/graphql/resolvers/base_connection.py
@@ -1,121 +1,118 @@
from abc import ABC, abstractmethod
from typing import Any
from swh.graphql.utils import utils
# from dataclasses import dataclass
# @dataclass
# class PageInfo:
# nex_page_token: str
# class Arguments:
# """
# dataclass
# """
# after # Elements that come after the specified cursor
# first # Returns the first n elements
class BaseConnection(ABC):
"""
Base class for all the connection resolvers
"""
_node_class: Any = None
_page_size = 50 # default page size
- def __init__(self, obj, info, **kwargs):
+ def __init__(self, obj, info, paged_data=None, **kwargs):
self.obj = obj
self.info = info
self.kwargs = kwargs
-
- self._paged_data = None
- self.pageInfo = self.page_info # To match the name in schema
- self.totalCount = self.total_count # To match the name in schema
+ self._paged_data = paged_data
def __call__(self, *args, **kw):
return self
@property
def edges(self):
return self._get_edges()
@property
def nodes(self):
"""
Override if needed
return a list of objects
If a node class is set,
return a list of its (Node) intances
else a list of raw results
"""
if self._node_class is not None:
return [
self._node_class(self.obj, self.info, node_data=result, **self.kwargs)
for result in self.get_paged_data().results
]
return self.get_paged_data().results
@property
- def page_info(self):
+ def pageInfo(self): # To support the schema naming convention
# FIXME Replace with a dataclass
# return PageInfo(self.page_data.next_page_token)
# FIXME, add more details like startCursor
return {
"hasNextPage": bool(self.get_paged_data().next_page_token),
"endCursor": utils.get_encoded_cursor(
self.get_paged_data().next_page_token
),
}
@property
- def total_count(self):
+ def totalCount(self): # To support the schema naming convention
"""
Will be None for most of the connections
override if needed/possible
"""
return None
def get_paged_data(self):
"""
Cache to avoid multiple calls to
the backend (_get_paged_result)
return a PagedResult object
"""
if self._paged_data is None:
# FIXME, make this call async (not for v1)
self._paged_data = self._get_paged_result()
return self._paged_data
@abstractmethod
def _get_paged_result(self):
"""
Override for desired behaviour
return a PagedResult object
"""
# FIXME, make this call async (not for v1)
return None
def _get_edges(self):
# FIXME, make cursor work per item
# Cursor can't be None here
return [{"cursor": "dummy", "node": node} for node in self.nodes]
def _get_after_arg(self):
"""
Return the decoded next page token
override to use a specific token
"""
return utils.get_decoded_cursor(self.kwargs.get("after"))
def _get_first_arg(self):
"""
page_size is set to 50 by default
"""
return self.kwargs.get("first", self._page_size)
diff --git a/swh/graphql/resolvers/revision.py b/swh/graphql/resolvers/revision.py
index e3e2dc2..af54d0c 100644
--- a/swh/graphql/resolvers/revision.py
+++ b/swh/graphql/resolvers/revision.py
@@ -1,55 +1,82 @@
from swh.graphql.backends import archive
from swh.graphql.utils import utils
from .base_node import BaseNode
class BaseRevisionNode(BaseNode):
def _get_revision_by_id(self, revision_id):
+ # FIXME, make this call async
return (archive.Archive().get_revision(revision_id) or None)[0]
@property
def author(self):
# return a PersoneNode object
return self._node.author
@property
def committer(self):
# return a PersoneNode object
return self._node.committer
+ @property
+ def parentIds(self): # To support the schema naming convention
+ return self._node.parents
+
+ # @paginatedlist
+ @property
+ def parents(self):
+ """
+ Return a list of parent revisions
+ """
+ # FIXME, change this to a paginated list
+ # Storage fix or use paginatedlist decorator
+ return [
+ ParentRevisionNode(obj=self, info=self.info, sha1=revision_id)
+ for revision_id in self.parentIds
+ ]
+
+ 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
+ """
+ return "Revision"
+
class RevisionNode(BaseRevisionNode):
"""
When the revision is requested directly
(not from a connection) with an id
"""
def _get_node_data(self):
revision_id = utils.str_to_swid(self.kwargs.get("SWHId"))
return self._get_revision_by_id(revision_id)
+class ParentRevisionNode(BaseRevisionNode):
+ """
+ When a parent revision is requested
+ """
+
+ def _get_node_data(self):
+ revision_id = self.kwargs.get("sha1")
+ return self._get_revision_by_id(revision_id)
+
+
class BranchRevisionNode(BaseRevisionNode):
"""
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 self._get_revision_by_id(self.obj.target)
-
- 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
- """
- return "Revision"
diff --git a/swh/graphql/schema/schema.graphql b/swh/graphql/schema/schema.graphql
index fc78171..f9869ed 100644
--- a/swh/graphql/schema/schema.graphql
+++ b/swh/graphql/schema/schema.graphql
@@ -1,251 +1,253 @@
scalar SWHId
scalar DateTime
scalar DateTimeZone
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 {
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: DateTimeZone
type: String
directory: SWHId
+ parentIds: [SWHId]
+ parents: [Revision]
}
# type ReleaseConnection {
# }
# type ReleasEdge {
# }
type Release implements SWHNode {
id: SWHId!
name: BinaryText
message: BinaryText
author: Person
date: DateTimeZone
}
type Directory implements SWHNode {
id: SWHId!
name: BinaryText
entries: BinaryText # FIXME, change to Union type
}
type Content implements SWHNode {
id: SWHId!
status: String
}
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(
SWHId: String!
): Directory
"""
Get the content with the given swhid
"""
content(
SWHId: String!
): Content
# """
# Search with the given swhid
# """
# searchWithSwhid
}
diff --git a/swh/graphql/utils/paginate.py b/swh/graphql/utils/paginate.py
new file mode 100644
index 0000000..d05d162
--- /dev/null
+++ b/swh/graphql/utils/paginate.py
@@ -0,0 +1,32 @@
+"""
+Pagination at the GraphQL level
+This is a temporary fix and inefficient.
+Should eventually be moved to the
+backend (storage) level
+"""
+
+
+class PaginatedList:
+ def __init__(self, source):
+ """
+ source can be of any iterable type
+ """
+ self.source = source
+
+ def get_items(self, first, after):
+ """
+ Return the 'first' number of
+ items 'after' the given cursor
+ """
+ return self.source[after : (after + first)]
+
+ def get_item_objects(self, first, after):
+ """
+ Return the 'first' number of
+ items 'after' the given cursor
+ with an item cursor
+ """
+ return [
+ {"curosr": first + index, "node": item}
+ for (index, item) in enumerate(self.get_items(first, after), 1)
+ ]
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Aug 19, 12:52 AM (3 w, 2 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3261006
Attached To
rDGQL GraphQL API
Event Timeline
Log In to Comment