Browse Source

ref(notifications): Clean ActivityNotifications (#25554)

Marcos Gaeta 3 years ago
parent
commit
04a1b7cc7c

+ 10 - 45
src/sentry/notifications/activity/base.py

@@ -1,14 +1,15 @@
 import re
-from typing import Any, Mapping, MutableMapping, Optional, Set, Tuple
+from typing import Any, Mapping, MutableMapping, Optional, Tuple
 from urllib.parse import urlparse, urlunparse
 
 from django.utils.html import escape
 from django.utils.safestring import SafeString, mark_safe
 
-from sentry.models import Activity, GroupSubscription, User, UserOption
+from sentry.models import Activity, User
 from sentry.notifications.notify import notify
 from sentry.notifications.types import GroupSubscriptionReason
 from sentry.notifications.utils.avatar import avatar_as_html
+from sentry.notifications.utils.participants import get_participants_for_group
 from sentry.types.integrations import ExternalProviders
 from sentry.utils.http import absolute_uri
 
@@ -23,48 +24,6 @@ class ActivityNotification:
     def should_email(self) -> bool:
         return True
 
-    def get_providers_from_which_to_remove_user(
-        self,
-        user: User,
-        participants_by_provider: Mapping[ExternalProviders, Mapping[User, int]],
-    ) -> Set[ExternalProviders]:
-        """
-        Given a mapping of provider to mappings of users to why they should receive
-        notifications for an activity, return the set of providers where the user
-        has opted out of receiving notifications.
-        """
-
-        providers = {
-            provider
-            for provider, participants in participants_by_provider.items()
-            if user in participants
-        }
-        if (
-            providers
-            and UserOption.objects.get_value(user, key="self_notifications", default="0") == "0"
-        ):
-            return providers
-        return set()
-
-    def get_participants(self) -> Mapping[ExternalProviders, Mapping[User, int]]:
-        # TODO(dcramer): not used yet today except by Release's
-        if not self.group:
-            return {}
-
-        participants_by_provider: MutableMapping[
-            ExternalProviders, MutableMapping[User, int]
-        ] = GroupSubscription.objects.get_participants(self.group)
-        user_option = self.activity.user
-        if user_option:
-            # Optionally remove the actor that created the activity from the recipients list.
-            providers = self.get_providers_from_which_to_remove_user(
-                user_option, participants_by_provider
-            )
-            for provider in providers:
-                del participants_by_provider[provider][user_option]
-
-        return participants_by_provider
-
     def get_template(self) -> str:
         return "sentry/emails/activity/generic.txt"
 
@@ -78,6 +37,12 @@ class ActivityNotification:
         referrer = re.sub("Notification$", "Email", self.__class__.__name__)
         return str(self.group.get_absolute_url(params={"referrer": referrer}))
 
+    def get_participants_with_group_subscription_reason(
+        self,
+    ) -> Mapping[ExternalProviders, Mapping[User, int]]:
+        """ This is overridden by the activity subclasses. """
+        return get_participants_for_group(self.group, self.activity.user)
+
     def get_base_context(self) -> MutableMapping[str, Any]:
         """ The most basic context shared by every notification type. """
         activity = self.activity
@@ -184,7 +149,7 @@ class ActivityNotification:
         if not self.should_email():
             return
 
-        participants_by_provider = self.get_participants()
+        participants_by_provider = self.get_participants_with_group_subscription_reason()
         if not participants_by_provider:
             return
 

+ 7 - 20
src/sentry/notifications/activity/new_processing_issues.py

@@ -1,35 +1,22 @@
-from typing import Any, Iterable, MutableMapping
+from typing import Any, MutableMapping
 
-from sentry.models import Activity, EventError, Mapping, NotificationSetting, User
+from sentry.models import Activity, Mapping, NotificationSetting, User
 from sentry.notifications.types import GroupSubscriptionReason
+from sentry.notifications.utils import summarize_issues
 from sentry.types.integrations import ExternalProviders
 from sentry.utils.http import absolute_uri
 
 from .base import ActivityNotification
 
 
-def summarize_issues(issues: Iterable[Any]) -> Iterable[Mapping[str, str]]:
-    rv = []
-    for issue in issues:
-        extra_info = None
-        msg_d = dict(issue["data"])
-        msg_d["type"] = issue["type"]
-
-        if "image_path" in issue["data"]:
-            extra_info = issue["data"]["image_path"].rsplit("/", 1)[-1]
-            if "image_arch" in issue["data"]:
-                extra_info = "{} ({})".format(extra_info, issue["data"]["image_arch"])
-
-        rv.append({"message": EventError(msg_d).message, "extra_info": extra_info})
-    return rv
-
-
 class NewProcessingIssuesActivityNotification(ActivityNotification):
     def __init__(self, activity: Activity) -> None:
-        ActivityNotification.__init__(self, activity)
+        super().__init__(activity)
         self.issues = summarize_issues(self.activity.data["issues"])
 
-    def get_participants(self) -> Mapping[ExternalProviders, Mapping[User, int]]:
+    def get_participants_with_group_subscription_reason(
+        self,
+    ) -> Mapping[ExternalProviders, Mapping[User, int]]:
         users_by_provider = NotificationSetting.objects.get_notification_recipients(self.project)
         return {
             provider: {user: GroupSubscriptionReason.processing_issue for user in users}

+ 50 - 171
src/sentry/notifications/activity/release.py

@@ -1,34 +1,19 @@
-from collections import defaultdict
-from typing import Any, List, Mapping, MutableMapping, Optional, Set
-
-from django.db.models import Count
-
-from sentry.db.models.query import in_iexact
-from sentry.models import (
-    Activity,
-    CommitFileChange,
-    Deploy,
-    Environment,
-    Group,
-    GroupLink,
-    NotificationSetting,
-    ProjectTeam,
-    Release,
-    ReleaseCommit,
-    Repository,
-    User,
-    UserEmail,
-)
-from sentry.notifications.helpers import (
-    get_deploy_values_by_provider,
-    transform_to_notification_settings_by_user,
-)
-from sentry.notifications.notify import notification_providers
-from sentry.notifications.types import (
-    GroupSubscriptionReason,
-    NotificationSettingOptionValues,
-    NotificationSettingTypes,
+from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Set
+
+from sentry.models import Activity, Project, User
+from sentry.notifications.utils import (
+    get_commits_for_release,
+    get_deploy,
+    get_environment_for_deploy,
+    get_file_count,
+    get_group_counts_by_project,
+    get_projects,
+    get_release,
+    get_repos,
+    get_users_by_emails,
+    get_users_by_teams,
 )
+from sentry.notifications.utils.participants import get_participants_for_release
 from sentry.types.integrations import ExternalProviders
 from sentry.utils.compat import zip
 from sentry.utils.http import absolute_uri
@@ -40,174 +25,68 @@ class ReleaseActivityNotification(ActivityNotification):
     def __init__(self, activity: Activity) -> None:
         super().__init__(activity)
         self.organization = self.project.organization
-        self.user_id_team_lookup: Optional[MutableMapping[int, List[int]]] = None
+        self.user_id_team_lookup: Optional[Mapping[int, List[int]]] = None
         self.email_list: Set[str] = set()
         self.user_ids: Set[int] = set()
-
-        try:
-            self.deploy = Deploy.objects.get(id=activity.data["deploy_id"])
-        except Deploy.DoesNotExist:
-            self.deploy = None
-
-        try:
-            self.release = Release.objects.get(
-                organization_id=self.project.organization_id, version=activity.data["version"]
-            )
-        except Release.DoesNotExist:
-            self.release = None
-            self.repos = []
-            self.projects = []
-        else:
-            self.projects = list(self.release.projects.all())
-            self.commit_list = [
-                rc.commit
-                for rc in ReleaseCommit.objects.filter(release=self.release).select_related(
-                    "commit", "commit__author"
-                )
-            ]
-            repos = {
-                r_id: {"name": r_name, "commits": []}
-                for r_id, r_name in Repository.objects.filter(
-                    organization_id=self.project.organization_id,
-                    id__in={c.repository_id for c in self.commit_list},
-                ).values_list("id", "name")
-            }
-
-            self.email_list = {c.author.email for c in self.commit_list if c.author}
-            if self.email_list:
-                users = {
-                    ue.email: ue.user
-                    for ue in UserEmail.objects.filter(
-                        in_iexact("email", self.email_list),
-                        is_verified=True,
-                        user__sentry_orgmember_set__organization=self.organization,
-                    ).select_related("user")
-                }
-                self.user_ids = {u.id for u in users.values()}
-
-            else:
-                users = {}
-
-            for commit in self.commit_list:
-                repos[commit.repository_id]["commits"].append(
-                    (commit, users.get(commit.author.email) if commit.author_id else None)
-                )
-
-            self.repos = list(repos.values())
-
-            self.environment = (
-                Environment.objects.get(id=self.deploy.environment_id).name or "Default Environment"
-            )
-
-            self.group_counts_by_project = dict(
-                Group.objects.filter(
-                    project__in=self.projects,
-                    id__in=GroupLink.objects.filter(
-                        linked_type=GroupLink.LinkedType.commit,
-                        linked_id__in=ReleaseCommit.objects.filter(
-                            release=self.release
-                        ).values_list("commit_id", flat=True),
-                    ).values_list("group_id", flat=True),
-                )
-                .values_list("project")
-                .annotate(num_groups=Count("id"))
-            )
+        self.deploy = get_deploy(activity)
+
+        self.release = get_release(activity, self.organization)
+        if not self.release:
+            self.repos: Iterable[Mapping[str, Any]] = set()
+            self.projects: Set[Project] = set()
+            self.version = "unknown"
+            return
+
+        self.projects = set(self.release.projects.all())
+        self.commit_list = get_commits_for_release(self.release)
+        self.email_list = {c.author.email for c in self.commit_list if c.author}
+        users = get_users_by_emails(self.email_list, self.organization)
+        self.user_ids = {u.id for u in users.values()}
+        self.repos = get_repos(self.commit_list, users, self.organization)
+        self.environment = get_environment_for_deploy(self.deploy)
+        self.group_counts_by_project = get_group_counts_by_project(self.release, self.projects)
+        self.version = self.release.version
 
     def should_email(self) -> bool:
         return bool(self.release and self.deploy)
 
-    def get_reason(self, user: User, value: NotificationSettingOptionValues) -> Optional[int]:
-        # Members who opt into all deploy emails.
-        if value == NotificationSettingOptionValues.ALWAYS:
-            return GroupSubscriptionReason.deploy_setting
-
-        # Members which have been seen in the commit log.
-        elif value == NotificationSettingOptionValues.COMMITTED_ONLY and user.id in self.user_ids:
-            return GroupSubscriptionReason.committed
-        return None
-
-    def get_participants(self) -> Mapping[ExternalProviders, Mapping[User, int]]:
-        # Collect all users with verified emails on a team in the related projects.
-        users = list(User.objects.get_team_members_with_verified_email_for_projects(self.projects))
-
-        # Get all the involved users' settings for deploy-emails (including
-        # users' organization-independent settings.)
-        notification_settings = NotificationSetting.objects.get_for_users_by_parent(
-            NotificationSettingTypes.DEPLOY,
-            users=users,
-            parent=self.organization,
-        )
-        notification_settings_by_user = transform_to_notification_settings_by_user(
-            notification_settings, users
-        )
-
-        # Map users to their setting value. Prioritize user/org specific, then
-        # user default, then product default.
-        users_to_reasons_by_provider: MutableMapping[
-            ExternalProviders, MutableMapping[User, int]
-        ] = defaultdict(dict)
-        for user in users:
-            notification_settings_by_scope = notification_settings_by_user.get(user, {})
-            values_by_provider = get_deploy_values_by_provider(
-                notification_settings_by_scope, notification_providers()
-            )
-            for provider, value in values_by_provider.items():
-                reason_option = self.get_reason(user, value)
-                if reason_option:
-                    users_to_reasons_by_provider[provider][user] = reason_option
-        return users_to_reasons_by_provider
+    def get_participants_with_group_subscription_reason(
+        self,
+    ) -> Mapping[ExternalProviders, Mapping[User, int]]:
+        return get_participants_for_release(self.projects, self.organization, self.user_ids)
 
     def get_users_by_teams(self) -> Mapping[int, List[int]]:
         if not self.user_id_team_lookup:
-            user_teams: MutableMapping[int, List[int]] = defaultdict(list)
-            queryset = User.objects.filter(
-                sentry_orgmember_set__organization_id=self.organization.id
-            ).values_list("id", "sentry_orgmember_set__teams")
-            for user_id, team_id in queryset:
-                user_teams[user_id].append(team_id)
-            self.user_id_team_lookup = user_teams
+            self.user_id_team_lookup = get_users_by_teams(self.organization)
         return self.user_id_team_lookup
 
     def get_context(self) -> MutableMapping[str, Any]:
-        file_count = (
-            CommitFileChange.objects.filter(
-                commit__in=self.commit_list, organization_id=self.organization.id
-            )
-            .values("filename")
-            .distinct()
-            .count()
-        )
-
         return {
             **self.get_base_context(),
             "commit_count": len(self.commit_list),
             "author_count": len(self.email_list),
-            "file_count": file_count,
+            "file_count": get_file_count(self.commit_list, self.organization),
             "repos": self.repos,
             "release": self.release,
             "deploy": self.deploy,
             "environment": self.environment,
             "setup_repo_link": absolute_uri(f"/organizations/{self.organization.slug}/repos/"),
-            "text_description": f"Version {self.release.version} was deployed to {self.environment}",
+            "text_description": f"Version {self.version} was deployed to {self.environment}",
         }
 
+    def get_projects(self, user: User) -> Set[Project]:
+        if user.is_superuser or self.organization.flags.allow_joinleave:
+            return self.projects
+        team_ids = self.get_users_by_teams()[user.id]
+        return get_projects(self.projects, team_ids)
+
     def get_user_context(
         self, user: User, reason: Optional[int] = None
     ) -> MutableMapping[str, Any]:
-        if user.is_superuser or self.organization.flags.allow_joinleave:
-            projects = self.projects
-        else:
-            teams = self.get_users_by_teams()[user.id]
-            team_projects = set(
-                ProjectTeam.objects.filter(team_id__in=teams)
-                .values_list("project_id", flat=True)
-                .distinct()
-            )
-            projects = [p for p in self.projects if p.id in team_projects]
-
+        projects = self.get_projects(user)
         release_links = [
             absolute_uri(
-                f"/organizations/{self.organization.slug}/releases/{self.release.version}/?project={p.id}"
+                f"/organizations/{self.organization.slug}/releases/{self.version}/?project={p.id}"
             )
             for p in projects
         ]
@@ -220,7 +99,7 @@ class ReleaseActivityNotification(ActivityNotification):
         }
 
     def get_subject(self) -> str:
-        return f"Deployed version {self.release.version} to {self.environment}"
+        return f"Deployed version {self.version} to {self.environment}"
 
     def get_title(self) -> str:
         return self.get_subject()

+ 150 - 0
src/sentry/notifications/utils/__init__.py

@@ -0,0 +1,150 @@
+from collections import defaultdict
+from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Set
+
+from django.db.models import Count
+
+from sentry.db.models.query import in_iexact
+from sentry.models import (
+    Activity,
+    Commit,
+    CommitFileChange,
+    Deploy,
+    Environment,
+    EventError,
+    Group,
+    GroupLink,
+    Organization,
+    Project,
+    ProjectTeam,
+    Release,
+    ReleaseCommit,
+    Repository,
+    User,
+    UserEmail,
+)
+
+
+def get_projects(projects: Iterable[Project], team_ids: Iterable[int]) -> Set[Project]:
+    team_projects = set(
+        ProjectTeam.objects.filter(team_id__in=team_ids)
+        .values_list("project_id", flat=True)
+        .distinct()
+    )
+    return {p for p in projects if p.id in team_projects}
+
+
+def get_users_by_teams(organization: Organization) -> Mapping[int, List[int]]:
+    user_teams: MutableMapping[int, List[int]] = defaultdict(list)
+    queryset = User.objects.filter(
+        sentry_orgmember_set__organization_id=organization.id
+    ).values_list("id", "sentry_orgmember_set__teams")
+    for user_id, team_id in queryset:
+        user_teams[user_id].append(team_id)
+    return user_teams
+
+
+def get_deploy(activity: Activity) -> Optional[Deploy]:
+    try:
+        return Deploy.objects.get(id=activity.data["deploy_id"])
+    except Deploy.DoesNotExist:
+        return None
+
+
+def get_release(activity: Activity, organization: Organization) -> Optional[Release]:
+    try:
+        return Release.objects.get(
+            organization_id=organization.id, version=activity.data["version"]
+        )
+    except Release.DoesNotExist:
+        return None
+
+
+def get_group_counts_by_project(
+    release: Release, projects: Iterable[Project]
+) -> Mapping[Project, int]:
+    return dict(
+        Group.objects.filter(
+            project__in=projects,
+            id__in=GroupLink.objects.filter(
+                linked_type=GroupLink.LinkedType.commit,
+                linked_id__in=ReleaseCommit.objects.filter(release=release).values_list(
+                    "commit_id", flat=True
+                ),
+            ).values_list("group_id", flat=True),
+        )
+        .values_list("project")
+        .annotate(num_groups=Count("id"))
+    )
+
+
+def get_users_by_emails(emails: Iterable[str], organization: Organization) -> Mapping[str, User]:
+    if not emails:
+        return {}
+
+    return {
+        ue.email: ue.user
+        for ue in UserEmail.objects.filter(
+            in_iexact("email", emails),
+            is_verified=True,
+            user__sentry_orgmember_set__organization=organization,
+        ).select_related("user")
+    }
+
+
+def get_repos(
+    commits: Iterable[Commit], users_by_email: Mapping[str, User], organization: Organization
+) -> Iterable[Mapping[str, Any]]:
+    repos = {
+        r_id: {"name": r_name, "commits": []}
+        for r_id, r_name in Repository.objects.filter(
+            organization_id=organization.id,
+            id__in={c.repository_id for c in commits},
+        ).values_list("id", "name")
+    }
+    for commit in commits:
+        user_option = users_by_email.get(commit.author.email) if commit.author_id else None
+        repos[commit.repository_id]["commits"].append((commit, user_option))
+
+    return list(repos.values())
+
+
+def get_commits_for_release(release: Release) -> Set[Commit]:
+    return {
+        rc.commit
+        for rc in ReleaseCommit.objects.filter(release=release).select_related(
+            "commit", "commit__author"
+        )
+    }
+
+
+def get_environment_for_deploy(deploy: Optional[Deploy]) -> str:
+    if deploy:
+        environment = Environment.objects.get(id=deploy.environment_id)
+        if environment and environment.name:
+            return str(environment.name)
+    return "Default Environment"
+
+
+def get_file_count(commits: Iterable[Commit], organization: Organization) -> int:
+    return int(
+        CommitFileChange.objects.filter(commit__in=commits, organization_id=organization.id)
+        .values("filename")
+        .distinct()
+        .count()
+    )
+
+
+def summarize_issues(issues: Iterable[Any]) -> Iterable[Mapping[str, str]]:
+    rv = []
+    for issue in issues:
+        extra_info = None
+        msg_d = dict(issue["data"])
+        msg_d["type"] = issue["type"]
+
+        if "image_path" in issue["data"]:
+            extra_info = issue["data"]["image_path"].rsplit("/", 1)[-1]
+            if "image_arch" in issue["data"]:
+                extra_info = "{} ({})".format(extra_info, issue["data"]["image_arch"])
+
+        rv.append({"message": EventError(msg_d).message, "extra_info": extra_info})
+    return rv

+ 112 - 0
src/sentry/notifications/utils/participants.py

@@ -0,0 +1,112 @@
+from collections import defaultdict
+from typing import Iterable, Mapping, MutableMapping, Optional, Set
+
+from sentry.models import (
+    Group,
+    GroupSubscription,
+    NotificationSetting,
+    Organization,
+    Project,
+    User,
+    UserOption,
+)
+from sentry.notifications.helpers import (
+    get_deploy_values_by_provider,
+    transform_to_notification_settings_by_user,
+)
+from sentry.notifications.notify import notification_providers
+from sentry.notifications.types import (
+    GroupSubscriptionReason,
+    NotificationSettingOptionValues,
+    NotificationSettingTypes,
+)
+from sentry.types.integrations import ExternalProviders
+
+
+def get_providers_from_which_to_remove_user(
+    user: User,
+    participants_by_provider: Mapping[ExternalProviders, Mapping[User, int]],
+) -> Set[ExternalProviders]:
+    """
+    Given a mapping of provider to mappings of users to why they should receive
+    notifications for an activity, return the set of providers where the user
+    has opted out of receiving notifications.
+    """
+
+    providers = {
+        provider
+        for provider, participants in participants_by_provider.items()
+        if user in participants
+    }
+    if (
+        providers
+        and UserOption.objects.get_value(user, key="self_notifications", default="0") == "0"
+    ):
+        return providers
+    return set()
+
+
+def get_participants_for_group(
+    group: Group, user: Optional[User] = None
+) -> Mapping[ExternalProviders, Mapping[User, int]]:
+    # TODO(dcramer): not used yet today except by Release's
+    if not group:
+        return {}
+
+    participants_by_provider: MutableMapping[
+        ExternalProviders, MutableMapping[User, int]
+    ] = GroupSubscription.objects.get_participants(group)
+    if user:
+        # Optionally remove the actor that created the activity from the recipients list.
+        providers = get_providers_from_which_to_remove_user(user, participants_by_provider)
+        for provider in providers:
+            del participants_by_provider[provider][user]
+
+    return participants_by_provider
+
+
+def get_reason(
+    user: User, value: NotificationSettingOptionValues, user_ids: Set[int]
+) -> Optional[int]:
+    # Members who opt into all deploy emails.
+    if value == NotificationSettingOptionValues.ALWAYS:
+        return GroupSubscriptionReason.deploy_setting
+
+    # Members which have been seen in the commit log.
+    elif value == NotificationSettingOptionValues.COMMITTED_ONLY and user.id in user_ids:
+        return GroupSubscriptionReason.committed
+    return None
+
+
+def get_participants_for_release(
+    projects: Iterable[Project], organization: Organization, user_ids: Set[int]
+) -> Mapping[ExternalProviders, Mapping[User, int]]:
+    # Collect all users with verified emails on a team in the related projects.
+    users = list(User.objects.get_team_members_with_verified_email_for_projects(projects))
+
+    # Get all the involved users' settings for deploy-emails (including
+    # users' organization-independent settings.)
+    notification_settings = NotificationSetting.objects.get_for_users_by_parent(
+        NotificationSettingTypes.DEPLOY,
+        users=users,
+        parent=organization,
+    )
+    notification_settings_by_user = transform_to_notification_settings_by_user(
+        notification_settings, users
+    )
+
+    # Map users to their setting value. Prioritize user/org specific, then
+    # user default, then product default.
+    users_to_reasons_by_provider: MutableMapping[
+        ExternalProviders, MutableMapping[User, int]
+    ] = defaultdict(dict)
+    for user in users:
+        notification_settings_by_scope = notification_settings_by_user.get(user, {})
+        values_by_provider = get_deploy_values_by_provider(
+            notification_settings_by_scope, notification_providers()
+        )
+        for provider, value in values_by_provider.items():
+            reason_option = get_reason(user, value, user_ids)
+            if reason_option:
+                users_to_reasons_by_provider[provider][user] = reason_option
+    return users_to_reasons_by_provider

+ 7 - 3
tests/sentry/mail/activity/test_note.py

@@ -25,7 +25,7 @@ class NoteTestCase(ActivityTestCase):
 
     def test_simple(self):
         # Defaults: SUBSCRIBE_ONLY and self_notifications:0
-        assert not self.email.get_participants()
+        assert not self.email.get_participants_with_group_subscription_reason()
 
     def test_allow_self_notifications(self):
         NotificationSetting.objects.update_settings(
@@ -36,7 +36,9 @@ class NoteTestCase(ActivityTestCase):
         )
         UserOption.objects.create(user=self.user, key="self_notifications", value="1")
 
-        participants = self.email.get_participants()[ExternalProviders.EMAIL]
+        participants = self.email.get_participants_with_group_subscription_reason()[
+            ExternalProviders.EMAIL
+        ]
         assert len(participants) == 1
         assert participants == {
             self.user: GroupSubscriptionReason.implicit,
@@ -51,5 +53,7 @@ class NoteTestCase(ActivityTestCase):
         )
         UserOption.objects.create(user=self.user, key="self_notifications", value="0")
 
-        participants = self.email.get_participants()[ExternalProviders.EMAIL]
+        participants = self.email.get_participants_with_group_subscription_reason()[
+            ExternalProviders.EMAIL
+        ]
         assert len(participants) == 0

+ 9 - 3
tests/sentry/mail/activity/test_release.py

@@ -86,7 +86,9 @@ class ReleaseTestCase(ActivityTestCase):
         # for that org -- also tests to make sure org overrides default preference
         # user5 committed with another email address and is still included.
 
-        participants = email.get_participants()[ExternalProviders.EMAIL]
+        participants = email.get_participants_with_group_subscription_reason()[
+            ExternalProviders.EMAIL
+        ]
         assert len(participants) == 3
         assert participants == {
             self.user1: GroupSubscriptionReason.committed,
@@ -143,7 +145,9 @@ class ReleaseTestCase(ActivityTestCase):
         )
 
         # only user3 is included because they opted into all deploy emails
-        participants = email.get_participants()[ExternalProviders.EMAIL]
+        participants = email.get_participants_with_group_subscription_reason()[
+            ExternalProviders.EMAIL
+        ]
         assert len(participants) == 1
         assert participants == {self.user3: GroupSubscriptionReason.deploy_setting}
 
@@ -188,7 +192,9 @@ class ReleaseTestCase(ActivityTestCase):
 
         # user3 and user 6 are included because they oped into all deploy emails
         # (one on an org level, one as their default)
-        participants = email.get_participants()[ExternalProviders.EMAIL]
+        participants = email.get_participants_with_group_subscription_reason()[
+            ExternalProviders.EMAIL
+        ]
         assert len(participants) == 2
         assert participants == {
             user6: GroupSubscriptionReason.deploy_setting,