Skip to content

Commit bb1cbec

Browse files
author
Stephen Cefali
authored
feat(notifications): replace logic for bulk settings update (#60502)
Trying to remove all dependencies on `NotificationSetting`. We have a `bulk_update_settings` that's only used to enable all settings for a provider so I'm making the function do that instead without updating `NotificationSetting` rows.
1 parent 4e21dcb commit bb1cbec

File tree

5 files changed

+47
-80
lines changed

5 files changed

+47
-80
lines changed

src/sentry/integrations/slack/webhooks/action.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,12 @@
2828
from sentry.models.activity import ActivityIntegration
2929
from sentry.models.group import Group
3030
from sentry.models.organizationmember import InviteStatus, OrganizationMember
31-
from sentry.notifications.defaults import NOTIFICATION_SETTINGS_ALL_SOMETIMES
3231
from sentry.notifications.utils.actions import MessageAction
3332
from sentry.services.hybrid_cloud.integration import integration_service
3433
from sentry.services.hybrid_cloud.notifications import notifications_service
3534
from sentry.services.hybrid_cloud.user import RpcUser
3635
from sentry.shared_integrations.exceptions import ApiError
37-
from sentry.types.integrations import ExternalProviders
36+
from sentry.types.integrations import ExternalProviderEnum
3837
from sentry.utils import json
3938
from sentry.web.decorators import transaction_start
4039

@@ -467,10 +466,9 @@ def handle_enable_notifications(self, slack_request: SlackActionRequest) -> Resp
467466
if not identity_user:
468467
return self.respond_with_text(NO_IDENTITY_MESSAGE)
469468

470-
notifications_service.bulk_update_settings(
471-
external_provider=ExternalProviders.SLACK,
469+
notifications_service.enable_all_settings_for_provider(
470+
external_provider=ExternalProviderEnum.SLACK,
472471
user_id=identity_user.id,
473-
notification_type_to_value_map=NOTIFICATION_SETTINGS_ALL_SOMETIMES,
474472
)
475473
return self.respond_with_text(ENABLE_SLACK_SUCCESS_MESSAGE)
476474

src/sentry/notifications/manager.py

-38
Original file line numberDiff line numberDiff line change
@@ -521,41 +521,3 @@ def disable_settings_for_users(
521521
user_id=user.id,
522522
defaults={"value": NotificationSettingOptionValues.NEVER.value},
523523
)
524-
525-
def has_any_provider_settings(
526-
self, recipient: RpcActor | Team | User, provider: ExternalProviders
527-
) -> bool:
528-
from sentry.models.team import Team
529-
from sentry.models.user import User
530-
531-
key_field = None
532-
if isinstance(recipient, RpcActor):
533-
key_field = "user_id" if recipient.actor_type == ActorType.USER else "team_id"
534-
if isinstance(recipient, (RpcUser, User)):
535-
key_field = "user_id"
536-
if isinstance(recipient, Team):
537-
key_field = "team_id"
538-
539-
assert key_field, "Could not resolve key_field"
540-
541-
team_ids: Set[int] = set()
542-
user_ids: Set[int] = set()
543-
if isinstance(recipient, RpcActor):
544-
(team_ids if recipient.actor_type == ActorType.TEAM else user_ids).add(recipient.id)
545-
elif isinstance(recipient, Team):
546-
team_ids.add(recipient.id)
547-
elif isinstance(recipient, User):
548-
user_ids.add(recipient.id)
549-
550-
return (
551-
self._filter(provider=provider, team_ids=team_ids, user_ids=user_ids)
552-
.filter(
553-
value__in={
554-
NotificationSettingOptionValues.ALWAYS.value,
555-
NotificationSettingOptionValues.COMMITTED_ONLY.value,
556-
NotificationSettingOptionValues.SUBSCRIBE_ONLY.value,
557-
},
558-
**{key_field: recipient.id},
559-
)
560-
.exists()
561-
)

src/sentry/services/hybrid_cloud/notifications/impl.py

+15-16
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from sentry.models.user import User
1414
from sentry.notifications.notificationcontroller import NotificationController
1515
from sentry.notifications.types import (
16+
NOTIFICATION_SETTING_TYPES,
1617
NotificationScopeEnum,
1718
NotificationSettingEnum,
1819
NotificationSettingOptionValues,
@@ -30,7 +31,7 @@
3031
from sentry.services.hybrid_cloud.notifications.serial import serialize_notification_setting
3132
from sentry.services.hybrid_cloud.user import RpcUser
3233
from sentry.services.hybrid_cloud.user.service import user_service
33-
from sentry.types.integrations import ExternalProviders
34+
from sentry.types.integrations import ExternalProviderEnum, ExternalProviders
3435

3536

3637
class DatabaseBackedNotificationsService(NotificationsService):
@@ -67,26 +68,24 @@ def update_settings(
6768
skip_provider_updates=skip_provider_updates,
6869
)
6970

70-
def bulk_update_settings(
71+
def enable_all_settings_for_provider(
7172
self,
7273
*,
73-
notification_type_to_value_map: Mapping[
74-
NotificationSettingTypes, NotificationSettingOptionValues
75-
],
76-
external_provider: ExternalProviders,
74+
external_provider: ExternalProviderEnum,
7775
user_id: int,
7876
) -> None:
79-
with transaction.atomic(router.db_for_write(NotificationSetting)):
80-
for notification_type, setting_option in notification_type_to_value_map.items():
81-
self.update_settings(
82-
external_provider=external_provider,
83-
actor=RpcActor(id=user_id, actor_type=ActorType.USER),
84-
notification_type=notification_type,
85-
setting_option=setting_option,
86-
skip_provider_updates=True,
77+
with transaction.atomic(router.db_for_write(NotificationSettingProvider)):
78+
for type_str in NOTIFICATION_SETTING_TYPES.values():
79+
NotificationSettingProvider.objects.create_or_update(
80+
provider=external_provider.value,
81+
user_id=user_id,
82+
scope_type=NotificationScopeEnum.USER.value,
83+
scope_identifier=user_id,
84+
type=type_str,
85+
values={
86+
"value": NotificationSettingsOptionEnum.ALWAYS.value,
87+
},
8788
)
88-
# update the providers at the end
89-
NotificationSetting.objects.update_provider_settings(user_id, None)
9089

9190
def update_notification_options(
9291
self,

src/sentry/services/hybrid_cloud/notifications/service.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from sentry.services.hybrid_cloud.rpc import RpcService, rpc_method
2121
from sentry.services.hybrid_cloud.user import RpcUser
2222
from sentry.silo import SiloMode
23-
from sentry.types.integrations import ExternalProviders
23+
from sentry.types.integrations import ExternalProviderEnum, ExternalProviders
2424

2525

2626
class NotificationsService(RpcService):
@@ -53,13 +53,10 @@ def update_settings(
5353

5454
@rpc_method
5555
@abstractmethod
56-
def bulk_update_settings(
56+
def enable_all_settings_for_provider(
5757
self,
5858
*,
59-
notification_type_to_value_map: Mapping[
60-
NotificationSettingTypes, NotificationSettingOptionValues
61-
],
62-
external_provider: ExternalProviders,
59+
external_provider: ExternalProviderEnum,
6360
user_id: int,
6461
) -> None:
6562
pass

tests/sentry/integrations/slack/webhooks/actions/test_enable_notifications.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
NO_IDENTITY_MESSAGE,
44
)
55
from sentry.models.identity import Identity
6-
from sentry.models.notificationsetting import NotificationSetting
6+
from sentry.models.notificationsettingprovider import NotificationSettingProvider
77
from sentry.models.user import User
8-
from sentry.notifications.types import NotificationSettingOptionValues, NotificationSettingTypes
9-
from sentry.types.integrations import ExternalProviders
108

119
from . import BaseEventTest
1210

@@ -27,28 +25,34 @@ def test_enable_all_slack_no_identity(self):
2725
assert response.data["text"] == NO_IDENTITY_MESSAGE
2826

2927
def test_enable_all_slack_already_enabled(self):
30-
NotificationSetting.objects.update_settings(
31-
ExternalProviders.EMAIL,
32-
NotificationSettingTypes.ISSUE_ALERTS,
33-
NotificationSettingOptionValues.NEVER,
28+
NotificationSettingProvider.objects.create(
3429
user_id=self.user.id,
30+
scope_type="user",
31+
scope_identifier=self.user.id,
32+
type="alerts",
33+
provider="slack",
34+
value="never",
3535
)
36-
3736
response = self.post_webhook(
3837
action_data=[{"name": "enable_notifications", "value": "all_slack"}]
3938
)
4039
self.user = User.objects.get(id=self.user.id) # Reload to fetch actor
4140
assert response.status_code == 200, response.content
4241
assert response.data["text"] == ENABLE_SLACK_SUCCESS_MESSAGE
4342

44-
assert NotificationSetting.objects.has_any_provider_settings(
45-
self.user, ExternalProviders.SLACK
43+
assert (
44+
NotificationSettingProvider.objects.get(
45+
user_id=self.user.id,
46+
scope_type="user",
47+
scope_identifier=self.user.id,
48+
type="alerts",
49+
provider="slack",
50+
).value
51+
== "always"
4652
)
4753

4854
def test_enable_all_slack(self):
49-
assert not NotificationSetting.objects.has_any_provider_settings(
50-
self.user, ExternalProviders.SLACK
51-
)
55+
assert not NotificationSettingProvider.objects.all().exists()
5256

5357
response = self.post_webhook(
5458
action_data=[{"name": "enable_notifications", "value": "all_slack"}]
@@ -57,6 +61,13 @@ def test_enable_all_slack(self):
5761
assert response.status_code == 200, response.content
5862
assert response.data["text"] == ENABLE_SLACK_SUCCESS_MESSAGE
5963

60-
assert NotificationSetting.objects.has_any_provider_settings(
61-
self.user, ExternalProviders.SLACK
64+
assert (
65+
NotificationSettingProvider.objects.get(
66+
user_id=self.user.id,
67+
scope_type="user",
68+
scope_identifier=self.user.id,
69+
type="alerts",
70+
provider="slack",
71+
).value
72+
== "always"
6273
)

0 commit comments

Comments
 (0)