Browse Source

feat(analytics): Add analytics for different types of issue resolution (#44106)

Add analytic event for issue resolution by third party and automatic
resolution.
Modify analytic event for issue resolution by commit to include project
id.

WOR-2411
Jodi Jang 2 years ago
parent
commit
9ef432fba0

+ 1 - 0
src/sentry/analytics/events/issue_resolved.py

@@ -6,6 +6,7 @@ class IssueResolvedEvent(analytics.Event):
 
     attributes = (
         analytics.Attribute("user_id", required=False),
+        analytics.Attribute("project_id", required=False),
         analytics.Attribute("default_user_id"),
         analytics.Attribute("organization_id"),
         analytics.Attribute("group_id"),

+ 9 - 1
src/sentry/api/serializers/models/group.py

@@ -25,7 +25,7 @@ import sentry_sdk
 from django.conf import settings
 from django.db.models import Min, prefetch_related_objects
 
-from sentry import tagstore
+from sentry import analytics, tagstore
 from sentry.api.serializers import Serializer, register, serialize
 from sentry.api.serializers.models.actor import ActorSerializer
 from sentry.api.serializers.models.plugin import is_plugin_deprecated
@@ -424,6 +424,14 @@ class GroupSerializerBase(Serializer, ABC):
         if status == GroupStatus.UNRESOLVED and obj.is_over_resolve_age():
             status = GroupStatus.RESOLVED
             status_details["autoResolved"] = True
+            analytics.record(
+                "issue.resolved",
+                default_user_id=obj.project.organization.get_default_owner().id,
+                project_id=obj.project.id,
+                organization_id=obj.project.organization_id,
+                group_id=obj.id,
+                resolution_type="automatic",
+            )
         if status == GroupStatus.RESOLVED:
             status_label = "resolved"
             if attrs["resolution_type"] == "release":

+ 1 - 0
src/sentry/receivers/features.py

@@ -206,6 +206,7 @@ def record_issue_resolved(organization_id, project, group, user, resolution_type
     analytics.record(
         "issue.resolved",
         user_id=user_id,
+        project_id=project.id,
         default_user_id=default_user_id,
         organization_id=organization_id,
         group_id=group.id,

+ 11 - 0
src/sentry/tasks/integrations/sync_status_inbound.py

@@ -1,5 +1,6 @@
 from typing import Any, Mapping
 
+from sentry import analytics
 from sentry.models import Group, GroupStatus, Integration, Organization
 from sentry.tasks.base import instrumented_task, retry, track_group_async_operation
 from sentry.types.activity import ActivityType
@@ -38,6 +39,16 @@ def sync_status_inbound(
         Group.objects.update_group_status(
             affected_groups, GroupStatus.RESOLVED, ActivityType.SET_RESOLVED
         )
+
+        for group in affected_groups:
+            analytics.record(
+                "issue.resolved",
+                project_id=group.project.id,
+                default_user_id=organizations[0].get_default_owner().id,
+                organization_id=organization_id,
+                group_id=group.id,
+                resolution_type="with_third_party_app",
+            )
     elif action == ResolveSyncAction.UNRESOLVE:
         Group.objects.update_group_status(
             affected_groups, GroupStatus.UNRESOLVED, ActivityType.SET_UNRESOLVED

+ 10 - 1
tests/snuba/api/serializers/test_group.py

@@ -138,8 +138,9 @@ class GroupSerializerSnubaTest(APITestCase, SnubaTestCase):
         assert result["status"] == "resolved"
         assert result["statusDetails"]["inCommit"]["id"] == commit.key
 
+    @patch("sentry.analytics.record")
     @patch("sentry.models.Group.is_over_resolve_age")
-    def test_auto_resolved(self, mock_is_over_resolve_age):
+    def test_auto_resolved(self, mock_is_over_resolve_age, mock_record):
         mock_is_over_resolve_age.return_value = True
 
         user = self.create_user()
@@ -148,6 +149,14 @@ class GroupSerializerSnubaTest(APITestCase, SnubaTestCase):
         result = serialize(group, user, serializer=GroupSerializerSnuba())
         assert result["status"] == "resolved"
         assert result["statusDetails"] == {"autoResolved": True}
+        mock_record.assert_called_with(
+            "issue.resolved",
+            default_user_id=self.project.organization.get_default_owner().id,
+            project_id=self.project.id,
+            organization_id=self.project.organization_id,
+            group_id=group.id,
+            resolution_type="automatic",
+        )
 
     def test_subscribed(self):
         user = self.create_user()