Repository: getsentry/sentry

Issue:
[BE] Unsubscribe a user who un-bookmarks an issue
When a user bookmarks an issue (Issue details > "..." > Bookmark) a `GroupSubscription` row is created with the reason "bookmark", but when they remove the bookmark, the `GroupSubscription` row isn't removed (or marked `is_active` false) - we should remove this. 


Discussion:

Resolving patch:
diff --git a/src/sentry/api/helpers/group_index/update.py b/src/sentry/api/helpers/group_index/update.py
--- a/src/sentry/api/helpers/group_index/update.py
+++ b/src/sentry/api/helpers/group_index/update.py
@@ -717,7 +717,7 @@ def handle_is_bookmarked(
     acting_user: User | None,
 ) -> None:
     """
-    Creates bookmarks and subscriptions for a user, or deletes the exisitng bookmarks.
+    Creates bookmarks and subscriptions for a user, or deletes the existing bookmarks and subscriptions.
     """
     if is_bookmarked:
         for group in group_list:
@@ -734,6 +734,11 @@ def handle_is_bookmarked(
             group__in=group_ids,
             user_id=acting_user.id if acting_user else None,
         ).delete()
+        if features.has("organizations:participants-purge", group_list[0].organization):
+            GroupSubscription.objects.filter(
+                user_id=acting_user.id,
+                group__in=group_ids,
+            ).delete()
 
 
 def handle_has_seen(
diff --git a/src/sentry/conf/server.py b/src/sentry/conf/server.py
--- a/src/sentry/conf/server.py
+++ b/src/sentry/conf/server.py
@@ -1429,6 +1429,8 @@ def custom_parameter_sort(parameter: dict) -> tuple[str, int]:
     "organizations:higher-ownership-limit": False,
     # Enable Monitors (Crons) view
     "organizations:monitors": False,
+    # Enable participants purge
+    "organizations:participants-purge": False,
     # Enable Performance view
     "organizations:performance-view": True,
     # Enable profiling
diff --git a/src/sentry/features/__init__.py b/src/sentry/features/__init__.py
--- a/src/sentry/features/__init__.py
+++ b/src/sentry/features/__init__.py
@@ -127,6 +127,7 @@
 default_manager.add("organizations:notification-all-recipients", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:onboarding", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)  # Only enabled in sentry.io to enable onboarding flows.
 default_manager.add("organizations:org-subdomains", OrganizationFeature, FeatureHandlerStrategy.INTERNAL)
+default_manager.add("organizations:participants-purge", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:performance-anomaly-detection-ui", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:performance-change-explorer", OrganizationFeature, FeatureHandlerStrategy.REMOTE)
 default_manager.add("organizations:performance-chart-interpolation", OrganizationFeature, FeatureHandlerStrategy.REMOTE)


Test patch:
diff --git a/tests/sentry/api/helpers/test_group_index.py b/tests/sentry/api/helpers/test_group_index.py
--- a/tests/sentry/api/helpers/test_group_index.py
+++ b/tests/sentry/api/helpers/test_group_index.py
@@ -24,6 +24,7 @@
 from sentry.models.groupshare import GroupShare
 from sentry.models.groupsnooze import GroupSnooze
 from sentry.models.groupsubscription import GroupSubscription
+from sentry.notifications.types import GroupSubscriptionReason
 from sentry.testutils.cases import TestCase
 from sentry.testutils.helpers.features import with_feature
 from sentry.testutils.skips import requires_snuba
@@ -396,15 +397,42 @@ def test_is_bookmarked(self) -> None:
         handle_is_bookmarked(True, self.group_list, self.group_ids, self.project_lookup, self.user)
 
         assert GroupBookmark.objects.filter(group=self.group, user_id=self.user.id).exists()
+        assert GroupSubscription.objects.filter(
+            group=self.group, user_id=self.user.id, reason=GroupSubscriptionReason.bookmark
+        ).exists()
 
     def test_not_is_bookmarked(self) -> None:
         GroupBookmark.objects.create(
             group=self.group, user_id=self.user.id, project_id=self.group.project_id
         )
+        GroupSubscription.objects.create(
+            project=self.group.project,
+            group=self.group,
+            user_id=self.user.id,
+            reason=GroupSubscriptionReason.bookmark,
+        )
 
         handle_is_bookmarked(False, self.group_list, self.group_ids, self.project_lookup, self.user)
 
         assert not GroupBookmark.objects.filter(group=self.group, user_id=self.user.id).exists()
+        assert GroupSubscription.objects.filter(
+            project=self.group.project,
+            group=self.group,
+            user_id=self.user.id,
+        ).exists()
+
+        # test with feature flag
+        GroupBookmark.objects.create(
+            group=self.group, user_id=self.user.id, project_id=self.group.project_id
+        )
+
+        with self.feature("organizations:participants-purge"):
+            handle_is_bookmarked(
+                False, self.group_list, self.group_ids, self.project_lookup, self.user
+            )
+
+        assert not GroupBookmark.objects.filter(group=self.group, user_id=self.user.id).exists()
+        assert not GroupSubscription.objects.filter(group=self.group, user_id=self.user.id).exists()
 
 
 class TestHandleHasSeen(TestCase):