diff --git a/src/sentry/models/groupsubscription.py b/src/sentry/models/groupsubscription.py index d0cd05af31f3cf..c06acf63b08f61 100644 --- 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) diff --git a/tests/sentry/models/test_groupsubscription.py b/tests/sentry/models/test_groupsubscription.py index 9f5e5b5ddd36b8..b1c44682155705 100644 --- 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()