This leaves in place some legacy API use regarding the handling of
ticket references in git commits, which is a bit more complex to address
and will be handled in a follow-up patch.
---
hubsrht/blueprints/mailing_lists.py | 14 +-
hubsrht/blueprints/sources.py | 2 +-
hubsrht/blueprints/trackers.py | 6 +-
hubsrht/services.py | 419 ++++++++++++++++--------
hubsrht/templates/mailing-list-new.html | 2 +-
hubsrht/templates/tracker-new.html | 2 +-
6 files changed, 299 insertions(+), 146 deletions(-)
diff --git a/hubsrht/blueprints/mailing_lists.py b/hubsrht/blueprints/mailing_lists.py
index f6164cb..23c65da 100644
--- a/hubsrht/blueprints/mailing_lists.py
+++ b/hubsrht/blueprints/mailing_lists.py
@@ -93,14 +93,14 @@ Mailing list for end-user discussion and questions related to the
for list_name in template:
desc = descs[list_name]
list_name = list_name.lower() # Per lists.sr.ht naming rules
- try:
- mailing_list = lists.get_list(owner, list_name)
+ mailing_list = lists.get_list(owner, list_name)
+ if mailing_list is not None:
in_project = [l.remote_id for l in (MailingList.query
.filter(MailingList.project_id == project.id)
.filter(MailingList.name == list_name)).all()]
if in_project:
continue
- except:
+ else:
valid = Validation({
"name": list_name,
"description": desc,
@@ -115,7 +115,7 @@ Mailing list for end-user discussion and questions related to the
ml.owner_id = project.owner_id
ml.name = mailing_list["name"]
ml.description = mailing_list["description"]
- if any(mailing_list["permissions"]["nonsubscriber"]):
+ if mailing_list["defaultACL"]["browse"]:
ml.visibility = Visibility.PUBLIC
else:
ml.visibility = Visibility.UNLISTED
@@ -189,7 +189,7 @@ def new_POST(owner, project_name):
ml.owner_id = project.owner_id
ml.name = mailing_list["name"]
ml.description = mailing_list["description"]
- if any(mailing_list["permissions"]["nonsubscriber"]):
+ if mailing_list["defaultACL"]["browse"]:
ml.visibility = Visibility.PUBLIC
else:
ml.visibility = Visibility.UNLISTED
@@ -257,7 +257,7 @@ def delete_POST(owner, project_name, list_id):
if not mailing_list:
abort(404)
- list_name = mailing_list.name
+ list_id = mailing_list.remote_id
lists.unensure_mailing_list_webhooks(mailing_list)
db.session.delete(mailing_list)
db.session.commit()
@@ -265,7 +265,7 @@ def delete_POST(owner, project_name, list_id):
valid = Validation(request)
delete_remote = valid.optional("delete-remote") == "on"
if delete_remote:
- lists.delete_list(owner, list_name)
+ lists.delete_list(owner, list_id)
return redirect(url_for("projects.summary_GET",
owner=owner.canonical_name, project_name=project.name))
diff --git a/hubsrht/blueprints/sources.py b/hubsrht/blueprints/sources.py
index 1e6d004..8ce5795 100644
--- a/hubsrht/blueprints/sources.py
+++ b/hubsrht/blueprints/sources.py
@@ -275,7 +275,7 @@ def delete_POST(owner, project_name, repo_id):
if repo.repo_type == RepoType.git:
git.delete_repo(owner, repo_id)
elif repo.repo_type == RepoType.hg:
- hg.delete_repo(owner, repo_name)
+ hg.delete_repo(owner, repo_id)
else:
assert False
diff --git a/hubsrht/blueprints/trackers.py b/hubsrht/blueprints/trackers.py
index 272a2c3..17a51c4 100644
--- a/hubsrht/blueprints/trackers.py
+++ b/hubsrht/blueprints/trackers.py
@@ -89,7 +89,7 @@ def new_POST(owner, project_name):
tracker.owner_id = owner.id
tracker.name = remote_tracker["name"]
tracker.description = remote_tracker["description"]
- if any(remote_tracker["default_access"]):
+ if remote_tracker["defaultACL"]["browse"]:
tracker.visibility = Visibility.PUBLIC
else:
tracker.visibility = Visibility.UNLISTED
@@ -156,14 +156,14 @@ def delete_POST(owner, project_name, tracker_id):
.filter(Tracker.project_id == project.id)).one_or_none()
if not tracker:
abort(404)
- tracker_name = tracker.name
+ tracker_name = tracker.remote_id
db.session.delete(tracker)
db.session.commit()
valid = Validation(request)
delete_remote = valid.optional("delete-remote") == "on"
if delete_remote:
- todo.delete_tracker(owner, tracker_name)
+ todo.delete_tracker(owner, tracker_id)
return redirect(url_for("projects.summary_GET",
owner=owner.canonical_name, project_name=project.name))
diff --git a/hubsrht/services.py b/hubsrht/services.py
index 76bf568..11262df 100644
--- a/hubsrht/services.py
+++ b/hubsrht/services.py
@@ -355,14 +355,43 @@ class HgService(SrhtService):
super().__init__("hg.sr.ht")
def get_repos(self, user):
- return get_results(f"{_hgsrht}/api/repos", user)
+ repos = self.enumerate(user, """
+ query GetRepos($cursor: Cursor) {
+ me {
+ repositories(cursor: $cursor) {
+ results {
+ id
+ name
+ updated
+ owner {
+ canonicalName
+ }
+ }
+ cursor
+ }
+ }
+ }
+ """, collector=lambda result: result["me"]["repositories"])
+
+ for repo in repos:
+ repo["updated"] = gql_time(repo["updated"])
+
+ return repos
def get_repo(self, user, repo_name):
- r = self.session.get(f"{_hgsrht}/api/repos/{repo_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 200:
- raise Exception(r.text)
- return r.json()
+ resp = self.exec(user, """
+ query GetRepo($repoName: String!) {
+ me {
+ repository(name: $repoName) {
+ id
+ name
+ description
+ visibility
+ }
+ }
+ }
+ """, repoName=repo_name)
+ return resp["me"]["repository"]
def get_readme(self, user, repo_name, repo_url):
# TODO: Cache?
@@ -386,17 +415,35 @@ class HgService(SrhtService):
description = valid.require("description")
if not valid.ok:
return None
- return self.post(user, valid, f"{_hgsrht}/api/repos", {
- "name": name,
- "description": description,
- "visibility": visibility.value,
- })
- def delete_repo(self, user, repo_name):
- r = self.session.delete(f"{_hgsrht}/api/repos/{repo_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 204 and r.status_code != 404:
- raise Exception(r.text)
+ resp = self.exec(user, """
+ mutation CreateRepo(
+ $name: String!,
+ $description: String!,
+ $visibility: Visibility!) {
+ createRepository(name: $name,
+ description: $description,
+ visibility: $visibility) {
+ id, name, description, visibility
+ }
+ }
+ """,
+ name=name,
+ description=description,
+ visibility=visibility.value,
+ valid=valid)
+
+ if not valid.ok:
+ return None
+
+ return resp["createRepository"]
+
+ def delete_repo(self, user, repo_id):
+ self.exec(user, """
+ mutation DeleteRepo($repo_id: Int!) {
+ deleteRepository(id: $repo_id) { id }
+ }
+ """, repo_id=repo_id)
def ensure_user_webhooks(self, user):
config = {
@@ -417,14 +464,119 @@ class ListService(SrhtService):
super().__init__("lists.sr.ht")
def get_lists(self, user):
- return get_results(f"{_listsrht}/api/lists", user)
+ lists = self.enumerate(user, """
+ query GetLists($cursor: Cursor) {
+ me {
+ lists(cursor: $cursor) {
+ results {
+ id
+ name
+ description
+ updated
+ owner {
+ canonicalName
+ }
+ }
+ cursor
+ }
+ }
+ }
+ """,
+ collector=lambda result: result["me"]["lists"])
+
+ for ml in lists:
+ ml["updated"] = gql_time(ml["updated"])
+
+ return lists
def get_list(self, user, list_name):
- r = self.session.get(f"{_listsrht}/api/lists/{list_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 200:
- raise Exception(r.json())
- return r.json()
+ resp = self.exec(user, """
+ query GetList($listName: String!) {
+ me {
+ list(name: $listName) {
+ id
+ name
+ description
+ visibility
+ defaultACL {
+ browse
+ }
+ }
+ }
+ }
+ """, listName=list_name)
+ return resp["me"]["list"]
+
+ def create_list(self, user, valid):
+ name = valid.require("name")
+ description = valid.optional("description")
+ if not valid.ok:
+ return None
+
+ resp = self.exec(user, """
+ mutation CreateList(
+ $name: String!,
+ $description: String!) {
+ createMailingList(name: $name,
+ description: $description,
+ visibility: PUBLIC) {
+ id
+ name
+ description
+ visibility
+ defaultACL {
+ browse
+ }
+ }
+ }
+ """,
+ name=name,
+ description=description,
+ valid=valid)
+
+ if not valid.ok:
+ return None
+
+ return resp["createMailingList"]
+
+ def delete_list(self, user, list_id):
+ resp = self.exec(user, """
+ mutation DeleteList($list_id: Int!) {
+ deleteMailingList(id: $list_id) { id }
+ }
+ """, list_id=list_id)
+
+ def patchset_create_tool(self, user, patchset_id, icon, details):
+ resp = self.exec(user, """
+ mutation CreateTool(
+ $patchset_id: Int!,
+ $details: String!,
+ $icon: ToolIcon!) {
+ createTool(patchsetID: $patchsetID, details: $details, icon: $icon) {
+ id
+ }
+ }
+ """,
+ patchsetID=patchset_id,
+ icon=icon,
+ details=details)
+ return r["createTool"]["id"]
+
+ def patchset_update_tool(self, user, tool_id, icon, details):
+ resp = self.exec(user, """
+ mutation UpdateTool(
+ $toolID: Int!,
+ $details: String!,
+ $icon: ToolIcon!) {
+ updateTool(id: $toolID, details: $details, icon: $icon) {
+ id
+ }
+ }
+ """,
+ toolID=tool_id,
+ icon=icon,
+ details=details)
+ return resp["updateTool"]["id"]
def ensure_mailing_list_webhooks(self, mailing_list):
config = {
@@ -444,92 +596,136 @@ class ListService(SrhtService):
except:
pass # nbd, upstream was presumably deleted
- def create_list(self, user, valid):
+class TodoService(SrhtService):
+ def __init__(self):
+ super().__init__("todo.sr.ht")
+
+ def get_trackers(self, user):
+ trackers = self.enumerate(user, """
+ query GetTrackers($cursor: Cursor) {
+ me {
+ trackers(cursor: $cursor) {
+ results {
+ id
+ name
+ description
+ updated
+ owner {
+ canonicalName
+ }
+ }
+ cursor
+ }
+ }
+ }
+ """, collector=lambda result: result["me"]["trackers"])
+
+ for tr in trackers:
+ tr["updated"] = gql_time(tr["updated"])
+
+ return trackers
+
+ def get_tracker(self, user, tracker_name):
+ resp = self.exec(user, """
+ query GetTracker($trackerName: String!) {
+ me {
+ tracker(name: $trackerName) {
+ id
+ name
+ description
+ visibility
+ defaultACL {
+ browse
+ }
+ }
+ }
+ }
+ """, trackerName=tracker_name)
+ return resp["me"]["tracker"]
+
+ def create_tracker(self, user, valid, visibility):
name = valid.require("name")
description = valid.optional("description")
if not valid.ok:
return None
- return self.post(user, valid, f"{_listsrht}/api/lists", {
- "name": name,
- "description": description,
- })
-
- def delete_list(self, user, list_name):
- r = self.session.delete(f"{_listsrht}/api/lists/{list_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 204 and r.status_code != 404:
- raise Exception(r.text)
- def patchset_create_tool(self, user, patchset_id, icon, details):
- query = """
- mutation CreateTool($id: Int!, $details: String!, $icon: ToolIcon!) {
- createTool(patchsetID: $id, details: $details, icon: $icon) {
+ resp = self.exec(user, """
+ mutation CreateTracker(
+ $name: String!,
+ $description: String!,
+ $visibility: Visibility!) {
+ createTracker(name: $name,
+ description: $description,
+ visibility: $visibility) {
id
+ name
+ description
+ visibility
+ defaultACL {
+ browse
+ }
}
}
- """
- r = self.post(user, None, f"{_listsrht_api}/query", {
- "query": query,
- "variables": {
- "id": patchset_id,
- "icon": icon,
- "details": details,
- },
- })
- if not r["data"] or not r["data"]["createTool"]:
+ """,
+ name=name,
+ description=description,
+ visibility=visibility.value,
+ valid=valid)
+
+ if not valid.ok:
return None
- return r["data"]["createTool"]["id"]
- def patchset_update_tool(self, user, tool_id, icon, details):
+ return resp["createTracker"]
+
+ def delete_tracker(self, user, tracker_id):
+ self.exec(user, """
+ mutation DeleteTracker($trackerID: Int!) {
+ deleteTracker(id: $trackerID) { id }
+ }
+ """, trackerID=tracker_id)
+
+ def get_ticket_comments(self, user, owner, tracker, ticket):
query = """
- mutation UpdateTool($id: Int!, $details: String!, $icon: ToolIcon!) {
- updateTool(id: $id, details: $details, icon: $icon) {
- id
+ query TicketComments($username: String!, $tracker: String!, $ticket: Int!) {
+ user(username: $username) {
+ tracker(name: $tracker) {
+ ticket(id: $ticket) {
+ events {
+ results {
+ changes {
+ ... on Comment {
+ text
+ }
+ }
+ }
+ }
+ }
}
+ }
}
"""
- r = self.post(user, None, f"{_listsrht_api}/query", {
+ r = self.post(user, None, f"{_todosrht_api}/query", {
"query": query,
"variables": {
- "id": tool_id,
- "icon": icon,
- "details": details,
- },
- })
- if not r["data"] or not r["data"]["updateTool"]:
- return None
- return r["data"]["updateTool"]["id"]
-
-class TodoService(SrhtService):
- def __init__(self):
- super().__init__("todo.sr.ht")
-
- def get_trackers(self, user):
- return get_results(f"{_todosrht}/api/trackers", user)
-
- def get_tracker(self, user, tracker_name):
- r = self.session.get(f"{_todosrht}/api/trackers/{tracker_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 200:
- raise Exception(r.json())
- return r.json()
-
- def create_tracker(self, user, valid, visibility):
- name = valid.require("name")
- description = valid.optional("description")
- if not valid.ok:
- return None
- return self.post(user, valid, f"{_todosrht}/api/trackers", {
- "name": name,
- "description": description,
- "visibility": visibility.value.upper(),
+ "username": owner[1:],
+ "tracker": tracker,
+ "ticket": ticket,
+ }
})
+ comments = []
+ for e in r["data"]["user"]["tracker"]["ticket"]["events"]["results"]:
+ for c in e["changes"]:
+ if "text" in c:
+ comments.append(c["text"])
+ return comments
- def delete_tracker(self, user, tracker_name):
- r = self.session.delete(f"{_todosrht}/api/trackers/{tracker_name}",
- headers=encrypt_request_authorization(user))
- if r.status_code != 204 and r.status_code != 404:
- raise Exception(r.text)
+ def update_ticket(self, user, owner, tracker, ticket, comment, resolution=None):
+ url = f"{_todosrht}/api/user/{owner}/trackers/{tracker}/tickets/{ticket}"
+ payload = {"comment": comment}
+ if resolution is not None:
+ payload["resolution"] = resolution
+ payload["status"] = "resolved"
+ self.put(user, None, url, payload)
def ensure_user_webhooks(self, user):
config = {
@@ -583,49 +779,6 @@ class TodoService(SrhtService):
except:
pass # nbd, upstream was presumably deleted
- def get_ticket_comments(self, user, owner, tracker, ticket):
- query = """
- query TicketComments($username: String!, $tracker: String!, $ticket: Int!) {
- user(username: $username) {
- tracker(name: $tracker) {
- ticket(id: $ticket) {
- events {
- results {
- changes {
- ... on Comment {
- text
- }
- }
- }
- }
- }
- }
- }
- }
- """
- r = self.post(user, None, f"{_todosrht_api}/query", {
- "query": query,
- "variables": {
- "username": owner[1:],
- "tracker": tracker,
- "ticket": ticket,
- }
- })
- comments = []
- for e in r["data"]["user"]["tracker"]["ticket"]["events"]["results"]:
- for c in e["changes"]:
- if "text" in c:
- comments.append(c["text"])
- return comments
-
- def update_ticket(self, user, owner, tracker, ticket, comment, resolution=None):
- url = f"{_todosrht}/api/user/{owner}/trackers/{tracker}/tickets/{ticket}"
- payload = {"comment": comment}
- if resolution is not None:
- payload["resolution"] = resolution
- payload["status"] = "resolved"
- self.put(user, None, url, payload)
-
class BuildService(SrhtService):
def __init__(self):
super().__init__("builds.sr.ht")
diff --git a/hubsrht/templates/mailing-list-new.html b/hubsrht/templates/mailing-list-new.html
index 08452ab..aacb994 100644
--- a/hubsrht/templates/mailing-list-new.html
+++ b/hubsrht/templates/mailing-list-new.html
@@ -142,7 +142,7 @@
{% endif %}
<a
href="{{get_origin("lists.sr.ht",
- external=True)}}/{{ list["owner"]["canonical_name"] }}/{{list["name"]}}"
+ external=True)}}/{{ list["owner"]["canonicalName"] }}/{{list["name"]}}"
target="_blank"
rel="noopener"
>{{ list["name"] }}</a>
diff --git a/hubsrht/templates/tracker-new.html b/hubsrht/templates/tracker-new.html
index 61e2397..4045749 100644
--- a/hubsrht/templates/tracker-new.html
+++ b/hubsrht/templates/tracker-new.html
@@ -78,7 +78,7 @@
{% endif %}
<a
href="{{get_origin("todo.sr.ht",
- external=True)}}/{{ tracker["owner"]["canonical_name"] }}/{{tracker["name"]}}"
+ external=True)}}/{{ tracker["owner"]["canonicalName"] }}/{{tracker["name"]}}"
target="_blank"
rel="noopener"
>{{ tracker["name"] }}</a>
--
2.46.0
LGTM. Re the commit message, I'll just point out that it also leaves in
place using the legacy API for lists.ensure_webhooks, but I suppose that
cannot be changed because this is about legacy webhooks?
Either way, ship it.
On 8/27/24 10:54 AM, Drew DeVault wrote:
> This leaves in place some legacy API use regarding the handling of
> ticket references in git commits, which is a bit more complex to address
> and will be handled in a follow-up patch.
> ---
> hubsrht/blueprints/mailing_lists.py | 14 +-
> hubsrht/blueprints/sources.py | 2 +-
> hubsrht/blueprints/trackers.py | 6 +-
> hubsrht/services.py | 419 ++++++++++++++++--------
> hubsrht/templates/mailing-list-new.html | 2 +-
> hubsrht/templates/tracker-new.html | 2 +-
> 6 files changed, 299 insertions(+), 146 deletions(-)
>
> diff --git a/hubsrht/blueprints/mailing_lists.py b/hubsrht/blueprints/mailing_lists.py
> index f6164cb..23c65da 100644
> --- a/hubsrht/blueprints/mailing_lists.py
> +++ b/hubsrht/blueprints/mailing_lists.py
> @@ -93,14 +93,14 @@ Mailing list for end-user discussion and questions related to the
> for list_name in template:
> desc = descs[list_name]
> list_name = list_name.lower() # Per lists.sr.ht naming rules
> - try:
> - mailing_list = lists.get_list(owner, list_name)
> + mailing_list = lists.get_list(owner, list_name)
> + if mailing_list is not None:
> in_project = [l.remote_id for l in (MailingList.query
> .filter(MailingList.project_id == project.id)
> .filter(MailingList.name == list_name)).all()]
> if in_project:
> continue
> - except:
> + else:
> valid = Validation({
> "name": list_name,
> "description": desc,
> @@ -115,7 +115,7 @@ Mailing list for end-user discussion and questions related to the
> ml.owner_id = project.owner_id
> ml.name = mailing_list["name"]
> ml.description = mailing_list["description"]
> - if any(mailing_list["permissions"]["nonsubscriber"]):
> + if mailing_list["defaultACL"]["browse"]:
> ml.visibility = Visibility.PUBLIC
> else:
> ml.visibility = Visibility.UNLISTED
> @@ -189,7 +189,7 @@ def new_POST(owner, project_name):
> ml.owner_id = project.owner_id
> ml.name = mailing_list["name"]
> ml.description = mailing_list["description"]
> - if any(mailing_list["permissions"]["nonsubscriber"]):
> + if mailing_list["defaultACL"]["browse"]:
> ml.visibility = Visibility.PUBLIC
> else:
> ml.visibility = Visibility.UNLISTED
> @@ -257,7 +257,7 @@ def delete_POST(owner, project_name, list_id):
> if not mailing_list:
> abort(404)
>
> - list_name = mailing_list.name
> + list_id = mailing_list.remote_id
> lists.unensure_mailing_list_webhooks(mailing_list)
> db.session.delete(mailing_list)
> db.session.commit()
> @@ -265,7 +265,7 @@ def delete_POST(owner, project_name, list_id):
> valid = Validation(request)
> delete_remote = valid.optional("delete-remote") == "on"
> if delete_remote:
> - lists.delete_list(owner, list_name)
> + lists.delete_list(owner, list_id)
>
> return redirect(url_for("projects.summary_GET",
> owner=owner.canonical_name, project_name=project.name))
> diff --git a/hubsrht/blueprints/sources.py b/hubsrht/blueprints/sources.py
> index 1e6d004..8ce5795 100644
> --- a/hubsrht/blueprints/sources.py
> +++ b/hubsrht/blueprints/sources.py
> @@ -275,7 +275,7 @@ def delete_POST(owner, project_name, repo_id):
> if repo.repo_type == RepoType.git:
> git.delete_repo(owner, repo_id)
> elif repo.repo_type == RepoType.hg:
> - hg.delete_repo(owner, repo_name)
> + hg.delete_repo(owner, repo_id)
> else:
> assert False
>
> diff --git a/hubsrht/blueprints/trackers.py b/hubsrht/blueprints/trackers.py
> index 272a2c3..17a51c4 100644
> --- a/hubsrht/blueprints/trackers.py
> +++ b/hubsrht/blueprints/trackers.py
> @@ -89,7 +89,7 @@ def new_POST(owner, project_name):
> tracker.owner_id = owner.id
> tracker.name = remote_tracker["name"]
> tracker.description = remote_tracker["description"]
> - if any(remote_tracker["default_access"]):
> + if remote_tracker["defaultACL"]["browse"]:
> tracker.visibility = Visibility.PUBLIC
> else:
> tracker.visibility = Visibility.UNLISTED
> @@ -156,14 +156,14 @@ def delete_POST(owner, project_name, tracker_id):
> .filter(Tracker.project_id == project.id)).one_or_none()
> if not tracker:
> abort(404)
> - tracker_name = tracker.name
> + tracker_name = tracker.remote_id
> db.session.delete(tracker)
> db.session.commit()
>
> valid = Validation(request)
> delete_remote = valid.optional("delete-remote") == "on"
> if delete_remote:
> - todo.delete_tracker(owner, tracker_name)
> + todo.delete_tracker(owner, tracker_id)
>
> return redirect(url_for("projects.summary_GET",
> owner=owner.canonical_name, project_name=project.name))
> diff --git a/hubsrht/services.py b/hubsrht/services.py
> index 76bf568..11262df 100644
> --- a/hubsrht/services.py
> +++ b/hubsrht/services.py
> @@ -355,14 +355,43 @@ class HgService(SrhtService):
> super().__init__("hg.sr.ht")
>
> def get_repos(self, user):
> - return get_results(f"{_hgsrht}/api/repos", user)
> + repos = self.enumerate(user, """
> + query GetRepos($cursor: Cursor) {
> + me {
> + repositories(cursor: $cursor) {
> + results {
> + id
> + name
> + updated
> + owner {
> + canonicalName
> + }
> + }
> + cursor
> + }
> + }
> + }
> + """, collector=lambda result: result["me"]["repositories"])
> +
> + for repo in repos:
> + repo["updated"] = gql_time(repo["updated"])
> +
> + return repos
>
> def get_repo(self, user, repo_name):
> - r = self.session.get(f"{_hgsrht}/api/repos/{repo_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 200:
> - raise Exception(r.text)
> - return r.json()
> + resp = self.exec(user, """
> + query GetRepo($repoName: String!) {
> + me {
> + repository(name: $repoName) {
> + id
> + name
> + description
> + visibility
> + }
> + }
> + }
> + """, repoName=repo_name)
> + return resp["me"]["repository"]
>
> def get_readme(self, user, repo_name, repo_url):
> # TODO: Cache?
> @@ -386,17 +415,35 @@ class HgService(SrhtService):
> description = valid.require("description")
> if not valid.ok:
> return None
> - return self.post(user, valid, f"{_hgsrht}/api/repos", {
> - "name": name,
> - "description": description,
> - "visibility": visibility.value,
> - })
>
> - def delete_repo(self, user, repo_name):
> - r = self.session.delete(f"{_hgsrht}/api/repos/{repo_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 204 and r.status_code != 404:
> - raise Exception(r.text)
> + resp = self.exec(user, """
> + mutation CreateRepo(
> + $name: String!,
> + $description: String!,
> + $visibility: Visibility!) {
> + createRepository(name: $name,
> + description: $description,
> + visibility: $visibility) {
> + id, name, description, visibility
> + }
> + }
> + """,
> + name=name,
> + description=description,
> + visibility=visibility.value,
> + valid=valid)
> +
> + if not valid.ok:
> + return None
> +
> + return resp["createRepository"]
> +
> + def delete_repo(self, user, repo_id):
> + self.exec(user, """
> + mutation DeleteRepo($repo_id: Int!) {
> + deleteRepository(id: $repo_id) { id }
> + }
> + """, repo_id=repo_id)
>
> def ensure_user_webhooks(self, user):
> config = {
> @@ -417,14 +464,119 @@ class ListService(SrhtService):
> super().__init__("lists.sr.ht")
>
> def get_lists(self, user):
> - return get_results(f"{_listsrht}/api/lists", user)
> + lists = self.enumerate(user, """
> + query GetLists($cursor: Cursor) {
> + me {
> + lists(cursor: $cursor) {
> + results {
> + id
> + name
> + description
> + updated
> + owner {
> + canonicalName
> + }
> + }
> + cursor
> + }
> + }
> + }
> + """,
> + collector=lambda result: result["me"]["lists"])
> +
> + for ml in lists:
> + ml["updated"] = gql_time(ml["updated"])
> +
> + return lists
>
> def get_list(self, user, list_name):
> - r = self.session.get(f"{_listsrht}/api/lists/{list_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 200:
> - raise Exception(r.json())
> - return r.json()
> + resp = self.exec(user, """
> + query GetList($listName: String!) {
> + me {
> + list(name: $listName) {
> + id
> + name
> + description
> + visibility
> + defaultACL {
> + browse
> + }
> + }
> + }
> + }
> + """, listName=list_name)
> + return resp["me"]["list"]
> +
> + def create_list(self, user, valid):
> + name = valid.require("name")
> + description = valid.optional("description")
> + if not valid.ok:
> + return None
> +
> + resp = self.exec(user, """
> + mutation CreateList(
> + $name: String!,
> + $description: String!) {
> + createMailingList(name: $name,
> + description: $description,
> + visibility: PUBLIC) {
> + id
> + name
> + description
> + visibility
> + defaultACL {
> + browse
> + }
> + }
> + }
> + """,
> + name=name,
> + description=description,
> + valid=valid)
> +
> + if not valid.ok:
> + return None
> +
> + return resp["createMailingList"]
> +
> + def delete_list(self, user, list_id):
> + resp = self.exec(user, """
> + mutation DeleteList($list_id: Int!) {
> + deleteMailingList(id: $list_id) { id }
> + }
> + """, list_id=list_id)
> +
> + def patchset_create_tool(self, user, patchset_id, icon, details):
> + resp = self.exec(user, """
> + mutation CreateTool(
> + $patchset_id: Int!,
> + $details: String!,
> + $icon: ToolIcon!) {
> + createTool(patchsetID: $patchsetID, details: $details, icon: $icon) {
> + id
> + }
> + }
> + """,
> + patchsetID=patchset_id,
> + icon=icon,
> + details=details)
> + return r["createTool"]["id"]
> +
> + def patchset_update_tool(self, user, tool_id, icon, details):
> + resp = self.exec(user, """
> + mutation UpdateTool(
> + $toolID: Int!,
> + $details: String!,
> + $icon: ToolIcon!) {
> + updateTool(id: $toolID, details: $details, icon: $icon) {
> + id
> + }
> + }
> + """,
> + toolID=tool_id,
> + icon=icon,
> + details=details)
> + return resp["updateTool"]["id"]
>
> def ensure_mailing_list_webhooks(self, mailing_list):
> config = {
> @@ -444,92 +596,136 @@ class ListService(SrhtService):
> except:
> pass # nbd, upstream was presumably deleted
>
> - def create_list(self, user, valid):
> +class TodoService(SrhtService):
> + def __init__(self):
> + super().__init__("todo.sr.ht")
> +
> + def get_trackers(self, user):
> + trackers = self.enumerate(user, """
> + query GetTrackers($cursor: Cursor) {
> + me {
> + trackers(cursor: $cursor) {
> + results {
> + id
> + name
> + description
> + updated
> + owner {
> + canonicalName
> + }
> + }
> + cursor
> + }
> + }
> + }
> + """, collector=lambda result: result["me"]["trackers"])
> +
> + for tr in trackers:
> + tr["updated"] = gql_time(tr["updated"])
> +
> + return trackers
> +
> + def get_tracker(self, user, tracker_name):
> + resp = self.exec(user, """
> + query GetTracker($trackerName: String!) {
> + me {
> + tracker(name: $trackerName) {
> + id
> + name
> + description
> + visibility
> + defaultACL {
> + browse
> + }
> + }
> + }
> + }
> + """, trackerName=tracker_name)
> + return resp["me"]["tracker"]
> +
> + def create_tracker(self, user, valid, visibility):
> name = valid.require("name")
> description = valid.optional("description")
> if not valid.ok:
> return None
> - return self.post(user, valid, f"{_listsrht}/api/lists", {
> - "name": name,
> - "description": description,
> - })
> -
> - def delete_list(self, user, list_name):
> - r = self.session.delete(f"{_listsrht}/api/lists/{list_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 204 and r.status_code != 404:
> - raise Exception(r.text)
>
> - def patchset_create_tool(self, user, patchset_id, icon, details):
> - query = """
> - mutation CreateTool($id: Int!, $details: String!, $icon: ToolIcon!) {
> - createTool(patchsetID: $id, details: $details, icon: $icon) {
> + resp = self.exec(user, """
> + mutation CreateTracker(
> + $name: String!,
> + $description: String!,
> + $visibility: Visibility!) {
> + createTracker(name: $name,
> + description: $description,
> + visibility: $visibility) {
> id
> + name
> + description
> + visibility
> + defaultACL {
> + browse
> + }
> }
> }
> - """
> - r = self.post(user, None, f"{_listsrht_api}/query", {
> - "query": query,
> - "variables": {
> - "id": patchset_id,
> - "icon": icon,
> - "details": details,
> - },
> - })
> - if not r["data"] or not r["data"]["createTool"]:
> + """,
> + name=name,
> + description=description,
> + visibility=visibility.value,
> + valid=valid)
> +
> + if not valid.ok:
> return None
> - return r["data"]["createTool"]["id"]
>
> - def patchset_update_tool(self, user, tool_id, icon, details):
> + return resp["createTracker"]
> +
> + def delete_tracker(self, user, tracker_id):
> + self.exec(user, """
> + mutation DeleteTracker($trackerID: Int!) {
> + deleteTracker(id: $trackerID) { id }
> + }
> + """, trackerID=tracker_id)
> +
> + def get_ticket_comments(self, user, owner, tracker, ticket):
> query = """
> - mutation UpdateTool($id: Int!, $details: String!, $icon: ToolIcon!) {
> - updateTool(id: $id, details: $details, icon: $icon) {
> - id
> + query TicketComments($username: String!, $tracker: String!, $ticket: Int!) {
> + user(username: $username) {
> + tracker(name: $tracker) {
> + ticket(id: $ticket) {
> + events {
> + results {
> + changes {
> + ... on Comment {
> + text
> + }
> + }
> + }
> + }
> + }
> }
> + }
> }
> """
> - r = self.post(user, None, f"{_listsrht_api}/query", {
> + r = self.post(user, None, f"{_todosrht_api}/query", {
> "query": query,
> "variables": {
> - "id": tool_id,
> - "icon": icon,
> - "details": details,
> - },
> - })
> - if not r["data"] or not r["data"]["updateTool"]:
> - return None
> - return r["data"]["updateTool"]["id"]
> -
> -class TodoService(SrhtService):
> - def __init__(self):
> - super().__init__("todo.sr.ht")
> -
> - def get_trackers(self, user):
> - return get_results(f"{_todosrht}/api/trackers", user)
> -
> - def get_tracker(self, user, tracker_name):
> - r = self.session.get(f"{_todosrht}/api/trackers/{tracker_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 200:
> - raise Exception(r.json())
> - return r.json()
> -
> - def create_tracker(self, user, valid, visibility):
> - name = valid.require("name")
> - description = valid.optional("description")
> - if not valid.ok:
> - return None
> - return self.post(user, valid, f"{_todosrht}/api/trackers", {
> - "name": name,
> - "description": description,
> - "visibility": visibility.value.upper(),
> + "username": owner[1:],
> + "tracker": tracker,
> + "ticket": ticket,
> + }
> })
> + comments = []
> + for e in r["data"]["user"]["tracker"]["ticket"]["events"]["results"]:
> + for c in e["changes"]:
> + if "text" in c:
> + comments.append(c["text"])
> + return comments
>
> - def delete_tracker(self, user, tracker_name):
> - r = self.session.delete(f"{_todosrht}/api/trackers/{tracker_name}",
> - headers=encrypt_request_authorization(user))
> - if r.status_code != 204 and r.status_code != 404:
> - raise Exception(r.text)
> + def update_ticket(self, user, owner, tracker, ticket, comment, resolution=None):
> + url = f"{_todosrht}/api/user/{owner}/trackers/{tracker}/tickets/{ticket}"
> + payload = {"comment": comment}
> + if resolution is not None:
> + payload["resolution"] = resolution
> + payload["status"] = "resolved"
> + self.put(user, None, url, payload)
>
> def ensure_user_webhooks(self, user):
> config = {
> @@ -583,49 +779,6 @@ class TodoService(SrhtService):
> except:
> pass # nbd, upstream was presumably deleted
>
> - def get_ticket_comments(self, user, owner, tracker, ticket):
> - query = """
> - query TicketComments($username: String!, $tracker: String!, $ticket: Int!) {
> - user(username: $username) {
> - tracker(name: $tracker) {
> - ticket(id: $ticket) {
> - events {
> - results {
> - changes {
> - ... on Comment {
> - text
> - }
> - }
> - }
> - }
> - }
> - }
> - }
> - }
> - """
> - r = self.post(user, None, f"{_todosrht_api}/query", {
> - "query": query,
> - "variables": {
> - "username": owner[1:],
> - "tracker": tracker,
> - "ticket": ticket,
> - }
> - })
> - comments = []
> - for e in r["data"]["user"]["tracker"]["ticket"]["events"]["results"]:
> - for c in e["changes"]:
> - if "text" in c:
> - comments.append(c["text"])
> - return comments
> -
> - def update_ticket(self, user, owner, tracker, ticket, comment, resolution=None):
> - url = f"{_todosrht}/api/user/{owner}/trackers/{tracker}/tickets/{ticket}"
> - payload = {"comment": comment}
> - if resolution is not None:
> - payload["resolution"] = resolution
> - payload["status"] = "resolved"
> - self.put(user, None, url, payload)
> -
> class BuildService(SrhtService):
> def __init__(self):
> super().__init__("builds.sr.ht")
> diff --git a/hubsrht/templates/mailing-list-new.html b/hubsrht/templates/mailing-list-new.html
> index 08452ab..aacb994 100644
> --- a/hubsrht/templates/mailing-list-new.html
> +++ b/hubsrht/templates/mailing-list-new.html
> @@ -142,7 +142,7 @@
> {% endif %}
> <a
> href="{{get_origin("lists.sr.ht",
> - external=True)}}/{{ list["owner"]["canonical_name"] }}/{{list["name"]}}"
> + external=True)}}/{{ list["owner"]["canonicalName"] }}/{{list["name"]}}"
> target="_blank"
> rel="noopener"
> >{{ list["name"] }}</a>
> diff --git a/hubsrht/templates/tracker-new.html b/hubsrht/templates/tracker-new.html
> index 61e2397..4045749 100644
> --- a/hubsrht/templates/tracker-new.html
> +++ b/hubsrht/templates/tracker-new.html
> @@ -78,7 +78,7 @@
> {% endif %}
> <a
> href="{{get_origin("todo.sr.ht",
> - external=True)}}/{{ tracker["owner"]["canonical_name"] }}/{{tracker["name"]}}"
> + external=True)}}/{{ tracker["owner"]["canonicalName"] }}/{{tracker["name"]}}"
> target="_blank"
> rel="noopener"
> >{{ tracker["name"] }}</a>