Repository: getsentry/sentry

Issue:
Update serializer in `endpoints/group_details.py`
Once we update the other parts of the backend, we will need the serializer to look for teams in GroupSubscriptions so that we can show them on the front end . Updates will need to be made here: https://github.com/getsentry/sentry/blob/master/src/sentry/api/endpoints/group_details.py. 

1. Move addition of participants to return value to be below the current bulk update
2. Add type of user to every user that is returned from serializing 
3. With feature flag, find all teams that are participants 
4. Add type to the teams 
5. Combine with existing user participants 
6. Update return value with new participants 



Discussion:

Resolving patch:
diff --git a/src/sentry/api/endpoints/group_details.py b/src/sentry/api/endpoints/group_details.py
--- a/src/sentry/api/endpoints/group_details.py
+++ b/src/sentry/api/endpoints/group_details.py
@@ -22,12 +22,14 @@
 )
 from sentry.api.serializers import GroupSerializer, GroupSerializerSnuba, serialize
 from sentry.api.serializers.models.plugin import PluginSerializer, is_plugin_deprecated
+from sentry.api.serializers.models.team import TeamSerializer
 from sentry.issues.constants import get_issue_tsdb_group_model
 from sentry.issues.escalating_group_forecast import EscalatingGroupForecast
 from sentry.issues.grouptype import GroupCategory
 from sentry.models import Activity, Group, GroupSeen, GroupSubscriptionManager, UserReport
 from sentry.models.groupinbox import get_inbox_details
 from sentry.models.groupowner import get_owner_details
+from sentry.models.team import Team
 from sentry.plugins.base import plugins
 from sentry.plugins.bases.issue2 import IssueTrackingPlugin2
 from sentry.services.hybrid_cloud.user.service import user_service
@@ -246,12 +248,6 @@ def get(self, request: Request, group) -> Response:
                 {
                     "activity": serialize(activity, request.user),
                     "seenBy": seen_by,
-                    "participants": user_service.serialize_many(
-                        filter={
-                            "user_ids": GroupSubscriptionManager.get_participating_user_ids(group)
-                        },
-                        as_user=request.user,
-                    ),
                     "pluginActions": action_list,
                     "pluginIssues": self._get_available_issue_plugins(request, group),
                     "pluginContexts": self._get_context_plugins(request, group),
@@ -260,6 +256,30 @@ def get(self, request: Request, group) -> Response:
                 }
             )
 
+            participants = user_service.serialize_many(
+                filter={"user_ids": GroupSubscriptionManager.get_participating_user_ids(group)},
+                as_user=request.user,
+            )
+
+            for participant in participants:
+                participant["type"] = "user"
+
+            if features.has("organizations:team-workflow-notifications", group.organization):
+                team_ids = GroupSubscriptionManager.get_participating_team_ids(group)
+
+                teams = Team.objects.filter(id__in=team_ids)
+                team_serializer = TeamSerializer()
+
+                serialized_teams = []
+                for team in teams:
+                    serialized_team = serialize(team, request.user, team_serializer)
+                    serialized_team["type"] = "team"
+                    serialized_teams.append(serialized_team)
+
+                participants.extend(serialized_teams)
+
+            data.update({"participants": participants})
+
             metrics.incr(
                 "group.update.http_response",
                 sample_rate=1.0,
diff --git a/src/sentry/models/groupsubscription.py b/src/sentry/models/groupsubscription.py
--- a/src/sentry/models/groupsubscription.py
+++ b/src/sentry/models/groupsubscription.py
@@ -202,11 +202,21 @@ def get_participating_user_ids(group: Group) -> Sequence[int]:
         """Return the list of user ids participating in this issue."""
 
         return list(
-            GroupSubscription.objects.filter(group=group, is_active=True).values_list(
+            GroupSubscription.objects.filter(group=group, is_active=True, team=None).values_list(
                 "user_id", flat=True
             )
         )
 
+    @staticmethod
+    def get_participating_team_ids(group: Group) -> Sequence[int]:
+        """Return the list of team ids participating in this issue."""
+
+        return list(
+            GroupSubscription.objects.filter(group=group, is_active=True, user_id=None).values_list(
+                "team_id", flat=True
+            )
+        )
+
 
 @region_silo_only_model
 class GroupSubscription(Model):


Test patch:
diff --git a/tests/sentry/api/endpoints/test_group_details.py b/tests/sentry/api/endpoints/test_group_details.py
--- a/tests/sentry/api/endpoints/test_group_details.py
+++ b/tests/sentry/api/endpoints/test_group_details.py
@@ -24,9 +24,11 @@
     GroupTombstone,
     Release,
 )
+from sentry.notifications.types import GroupSubscriptionReason
 from sentry.plugins.base import plugins
 from sentry.silo import SiloMode
 from sentry.testutils.cases import APITestCase, SnubaTestCase
+from sentry.testutils.helpers.features import with_feature
 from sentry.testutils.silo import assume_test_silo_mode, region_silo_test
 from sentry.types.activity import ActivityType
 
@@ -531,6 +533,48 @@ def test_subscription(self):
             user_id=self.user.id, group=group, is_active=False
         ).exists()
 
+    @with_feature("organizations:team-workflow-notifications")
+    def test_team_subscription(self):
+        group = self.create_group()
+        team = self.create_team(organization=group.project.organization, members=[self.user])
+
+        # subscribe the team
+        GroupSubscription.objects.create(
+            team=team,
+            group=group,
+            project=group.project,
+            is_active=True,
+            reason=GroupSubscriptionReason.team_mentioned,
+        )
+
+        self.login_as(user=self.user)
+
+        url = f"/api/0/issues/{group.id}/"
+        response = self.client.get(url)
+
+        assert response.status_code == 200
+        assert len(response.data["participants"]) == 1
+        assert response.data["participants"][0]["type"] == "team"
+
+        # add the user as a subscriber
+        GroupSubscription.objects.create(
+            user_id=self.user.id,
+            group=group,
+            project=group.project,
+            is_active=True,
+            reason=GroupSubscriptionReason.comment,
+        )
+
+        response = self.client.get(url)
+        assert response.status_code == 200
+
+        # both the user and their team should be subscribed separately
+        assert len(response.data["participants"]) == 2
+        assert (
+            response.data["participants"][0]["type"] == "user"
+        )  # user participants are processed first
+        assert response.data["participants"][1]["type"] == "team"
+
     def test_discard(self):
         self.login_as(user=self.user)
         group = self.create_group()