Repository: getsentry/sentry
Pull request: feat(issue-stream): Add "unhandled" as collapsable parameter
this pr adds in "unhandled" as a collapsable parameter for the issues stream request. right now, the issue stream request has to wait for the unhandled stats to be found, which slows things down. to get around this, we will collapse "unhandled" and make a separate request to find the "unhandled" information , similar to how we treat "stats". 


closes https://github.com/getsentry/sentry/issues/57844

Problem statement:
Update `OrganizationGroupIndexEndpoint` to handle "unhandled" as collapse
We need to add in the ability to collapse "unhandled" to the `OrganizationGroupIndexEndpoint`. From there, we need to use that collapsed parameter and pass it down to `get_attrs` and prevent `get_group_snuba_stats` from being called.

Hints from issue discussion:

Commit message: add collapse for unhandled

Patch to review:
diff --git a/src/sentry/api/endpoints/organization_group_index.py b/src/sentry/api/endpoints/organization_group_index.py
index 1c7e4e81e052ed..327dea1cdbe91e 100644
--- a/src/sentry/api/endpoints/organization_group_index.py
+++ b/src/sentry/api/endpoints/organization_group_index.py
@@ -228,7 +228,7 @@ def get(self, request: Request, organization) -> Response:
                                           issues belong to.
         :auth: required
         :qparam list expand: an optional list of strings to opt in to additional data. Supports `inbox`
-        :qparam list collapse: an optional list of strings to opt out of certain pieces of data. Supports `stats`, `lifetime`, `base`
+        :qparam list collapse: an optional list of strings to opt out of certain pieces of data. Supports `stats`, `lifetime`, `base`, `unhandled`
         """
         stats_period = request.GET.get("groupStatsPeriod")
         try:
diff --git a/src/sentry/api/serializers/models/group.py b/src/sentry/api/serializers/models/group.py
index 49edbb05a37d3a..c409d0e9c33be0 100644
--- a/src/sentry/api/serializers/models/group.py
+++ b/src/sentry/api/serializers/models/group.py
@@ -289,7 +289,9 @@ def get_attrs(
         ):
             merge_list_dictionaries(annotations_by_group_id, annotations_by_group)
 
-        snuba_stats = self._get_group_snuba_stats(item_list, seen_stats)
+        snuba_stats = {}
+        if not self._collapse("unhandled"):
+            snuba_stats = self._get_group_snuba_stats(item_list, seen_stats)
 
         result = {}
         for item in item_list:
@@ -325,8 +327,8 @@ def get_attrs(
                 "share_id": share_ids.get(item.id),
                 "authorized": authorized,
             }
-
-            result[item]["is_unhandled"] = bool(snuba_stats.get(item.id, {}).get("unhandled"))
+            if not self._collapse("unhandled"):
+                result[item]["is_unhandled"] = bool(snuba_stats.get(item.id, {}).get("unhandled"))
 
             if seen_stats:
                 result[item].update(seen_stats.get(item, {}))
diff --git a/tests/snuba/api/endpoints/test_organization_group_index.py b/tests/snuba/api/endpoints/test_organization_group_index.py
index 9f4544f6aef2c6..9a32357ed456f9 100644
--- a/tests/snuba/api/endpoints/test_organization_group_index.py
+++ b/tests/snuba/api/endpoints/test_organization_group_index.py
@@ -1917,6 +1917,20 @@ def test_collapse_stats_group_snooze_bug(self):
         assert len(response.data) == 1
         assert int(response.data[0]["id"]) == event.group.id
 
+    def test_collapse_unhandled(self):
+        event = self.store_event(
+            data={"timestamp": iso_format(before_now(seconds=500)), "fingerprint": ["group-1"]},
+            project_id=self.project.id,
+        )
+        self.login_as(user=self.user)
+        response = self.get_response(
+            sort_by="date", limit=10, query="is:unresolved", collapse=["unhandled"]
+        )
+        assert response.status_code == 200
+        assert len(response.data) == 1
+        assert int(response.data[0]["id"]) == event.group.id
+        assert "isUnhandled" not in response.data[0]
+
     def test_query_status_and_substatus_overlapping(self):
         event = self.store_event(
             data={"timestamp": iso_format(before_now(seconds=500)), "fingerprint": ["group-1"]},


Code review comments:
diff_hunk: @@ -289,7 +289,9 @@ def get_attrs(
         ):
             merge_list_dictionaries(annotations_by_group_id, annotations_by_group)
 
-        snuba_stats = self._get_group_snuba_stats(item_list, seen_stats)
+        snuba_stats = {}
+        if not self._collapse("unhandled"):
+            snuba_stats = self._get_group_snuba_stats(item_list, seen_stats)
path: src/sentry/api/serializers/models/group.py
text: @user1:
should we combine this with the logic lower down