-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Forgejo Issues support #896
base: main
Are you sure you want to change the base?
Changes from all commits
260d75c
a12a3ce
1ff98da
e9c9a8c
f44db83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Copyright Contributors to the Packit project. | ||
# SPDX-License-Identifier: MIT | ||
|
||
import datetime | ||
|
||
from pyforgejo.types.comment import Comment as FComment | ||
|
||
from ogr.abstract import Comment, IssueComment, PRComment | ||
|
||
|
||
class ForgejoComment(Comment): | ||
def _from_raw_comment(self, raw_comment: FComment) -> None: | ||
self._raw_comment = raw_comment | ||
|
||
@property | ||
def body(self) -> str: | ||
return self._raw_comment.body | ||
|
||
@property | ||
def edited(self) -> datetime.datetime: | ||
return self._raw_comment.updated_at | ||
|
||
|
||
class ForgejoIssueComment(ForgejoComment, IssueComment): | ||
def __str__(self): | ||
return "Forgejo" + super().__str__() | ||
|
||
|
||
class ForgejoPRComment(ForgejoComment, PRComment): | ||
def __str__(self): | ||
return "Forgejo" + super().__str__() |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,10 +1,167 @@ | ||||||
# Copyright Contributors to the Packit project. | ||||||
# SPDX-License-Identifier: MIT | ||||||
from datetime import datetime | ||||||
from typing import Optional, Union | ||||||
|
||||||
import pyforgejo.types.issue as _issue | ||||||
|
||||||
from ogr.abstract import Issue, IssueComment, IssueStatus | ||||||
from ogr.exceptions import IssueTrackerDisabled, OperationNotSupported | ||||||
from ogr.services import forgejo | ||||||
from ogr.services.base import BaseIssue | ||||||
from ogr.services.forgejo.comments import ForgejoIssueComment | ||||||
|
||||||
|
||||||
class ForgejoIssue(BaseIssue): | ||||||
def __init__(self, raw_issue, project: "forgejo.ForgejoProject"): | ||||||
project: "forgejo.ForgejoProject" | ||||||
|
||||||
def __init__(self, raw_issue: _issue, project: "forgejo.ForgejoProject"): | ||||||
super().__init__(raw_issue, project) | ||||||
self._raw_issue = raw_issue | ||||||
|
||||||
@property | ||||||
def _index(self): | ||||||
return self._raw_issue.number | ||||||
|
||||||
@property | ||||||
def title(self) -> str: | ||||||
return self._raw_issue.title | ||||||
|
||||||
@title.setter | ||||||
def title(self, new_title: str) -> None: | ||||||
self._issue_api_call( | ||||||
self.project.service.api.issue.edit_issue, | ||||||
title=new_title, | ||||||
) | ||||||
|
||||||
@property | ||||||
def id(self) -> int: | ||||||
return self._raw_issue.id | ||||||
|
||||||
@property | ||||||
def url(self) -> str: | ||||||
return self._raw_issue.url | ||||||
|
||||||
@property | ||||||
def description(self) -> str: | ||||||
return self._raw_issue.body | ||||||
|
||||||
@property | ||||||
def author(self) -> str: | ||||||
return self._raw_issue.user.login | ||||||
|
||||||
@property | ||||||
def created(self) -> datetime: | ||||||
return self._raw_issue.created_at | ||||||
|
||||||
@property | ||||||
def status(self) -> IssueStatus: | ||||||
return IssueStatus[self._raw_issue.state] | ||||||
|
||||||
@property | ||||||
def assignees(self) -> list: | ||||||
try: | ||||||
return self._raw_issue.assignees | ||||||
except AttributeError: | ||||||
return None | ||||||
|
||||||
@staticmethod | ||||||
def create( | ||||||
project: "forgejo.ForgejoProject", | ||||||
title: str, | ||||||
body: str, | ||||||
private: Optional[bool] = None, | ||||||
labels: Optional[list[str]] = None, | ||||||
assignees: Optional[list[str]] = None, | ||||||
) -> "Issue": | ||||||
|
||||||
if private: | ||||||
raise NotImplementedError() | ||||||
if not project.has_issues: | ||||||
raise IssueTrackerDisabled() | ||||||
|
||||||
issue = project.service.api.issue.create_issue( | ||||||
owner=project.namespace, | ||||||
repo=project.repo, | ||||||
title=title, | ||||||
body=body, | ||||||
labels=labels, | ||||||
assignees=assignees, | ||||||
) | ||||||
return ForgejoIssue(issue, project) | ||||||
|
||||||
@staticmethod | ||||||
def get(project: "forgejo.ForgejoProject", issue_id: int) -> _issue: | ||||||
if not project.has_issues: | ||||||
raise IssueTrackerDisabled | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
try: | ||||||
issue = project.service.api.issue.get_issue( | ||||||
owner=project.namespace, repo=project.repo, index=issue_id, | ||||||
) | ||||||
except Exception as ex: | ||||||
raise OperationNotSupported(f"Issue {issue_id} not found") from ex | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. operation not supported when the issue is not found? |
||||||
return ForgejoIssue(issue, project) | ||||||
|
||||||
@staticmethod | ||||||
def get_list( | ||||||
project: "forgejo.ForgejoProject", | ||||||
status: IssueStatus = IssueStatus.open, | ||||||
author: Optional[str] = None, | ||||||
assignee: Optional[str] = None, | ||||||
labels: Optional[list[str]] = None, | ||||||
) -> list["Issue"]: | ||||||
if not project.has_issues: | ||||||
raise IssueTrackerDisabled | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
parameters: dict[str, Union[str, list[str], bool]] = { | ||||||
"state": status if status != IssueStatus.open else "open", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure if this produces what you think it does… looking at the definition of |
||||||
"type": "issues", | ||||||
} | ||||||
if author: | ||||||
parameters["created_by"] = author | ||||||
if assignee: | ||||||
parameters["assigned_by"] = assignee | ||||||
if labels: | ||||||
parameters["labels"] = labels | ||||||
|
||||||
issues = project.service.api.issue.list_issues( | ||||||
owner=project.namespace, repo=project.repo, **parameters, | ||||||
) | ||||||
return [ForgejoIssue(issue, project) for issue in issues] | ||||||
Comment on lines
+128
to
+131
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part here is a bit tricky… See packit/packit#2543 (comment) and related notes…
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Totally missed it, will do the changes |
||||||
|
||||||
def close(self) -> "Issue": | ||||||
self._issue_api_call( | ||||||
self.project.service.api.issue.edit_issue, | ||||||
state="closed", | ||||||
) | ||||||
return self | ||||||
|
||||||
def add_label(self, *labels: str) -> None: | ||||||
self._issue_api_call( | ||||||
self.project.service.api.issue.add_label, | ||||||
labels=list(labels), | ||||||
) | ||||||
|
||||||
def comment(self, body: str) -> IssueComment: | ||||||
comment = self._issue_api_call( | ||||||
self.project.service.api.issue.create_comment, | ||||||
body=body, | ||||||
) | ||||||
return ForgejoIssueComment(self, comment) | ||||||
|
||||||
def _get_all_comments(self) -> list[IssueComment]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here with the pagination |
||||||
comments = self._issue_api_call(self.project.service.api.issue.get_comments) | ||||||
return [ | ||||||
ForgejoIssueComment(raw_comment=comment, parent=self) | ||||||
for comment in comments | ||||||
] | ||||||
|
||||||
def _issue_api_call(self, method, **kwargs): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eh… OK… I would probably even go with something like:
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m still new to this and learning the ropes, so I appreciate your patience. I’ll implement the suggested changes and do my best to improve, though there might still be a few rookie mistakes along the way. Thanks for understanding! Thanks for the detailed review means a lot ! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No problem 😁 |
||||||
params = { | ||||||
"owner": self.project.namespace, | ||||||
"repo": self.project.repo, | ||||||
"index": self._index, | ||||||
} | ||||||
params.update(kwargs) | ||||||
return method(**params) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure this maps as it should?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did manually test this, looked at generated openapi spec from forgejo and had similar doubts, I will check again and add some checks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you cover this with tests after, it should be fine.