Просмотр исходного кода

feat(open-pr-comments): queue task in webhook (#60656)

Cathy Teng 1 год назад
Родитель
Сommit
e78a926f12

+ 20 - 2
src/sentry/integrations/github/webhook.py

@@ -14,7 +14,7 @@ from django.utils.decorators import method_decorator
 from django.views.decorators.csrf import csrf_exempt
 from rest_framework.request import Request
 
-from sentry import analytics, options
+from sentry import analytics, features, options
 from sentry.api.api_publish_status import ApiPublishStatus
 from sentry.api.base import Endpoint, all_silo_endpoint
 from sentry.constants import EXTENSION_LANGUAGE_MAP, ObjectStatus
@@ -40,6 +40,7 @@ from sentry.services.hybrid_cloud.organization.serial import serialize_rpc_organ
 from sentry.services.hybrid_cloud.user.service import user_service
 from sentry.shared_integrations.exceptions import ApiError
 from sentry.silo import SiloMode
+from sentry.tasks.integrations.github.open_pr_comment import open_pr_comment_workflow
 from sentry.utils import json, metrics
 from sentry.utils.json import JSONData
 
@@ -450,6 +451,7 @@ class PullRequestEventWebhook(Webhook):
         title = pull_request["title"]
         body = pull_request["body"]
         user = pull_request["user"]
+        action = event["action"]
 
         """
         The value of the merge_commit_sha attribute changes depending on the
@@ -503,7 +505,7 @@ class PullRequestEventWebhook(Webhook):
 
         author.preload_users()
         try:
-            PullRequest.objects.update_or_create(
+            pr, created = PullRequest.objects.update_or_create(
                 organization_id=organization.id,
                 repository_id=repo.id,
                 key=number,
@@ -515,6 +517,22 @@ class PullRequestEventWebhook(Webhook):
                     "merge_commit_sha": merge_commit_sha,
                 },
             )
+
+            if action == "opened" and created:
+                if not features.has("organizations:integrations-open-pr-comment", organization):
+                    logger.info(
+                        "github.open_pr_comment.flag_missing",
+                        extra={"organization_id": organization.id},
+                    )
+                    return
+
+                metrics.incr("github.open_pr_comment.queue_task")
+                logger.info(
+                    "github.open_pr_comment.queue_task",
+                    extra={"pr_id": pr.id},
+                )
+                open_pr_comment_workflow.delay(pr_id=pr.id)
+
         except IntegrityError:
             pass
 

+ 4 - 1
src/sentry/tasks/integrations/github/open_pr_comment.py

@@ -31,6 +31,7 @@ from sentry.tasks.integrations.github.pr_comment import (
     PullRequestIssue,
     create_or_update_comment,
     format_comment_url,
+    get_pr_comment,
 )
 from sentry.templatetags.sentry_helpers import small_count
 from sentry.types.referrer_ids import GITHUB_OPEN_PR_BOT_REFERRER
@@ -363,9 +364,11 @@ def open_pr_comment_workflow(pr_id: int) -> None:
     issue_list: List[Dict[str, Any]] = list(itertools.chain.from_iterable(top_issues_per_file))
     issue_id_list: List[int] = [issue["group_id"] for issue in issue_list]
 
+    pr_comment = get_pr_comment(pr_id, comment_type=CommentType.OPEN_PR)
+
     try:
         create_or_update_comment(
-            pr_comment=None,
+            pr_comment=pr_comment,
             client=client,
             repo=repo,
             pr_key=pull_request.key,

+ 8 - 6
src/sentry/tasks/integrations/github/pr_comment.py

@@ -147,6 +147,13 @@ def get_comment_contents(issue_list: List[int]) -> List[PullRequestIssue]:
     ]
 
 
+def get_pr_comment(pr_id: int, comment_type: int) -> PullRequestComment | None:
+    pr_comment_query = PullRequestComment.objects.filter(
+        pull_request__id=pr_id, comment_type=comment_type
+    )
+    return pr_comment_query[0] if pr_comment_query.exists() else None
+
+
 def create_or_update_comment(
     pr_comment: PullRequestComment | None,
     client: GitHubAppsClient,
@@ -213,12 +220,7 @@ def github_comment_workflow(pullrequest_id: int, project_id: int):
         logger.info("github.pr_comment.option_missing", extra={"organization_id": org_id})
         return
 
-    pr_comment = None
-    pr_comment_query = PullRequestComment.objects.filter(
-        pull_request__id=pullrequest_id, comment_type=CommentType.MERGED_PR
-    )
-    if pr_comment_query.exists():
-        pr_comment = pr_comment_query[0]
+    pr_comment = get_pr_comment(pr_id=pullrequest_id, comment_type=CommentType.MERGED_PR)
 
     try:
         project = Project.objects.get_from_cache(id=project_id)

+ 27 - 2
tests/sentry/integrations/github/test_webhooks.py

@@ -26,6 +26,7 @@ from sentry.models.pullrequest import PullRequest
 from sentry.models.repository import Repository
 from sentry.silo import SiloMode
 from sentry.testutils.cases import APITestCase
+from sentry.testutils.helpers.features import with_feature
 from sentry.testutils.silo import (
     all_silo_test,
     assume_test_silo_mode,
@@ -524,7 +525,9 @@ class PullRequestEventWebhook(APITestCase):
 
         assert response.status_code == 204
 
-    def test_opened(self):
+    @with_feature("organizations:integrations-open-pr-comment")
+    @patch("sentry.integrations.github.webhook.metrics")
+    def test_opened(self, mock_metrics):
         project = self.project  # force creation
         group = self.create_group(project=project, short_id=7)
 
@@ -555,6 +558,24 @@ class PullRequestEventWebhook(APITestCase):
 
         self.assert_group_link(group, pr)
 
+        mock_metrics.incr.assert_called_with("github.open_pr_comment.queue_task")
+
+    @patch("sentry.integrations.github.webhook.metrics")
+    def test_opened_missing_feature_flag(self, mock_metrics):
+        project = self.project  # force creation
+        self.create_group(project=project, short_id=7)
+
+        Repository.objects.create(
+            organization_id=project.organization.id,
+            external_id="35129377",
+            provider="integrations:github",
+            name="baxterthehacker/public-repo",
+        )
+
+        self._setup_repo_test(project)
+
+        assert mock_metrics.incr.call_count == 0
+
     @patch("sentry.integrations.github.webhook.metrics")
     def test_creates_missing_repo(self, mock_metrics):
         project = self.project  # force creation
@@ -715,7 +736,9 @@ class PullRequestEventWebhook(APITestCase):
 
         self.assert_group_link(group, pr)
 
-    def test_closed(self):
+    @with_feature("organizations:integrations-open-pr-comment")
+    @patch("sentry.integrations.github.webhook.metrics")
+    def test_closed(self, mock_metrics):
         project = self.project  # force creation
 
         future_expires = datetime.now().replace(microsecond=0) + timedelta(minutes=5)
@@ -760,6 +783,8 @@ class PullRequestEventWebhook(APITestCase):
         assert pr.author.name == "baxterthehacker"
         assert pr.merge_commit_sha == "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c"
 
+        assert mock_metrics.incr.call_count == 0
+
     def assert_group_link(self, group, pr):
         link = GroupLink.objects.all().first()
         assert link

+ 50 - 0
tests/sentry/tasks/integrations/github/test_open_pr_comment.py

@@ -2,6 +2,7 @@ from unittest.mock import patch
 
 import pytest
 import responses
+from django.utils import timezone
 
 from sentry.models.group import Group
 from sentry.models.options.organization_option import OrganizationOption
@@ -520,6 +521,55 @@ class TestOpenPRCommentWorkflow(GithubCommentTestCase):
         assert pull_request_comment_query[0].comment_type == CommentType.OPEN_PR
         mock_metrics.incr.assert_called_with("github_open_pr_comment.comment_created")
 
+    @patch("sentry.tasks.integrations.github.open_pr_comment.get_pr_filenames")
+    @patch(
+        "sentry.tasks.integrations.github.open_pr_comment.get_projects_and_filenames_from_source_file"
+    )
+    @patch("sentry.tasks.integrations.github.open_pr_comment.get_top_5_issues_by_count_for_file")
+    @patch("sentry.tasks.integrations.github.open_pr_comment.safe_for_comment", return_value=True)
+    @patch("sentry.tasks.integrations.github.pr_comment.metrics")
+    @responses.activate
+    def test_comment_workflow_comment_exists(
+        self,
+        mock_metrics,
+        mock_safe_for_comment,
+        mock_issues,
+        mock_reverse_codemappings,
+        mock_pr_filenames,
+    ):
+        # two filenames, the second one has a toggle table
+        mock_pr_filenames.return_value = ["foo.py", "bar.py"]
+        mock_reverse_codemappings.return_value = ([self.project], ["foo.py"])
+
+        mock_issues.return_value = self.groups
+
+        now = timezone.now()
+        PullRequestComment.objects.create(
+            external_id=1,
+            pull_request=self.pr,
+            created_at=now,
+            updated_at=now,
+            group_ids=[0, 1],
+            comment_type=CommentType.OPEN_PR,
+        )
+
+        responses.add(
+            responses.PATCH,
+            self.base_url + "/repos/getsentry/sentry/issues/comments/1",
+            json={"id": 1},
+            headers={"X-Ratelimit-Limit": "60", "X-Ratelimit-Remaining": "59"},
+        )
+
+        open_pr_comment_workflow(self.pr.id)
+
+        pull_request_comment_query = PullRequestComment.objects.all()
+        pr_comment = pull_request_comment_query[0]
+        assert len(pull_request_comment_query) == 1
+        assert pr_comment.external_id == 1
+        assert pr_comment.comment_type == CommentType.OPEN_PR
+        assert pr_comment.created_at != pr_comment.updated_at
+        mock_metrics.incr.assert_called_with("github_open_pr_comment.comment_updated")
+
     @patch("sentry.tasks.integrations.github.open_pr_comment.get_pr_filenames")
     @patch(
         "sentry.tasks.integrations.github.open_pr_comment.get_projects_and_filenames_from_source_file"