Repository: getsentry/sentry

Issue:
Add subscription for team in `models/groupsubscription`
Two updates we need to make here:
1) Update `subscribe_actor` (https://github.com/getsentry/sentry/blob/master/src/sentry/models/groupsubscription.py#L67)  to no longer read all the team's users and just set subscribe the Team
2) Update `bulk_subscribe` to allow for bulk subscribing teams, instead of just users


Discussion:

Resolving patch:
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
@@ -82,22 +82,28 @@ def subscribe_actor(
             else:
                 # subscribe the members of the team
                 team_users_ids = list(actor.member_set.values_list("user_id", flat=True))
-                return self.bulk_subscribe(group, team_users_ids, reason)
+                return self.bulk_subscribe(group=group, user_ids=team_users_ids, reason=reason)
 
         raise NotImplementedError("Unknown actor type: %r" % type(actor))
 
     def bulk_subscribe(
         self,
         group: Group,
-        user_ids: Iterable[int],
+        user_ids: Iterable[int] | None = None,
+        team_ids: Iterable[int] | None = None,
         reason: int = GroupSubscriptionReason.unknown,
     ) -> bool:
         """
-        Subscribe a list of user ids to an issue, but only if the users are not explicitly
+        Subscribe a list of user ids and/or teams to an issue, but only if the users/teams are not explicitly
         unsubscribed.
         """
+        from sentry import features
+
         # Unique the IDs.
-        user_ids = set(user_ids)
+        user_ids = set(user_ids) if user_ids else set()
+
+        # Unique the teams.
+        team_ids = set(team_ids) if team_ids else set()
 
         # 5 retries for race conditions where
         # concurrent subscription attempts cause integrity errors
@@ -117,10 +123,29 @@ def bulk_subscribe(
                     is_active=True,
                     reason=reason,
                 )
-                for user_id in user_ids
-                if user_id not in existing_subscriptions
+                for user_id in user_ids.difference(existing_subscriptions)
             ]
 
+            if features.has("organizations:team-workflow-notifications", group.organization):
+                existing_team_subscriptions = set(
+                    GroupSubscription.objects.filter(
+                        team_id__in=team_ids, group=group, project=group.project
+                    ).values_list("team_id", flat=True)
+                )
+
+                subscriptions.extend(
+                    [
+                        GroupSubscription(
+                            team_id=team_id,
+                            group=group,
+                            project=group.project,
+                            is_active=True,
+                            reason=reason,
+                        )
+                        for team_id in team_ids.difference(existing_team_subscriptions)
+                    ]
+                )
+
             try:
                 with transaction.atomic(router.db_for_write(GroupSubscription)):
                     self.bulk_create(subscriptions)


Test patch:
diff --git a/tests/sentry/models/test_groupsubscription.py b/tests/sentry/models/test_groupsubscription.py
--- a/tests/sentry/models/test_groupsubscription.py
+++ b/tests/sentry/models/test_groupsubscription.py
@@ -42,7 +42,7 @@ def test_bulk(self):
         group = self.create_group()
 
         user_ids = []
-        for i in range(20):
+        for _ in range(20):
             user = self.create_user()
             user_ids.append(user.id)
 
@@ -71,6 +71,75 @@ def test_bulk_dupes(self):
 
         assert len(GroupSubscription.objects.filter(group=group)) == 1
 
+    @with_feature("organizations:team-workflow-notifications")
+    def test_bulk_teams(self):
+        group = self.create_group()
+
+        team_ids = []
+        for _ in range(20):
+            team = self.create_team()
+            team_ids.append(team.id)
+
+        GroupSubscription.objects.bulk_subscribe(group=group, team_ids=team_ids)
+
+        assert len(GroupSubscription.objects.filter(group=group)) == 20
+
+        one_more = self.create_team()
+        team_ids.append(one_more.id)
+
+        # should not error
+        GroupSubscription.objects.bulk_subscribe(group=group, team_ids=team_ids)
+
+        assert len(GroupSubscription.objects.filter(group=group)) == 21
+
+    @with_feature("organizations:team-workflow-notifications")
+    def test_bulk_teams_dupes(self):
+        group = self.create_group()
+
+        team_ids = []
+
+        team = self.create_team()
+        team_ids.append(team.id)
+        team_ids.append(team.id)
+
+        GroupSubscription.objects.bulk_subscribe(group=group, team_ids=team_ids)
+
+        assert len(GroupSubscription.objects.filter(group=group)) == 1
+
+    @with_feature("organizations:team-workflow-notifications")
+    def test_bulk_users_and_teams(self):
+        group = self.create_group()
+
+        user_ids = []
+        team_ids = []
+
+        for _ in range(10):
+            user = self.create_user()
+            user_ids.append(user.id)
+            team = self.create_team()
+            team_ids.append(team.id)
+
+        GroupSubscription.objects.bulk_subscribe(group=group, user_ids=user_ids, team_ids=team_ids)
+
+        assert len(GroupSubscription.objects.filter(group=group)) == 20
+
+    @with_feature("organizations:team-workflow-notifications")
+    def test_bulk_user_on_team(self):
+        """
+        Test that ensures bulk_subscribe subscribes users and teams individually, even if one of those users is part of one of those teams.
+        """
+        group = self.create_group()
+        team = self.create_team()
+        user = self.create_user()
+        self.create_member(user=user, organization=self.organization, role="member", teams=[team])
+
+        team_ids = [team.id]
+        user_ids = [user.id]
+
+        GroupSubscription.objects.bulk_subscribe(group=group, user_ids=user_ids, team_ids=team_ids)
+
+        assert len(GroupSubscription.objects.filter(group=group)) == 2
+
     def test_actor_user(self):
         group = self.create_group()
         user = self.create_user()