Browse Source

feat(issue-stream): Combined endpoint for stats + unhandled (#58456)

new version of https://github.com/getsentry/sentry/pull/58195 which was
reverted. that pr didn't fix a test that then fails because of the new
feature flagging. it is now updated so the test passes.
Richard Roggenkemper 1 year ago
parent
commit
fb22ac3d3c

+ 8 - 2
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 features, 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
@@ -493,7 +493,13 @@ class GroupSerializerBase(Serializer, ABC):
     def _get_group_snuba_stats(
         self, item_list: Sequence[Group], seen_stats: Optional[Mapping[Group, SeenStats]]
     ):
-        if self._collapse("unhandled"):
+        if (
+            self._collapse("unhandled")
+            and len(item_list) > 0
+            and features.has(
+                "organizations:issue-stream-performance", item_list[0].project.organization
+            )
+        ):
             return None
         start = self._get_start_from_seen_stats(seen_stats)
         unhandled = {}

+ 14 - 1
src/sentry/api/serializers/models/group_stream.py

@@ -8,7 +8,7 @@ from typing import Any, Callable, Mapping, MutableMapping, Optional, Sequence
 
 from django.utils import timezone
 
-from sentry import release_health, tsdb
+from sentry import features, release_health, tsdb
 from sentry.api.serializers.models.group import (
     BaseGroupSerializerResponse,
     GroupSerializer,
@@ -213,10 +213,21 @@ class StreamGroupSerializerSnuba(GroupSerializerSnuba, GroupStatsMixin):
             attrs = super().get_attrs(item_list, user)
         else:
             seen_stats = self._get_seen_stats(item_list, user)
+
             if seen_stats:
                 attrs = {item: seen_stats.get(item, {}) for item in item_list}
             else:
                 attrs = {item: {} for item in item_list}
+            if len(item_list) > 0 and features.has(
+                "organizations:issue-stream-performance", item_list[0].project.organization
+            ):
+                unhandled_stats = self._get_group_snuba_stats(item_list, seen_stats)
+
+                if unhandled_stats is not None:
+                    for item in item_list:
+                        attrs[item]["is_unhandled"] = bool(
+                            unhandled_stats.get(item.id, {}).get("unhandled")
+                        )
 
         if self.stats_period and not self._collapse("stats"):
             partial_get_stats = functools.partial(
@@ -306,6 +317,8 @@ class StreamGroupSerializerSnuba(GroupSerializerSnuba, GroupStatsMixin):
             }
             if "times_seen" in attrs:
                 result.update(self._convert_seen_stats(attrs))
+            if "is_unhandled" in attrs:
+                result["isUnhandled"] = attrs["is_unhandled"]
 
         if not self._collapse("stats"):
             if self.stats_period:

+ 1 - 0
tests/snuba/api/endpoints/test_organization_group_index.py

@@ -1917,6 +1917,7 @@ class GroupListTest(APITestCase, SnubaTestCase):
         assert len(response.data) == 1
         assert int(response.data[0]["id"]) == event.group.id
 
+    @with_feature("organizations:issue-stream-performance")
     def test_collapse_unhandled(self):
         event = self.store_event(
             data={"timestamp": iso_format(before_now(seconds=500)), "fingerprint": ["group-1"]},

+ 31 - 1
tests/snuba/api/endpoints/test_organization_group_index_stats.py

@@ -3,7 +3,7 @@ import uuid
 from sentry.issues.grouptype import ProfileFileIOGroupType
 from sentry.issues.occurrence_consumer import process_event_and_issue_occurrence
 from sentry.testutils.cases import APITestCase, SnubaTestCase
-from sentry.testutils.helpers import parse_link_header
+from sentry.testutils.helpers import parse_link_header, with_feature
 from sentry.testutils.helpers.datetime import before_now, iso_format
 from sentry.testutils.silo import region_silo_test
 from tests.sentry.issues.test_utils import OccurrenceTestMixin
@@ -67,6 +67,36 @@ class GroupListTest(APITestCase, SnubaTestCase, OccurrenceTestMixin):
         assert "userCount" in response_data[0]
         assert "lifetime" in response_data[0]
         assert "filtered" in response_data[0]
+        assert "isUnhandled" not in response_data[0]
+
+    @with_feature("organizations:issue-stream-performance")
+    def test_unhandled(self):
+        self.store_event(
+            data={"timestamp": iso_format(before_now(seconds=500)), "fingerprint": ["group-1"]},
+            project_id=self.project.id,
+        )
+        group_a = self.store_event(
+            data={"timestamp": iso_format(before_now(seconds=1)), "fingerprint": ["group-a"]},
+            project_id=self.project.id,
+        ).group
+
+        self.login_as(user=self.user)
+        response = self.get_response(query="is:unresolved", groups=[group_a.id])
+
+        response_data = sorted(response.data, key=lambda x: x["firstSeen"], reverse=True)
+
+        assert response.status_code == 200
+        assert len(response_data) == 1
+        assert "title" not in response_data[0]
+        assert "hasSeen" not in response_data[0]
+        assert "stats" in response_data[0]
+        assert "firstSeen" in response_data[0]
+        assert "lastSeen" in response_data[0]
+        assert "count" in response_data[0]
+        assert "userCount" in response_data[0]
+        assert "lifetime" in response_data[0]
+        assert "filtered" in response_data[0]
+        assert "isUnhandled" in response_data[0]
 
     def test_issue_platform_issue(self):
         event_id = uuid.uuid4().hex