Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/sentry/scm/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
CreatePullRequestProtocol,
CreatePullRequestReactionProtocol,
CreateReviewCommentFileProtocol,
CreateReviewCommentMultilineProtocol,
CreateReviewCommentReplyProtocol,
CreateReviewProtocol,
DeleteIssueCommentProtocol,
Expand Down Expand Up @@ -531,6 +532,22 @@ def create_review_comment_file(
return scm.create_review_comment_file(pull_request_id, commit_id, body, path, side)


def create_review_comment_multiline(
scm: CreateReviewCommentMultilineProtocol,
pull_request_id: str,
commit_id: SHA,
body: str,
path: str,
side: ReviewSide,
start_line: int,
end_line: int,
) -> ActionResult[ReviewComment]:
"""Leave a review comment on a line span."""
return scm.create_review_comment_multiline(
pull_request_id, commit_id, body, path, side, start_line, end_line
)


def create_review_comment_reply(
scm: CreateReviewCommentReplyProtocol,
pull_request_id: str,
Expand Down
71 changes: 71 additions & 0 deletions src/sentry/scm/private/github/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import datetime

import msgspec


class GitHubUser(msgspec.Struct, gc=False):
id: int
login: str
type: str | None = None


class GitHubCheckRun(msgspec.Struct, gc=False):
external_id: str
html_url: str


class GitHubIssueComment(msgspec.Struct, gc=False):
id: int
user: GitHubUser | None
body: str | None = None


class GitHubIssueCommentPullRequest(msgspec.Struct, gc=False):
pass


class GitHubIssue(msgspec.Struct, gc=False):
number: int
pull_request: GitHubIssueCommentPullRequest | None = None


class GitHubPullRequest(msgspec.Struct, gc=False):
body: str | None
head: "GitHubPullRequestHead"
base: "GitHubPullRequestBase"
merge_commit_sha: str | None
title: str
user: GitHubUser
merged: bool | None = None


class GitHubPullRequestBase(msgspec.Struct, gc=False):
ref: str
repo: "GitHubPullRequestRepo"
sha: str


class GitHubPullRequestRepo(msgspec.Struct, gc=False):
private: bool


class GitHubPullRequestHead(msgspec.Struct, gc=False):
ref: str
repo: GitHubPullRequestRepo | None
sha: str


class GitHubPullRequestReviewComment(msgspec.Struct, gc=False):
id: int
node_id: str
pull_request_review_id: int
author_association: str
body: str
commit_id: str
diff_hunk: str
html_url: str
original_commit_id: str
path: str
user: GitHubUser | None
created_at: datetime.datetime
updated_at: datetime.datetime
69 changes: 56 additions & 13 deletions src/sentry/scm/private/providers/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from email.utils import format_datetime, parsedate_to_datetime
from typing import Any, cast

import msgspec
import requests

from sentry.integrations.github.client import GitHubApiClient
from sentry.scm.errors import SCMProviderException
from sentry.scm.private.github.types import GitHubPullRequestReviewComment
from sentry.scm.private.rate_limit import (
DynamicRateLimiter,
RateLimitProvider,
Expand Down Expand Up @@ -810,10 +812,31 @@ def create_review_comment_file(
"subject_type": "file",
},
)
return map_action(response, map_review_comment)
return deserialize_action(response, deserialize_pull_request_review_comment)

# create_review_comment_line: not supported
# create_review_comment_multiline: not supported
def create_review_comment_multiline(
self,
pull_request_id: str,
commit_id: SHA,
body: str,
path: str,
side: ReviewSide,
start_line: int,
end_line: int,
) -> ActionResult[ReviewComment]:
"""Leave a review comment on a line span."""
response = self.client.post(
f"/repos/{self.repository['name']}/pulls/{pull_request_id}/comments",
data={
"body": body,
"commit_id": commit_id,
"path": path,
"line": end_line,
"side": side,
"start_line": start_line,
},
)
return deserialize_action(response, deserialize_pull_request_review_comment)

def create_review_comment_reply(
self,
Expand All @@ -829,7 +852,7 @@ def create_review_comment_reply(
"in_reply_to": int(comment_id),
},
)
return map_action(response, map_review_comment)
return deserialize_action(response, deserialize_pull_request_review_comment)

def create_review(
self,
Expand Down Expand Up @@ -1054,15 +1077,6 @@ def map_git_commit_object(raw: dict[str, Any]) -> GitCommitObject:
)


def map_review_comment(raw: dict[str, Any]) -> ReviewComment:
return ReviewComment(
id=str(raw["id"]),
html_url=raw["html_url"],
path=raw["path"],
body=raw["body"],
)


def map_review(raw: dict[str, Any]) -> Review:
return Review(
id=str(raw["id"]),
Expand Down Expand Up @@ -1146,3 +1160,32 @@ def map_paginated_action[T](
"raw": {"data": raw, "headers": dict(response.headers)},
"meta": meta,
}


def deserialize_action[T](response: requests.Response, fn: Callable[[bytes], T]) -> ActionResult[T]:
return {
"data": fn(response.content),
"type": "github",
"raw": {"data": response.json(), "headers": dict(response.headers)},
"meta": _extract_response_meta(response),
}


def deserialize_pull_request_review_comment(content: bytes) -> ReviewComment:
comment = msgspec.json.decode(content, type=GitHubPullRequestReviewComment)
return {
"author_association": comment.author_association,
"author": Author(id=str(comment.user.id), username=comment.user.login)
if comment.user
else None,
"body": comment.body,
"commit_sha": comment.original_commit_id,
"created_at": comment.created_at.isoformat(),
"diff_hunk": comment.diff_hunk,
"file_path": comment.path,
"head": comment.commit_id,
"id": str(comment.id),
"review_id": str(comment.pull_request_review_id),
"unique_id": comment.node_id,
"url": comment.html_url,
}
15 changes: 13 additions & 2 deletions src/sentry/scm/private/providers/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,11 +732,22 @@ def map_tree_entry(raw: dict[str, Any]) -> TreeEntry:

def map_review_comment(discussion_id: str) -> Callable[[dict[str, Any]], ReviewComment]:
def _map_review_comment(raw: dict[str, Any]) -> ReviewComment:
author_raw = raw.get("author")
return ReviewComment(
id=f"{discussion_id}:{raw['id']}",
html_url=None,
path=raw["position"]["new_path"],
unique_id=f"{discussion_id}:{raw['id']}",
url=None,
file_path=raw["position"]["new_path"],
body=raw["body"],
author=Author(id=str(author_raw["id"]), username=author_raw["username"])
if author_raw
else None,
created_at=raw.get("created_at"),
diff_hunk=None,
review_id=None,
author_association=None,
commit_sha=None,
head=None,
)

return _map_review_comment
2 changes: 2 additions & 0 deletions src/sentry/scm/private/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
create_pull_request_reaction,
create_review,
create_review_comment_file,
create_review_comment_multiline,
create_review_comment_reply,
delete_issue_comment,
delete_issue_comment_reaction,
Expand Down Expand Up @@ -150,6 +151,7 @@ def get_extra_fields(self) -> dict[str, Any]:
"create_pull_request_reaction_v1": create_pull_request_reaction,
"create_pull_request_v1": create_pull_request,
"create_review_comment_file_v1": create_review_comment_file,
"create_review_comment_multiline_v1": create_review_comment_multiline,
"create_review_comment_reply_v1": create_review_comment_reply,
"create_review_v1": create_review,
"delete_issue_comment_reaction_v1": delete_issue_comment_reaction,
Expand Down
68 changes: 10 additions & 58 deletions src/sentry/scm/private/webhooks/github.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from typing import Optional

import msgspec

from sentry.scm.private.github.types import (
GitHubCheckRun,
GitHubIssue,
GitHubIssueComment,
GitHubPullRequest,
)
from sentry.scm.types import (
CheckRunAction,
CheckRunEvent,
Expand All @@ -23,73 +27,21 @@
# * "push"


class GitHubUser(msgspec.Struct):
id: int
login: str # Username
type: str | None = None


class GitHubCheckRunEvent(msgspec.Struct, gc=False):
action: CheckRunAction
check_run: "GitHubCheckRun"


class GitHubCheckRun(msgspec.Struct, gc=False):
external_id: str
html_url: str
check_run: GitHubCheckRun


class GitHubIssueCommentEvent(msgspec.Struct, gc=False):
action: CommentAction
comment: "GitHubIssueComment"
issue: "GitHubIssue"


class GitHubIssueComment(msgspec.Struct, gc=False):
id: int
user: GitHubUser | None
body: str | None = None


class GitHubIssueCommentPullRequest(msgspec.Struct, gc=False):
pass


class GitHubIssue(msgspec.Struct, gc=False):
number: int
pull_request: GitHubIssueCommentPullRequest | None = None
comment: GitHubIssueComment
issue: GitHubIssue


class GitHubPullRequestEvent(msgspec.Struct, gc=False):
action: PullRequestAction
number: int
pull_request: "GitHubPullRequest"


class GitHubPullRequest(msgspec.Struct, gc=False):
body: str | None
head: "GitHubPullRequestHead"
base: "GitHubPullRequestBase"
merge_commit_sha: str | None
title: str
user: GitHubUser
merged: bool | None = None


class GitHubPullRequestBase(msgspec.Struct, gc=False):
ref: str
repo: "GitHubPullRequestRepo"
sha: str


class GitHubPullRequestHead(msgspec.Struct, gc=False):
ref: str
repo: Optional["GitHubPullRequestRepo"]
sha: str


class GitHubPullRequestRepo(msgspec.Struct, gc=False):
private: bool
pull_request: GitHubPullRequest


check_run_decoder = msgspec.json.Decoder(GitHubCheckRunEvent)
Expand Down
15 changes: 11 additions & 4 deletions src/sentry/scm/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,17 @@ class ReviewComment(TypedDict):
"""Provider-agnostic representation of a review comment."""

id: ResourceId
html_url: str | None
path: str
unique_id: str | None
url: str | None
file_path: str
body: str
author: Author | None
created_at: str | None
diff_hunk: str | None
review_id: ResourceId | None
author_association: str | None
commit_sha: str | None
head: str | None


class Review(TypedDict):
Expand Down Expand Up @@ -1043,10 +1051,9 @@ def create_review_comment_multiline(
commit_id: SHA,
body: str,
path: str,
side: ReviewSide,
start_line: int,
start_side: ReviewSide,
end_line: int,
end_side: ReviewSide,
) -> ActionResult[ReviewComment]: ...


Expand Down
Loading
Loading