diff --git a/src/sentry/charts/endpoints.py b/src/sentry/charts/endpoints.py index cfa8c1b5a68506..cd3531da0a6965 100644 --- a/src/sentry/charts/endpoints.py +++ b/src/sentry/charts/endpoints.py @@ -5,12 +5,14 @@ from django.views import static import sentry +from sentry.web.frontend.base import all_silo_view CONFIG_DIR = os.path.abspath( os.path.join(os.path.dirname(sentry.__file__), "..", "..", "config", "chartcuterie") ) +@all_silo_view def serve_chartcuterie_config( request: HttpRequest, ) -> HttpResponseBase: diff --git a/src/sentry/silo/base.py b/src/sentry/silo/base.py index d7e20653ce80ac..5850a204ecaddd 100644 --- a/src/sentry/silo/base.py +++ b/src/sentry/silo/base.py @@ -3,7 +3,6 @@ import abc import contextlib import functools -import itertools import threading import typing from collections.abc import Callable, Generator, Iterable @@ -158,10 +157,11 @@ def override(*args: P.args, **kwargs: P.kwargs) -> R: if is_available: return original_method(*args, **kwargs) else: + modes = list(self.modes) + if SiloMode.MONOLITH not in self.modes: + modes = modes + [SiloMode.MONOLITH] handler = self.handle_when_unavailable( - original_method, - SiloMode.get_current_mode(), - itertools.chain([SiloMode.MONOLITH], self.modes), + original_method, SiloMode.get_current_mode(), modes ) return handler(*args, **kwargs) diff --git a/src/sentry/web/frontend/base.py b/src/sentry/web/frontend/base.py index 543672d0c3a567..f7d0aa0b7e54b0 100644 --- a/src/sentry/web/frontend/base.py +++ b/src/sentry/web/frontend/base.py @@ -57,6 +57,12 @@ class ViewSiloLimit(SiloLimit): + def __init__(self, modes: SiloMode | Iterable[SiloMode], internal: bool = False) -> None: + if isinstance(modes, SiloMode): + modes = [modes] + self.modes = frozenset(modes) + self.internal = internal + def modify_endpoint_class(self, decorated_class: type[View]) -> type: dispatch_override = self.create_override(decorated_class.dispatch) new_class = type( @@ -71,7 +77,10 @@ def modify_endpoint_class(self, decorated_class: type[View]) -> type: return new_class def modify_endpoint_method(self, decorated_method: Callable[..., Any]) -> Callable[..., Any]: - return self.create_override(decorated_method) + decorated = self.create_override(decorated_method) + setattr(decorated, "silo_limit", self) + + return decorated def handle_when_unavailable( self, @@ -115,25 +124,32 @@ def __call__(self, decorated_obj: Any) -> Any: raise TypeError("`@ViewSiloLimit` must decorate a class or method") -control_silo_view = ViewSiloLimit(SiloMode.CONTROL) +control_silo_view = ViewSiloLimit([SiloMode.CONTROL]) """ Apply to frontend views that exist in CONTROL Silo If a request is received and the application is not in CONTROL/MONOLITH mode a 404 will be returned. """ -region_silo_view = ViewSiloLimit(SiloMode.REGION) +region_silo_view = ViewSiloLimit([SiloMode.REGION]) """ Apply to frontend views that exist in REGION Silo If a request is received and the application is not in REGION/MONOLITH mode a 404 will be returned. """ -all_silo_view = ViewSiloLimit(SiloMode.REGION, SiloMode.CONTROL, SiloMode.MONOLITH) +all_silo_view = ViewSiloLimit([SiloMode.REGION, SiloMode.CONTROL, SiloMode.MONOLITH]) """ Apply to frontend views that respond in both CONTROL and REGION mode. """ +internal_region_silo_view = ViewSiloLimit([SiloMode.REGION], internal=True) +""" +Apply to frontend views that exist in REGION Silo +and are not accessible via cell routing. +This is generally for debug/development views. +""" + class _HasRespond(Protocol): active_organization: RpcUserOrganizationContext | None diff --git a/src/sentry/web/frontend/debug/charts/debug_chart_renderer.py b/src/sentry/web/frontend/debug/charts/debug_chart_renderer.py index e57b0b648e38c5..db7978d39889f2 100644 --- a/src/sentry/web/frontend/debug/charts/debug_chart_renderer.py +++ b/src/sentry/web/frontend/debug/charts/debug_chart_renderer.py @@ -3,6 +3,7 @@ from sentry.charts import backend as charts from sentry.charts.types import ChartType +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.frontend.debug.mail import MailPreview discover_total_period = { @@ -279,6 +280,7 @@ } +@internal_region_silo_view class DebugChartRendererView(View): def get(self, request: HttpRequest) -> HttpResponse: ret = [] diff --git a/src/sentry/web/frontend/debug/charts/metric_alert_charts.py b/src/sentry/web/frontend/debug/charts/metric_alert_charts.py index 9612ebde06dc39..edb876ecb86d8f 100644 --- a/src/sentry/web/frontend/debug/charts/metric_alert_charts.py +++ b/src/sentry/web/frontend/debug/charts/metric_alert_charts.py @@ -4,6 +4,7 @@ from sentry.charts import backend as charts from sentry.charts.types import ChartType from sentry.seer.anomaly_detection.types import AnomalyType +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.frontend.debug.mail import MailPreview incident = { @@ -510,6 +511,7 @@ } +@internal_region_silo_view class DebugMetricAlertChartRendererView(View): def get(self, request: HttpRequest) -> HttpResponse: ret = [] diff --git a/src/sentry/web/frontend/debug/debug_auth_views.py b/src/sentry/web/frontend/debug/debug_auth_views.py index 5de8010b1afc7f..c0393652000402 100644 --- a/src/sentry/web/frontend/debug/debug_auth_views.py +++ b/src/sentry/web/frontend/debug/debug_auth_views.py @@ -2,9 +2,11 @@ from django.views.generic import View from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.helpers import render_to_response +@internal_region_silo_view class DebugAuthConfirmIdentity(View): def get(self, request: HttpRequest) -> HttpResponse: auth_identity = {"id": "bar@example.com", "email": "bar@example.com"} @@ -21,6 +23,7 @@ def get(self, request: HttpRequest) -> HttpResponse: ) +@internal_region_silo_view class DebugAuthConfirmLink(View): def get(self, request: HttpRequest) -> HttpResponse: auth_identity = {"id": "bar@example.com", "email": "test1@example.com"} diff --git a/src/sentry/web/frontend/debug/debug_codeowners_auto_sync_failure_email.py b/src/sentry/web/frontend/debug/debug_codeowners_auto_sync_failure_email.py index d7ea9c9000b6f4..87329705b1c273 100644 --- a/src/sentry/web/frontend/debug/debug_codeowners_auto_sync_failure_email.py +++ b/src/sentry/web/frontend/debug/debug_codeowners_auto_sync_failure_email.py @@ -6,10 +6,12 @@ from sentry.models.project import Project from sentry.notifications.notifications.codeowners_auto_sync import AutoSyncNotification from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from .mail import render_preview_email_for_notification +@internal_region_silo_view class DebugCodeOwnersAutoSyncFailureView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="petal", name="Petal") diff --git a/src/sentry/web/frontend/debug/debug_cron_broken_monitor_email.py b/src/sentry/web/frontend/debug/debug_cron_broken_monitor_email.py index 284dfdf2ccd525..2108995d4520f9 100644 --- a/src/sentry/web/frontend/debug/debug_cron_broken_monitor_email.py +++ b/src/sentry/web/frontend/debug/debug_cron_broken_monitor_email.py @@ -3,6 +3,8 @@ from django.http import HttpRequest, HttpResponse from django.views.generic import View +from sentry.web.frontend.base import internal_region_silo_view + from .mail import MailPreview @@ -17,6 +19,7 @@ def get_context(): } +@internal_region_silo_view class DebugCronBrokenMonitorEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context() diff --git a/src/sentry/web/frontend/debug/debug_cron_muted_monitor_email.py b/src/sentry/web/frontend/debug/debug_cron_muted_monitor_email.py index 28a0aa427f5079..ddb3aa68a4a0c4 100644 --- a/src/sentry/web/frontend/debug/debug_cron_muted_monitor_email.py +++ b/src/sentry/web/frontend/debug/debug_cron_muted_monitor_email.py @@ -3,6 +3,8 @@ from django.http import HttpRequest, HttpResponse from django.views.generic import View +from sentry.web.frontend.base import internal_region_silo_view + from .mail import MailPreview @@ -17,6 +19,7 @@ def get_context(): } +@internal_region_silo_view class DebugCronMutedMonitorEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context() diff --git a/src/sentry/web/frontend/debug/debug_error_embed.py b/src/sentry/web/frontend/debug/debug_error_embed.py index c292ff96af7317..c082378e33f852 100644 --- a/src/sentry/web/frontend/debug/debug_error_embed.py +++ b/src/sentry/web/frontend/debug/debug_error_embed.py @@ -5,9 +5,11 @@ from django.views.generic import View from sentry.models.projectkey import ProjectKey +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.helpers import render_to_response +@internal_region_silo_view class DebugErrorPageEmbedView(View): def _get_project_key(self): return ProjectKey.objects.filter(project=settings.SENTRY_PROJECT)[0] diff --git a/src/sentry/web/frontend/debug/debug_feedback_issue.py b/src/sentry/web/frontend/debug/debug_feedback_issue.py index f3052324165d7d..46a195f578d973 100644 --- a/src/sentry/web/frontend/debug/debug_feedback_issue.py +++ b/src/sentry/web/frontend/debug/debug_feedback_issue.py @@ -8,10 +8,12 @@ from sentry.notifications.utils import get_generic_data from sentry.notifications.utils.links import get_group_settings_link, get_rules from sentry.utils import json +from sentry.web.frontend.base import internal_region_silo_view from .mail import COMMIT_EXAMPLE, MailPreview, make_feedback_issue +@internal_region_silo_view class DebugFeedbackIssueEmailView(View): def get(self, request): org = Organization(id=1, slug="example", name="Example") diff --git a/src/sentry/web/frontend/debug/debug_generic_issue.py b/src/sentry/web/frontend/debug/debug_generic_issue.py index 5df29d9b0475de..80ebb5075f5175 100644 --- a/src/sentry/web/frontend/debug/debug_generic_issue.py +++ b/src/sentry/web/frontend/debug/debug_generic_issue.py @@ -9,10 +9,12 @@ from sentry.notifications.utils import get_generic_data from sentry.notifications.utils.links import get_group_settings_link, get_rules from sentry.utils import json +from sentry.web.frontend.base import internal_region_silo_view from .mail import COMMIT_EXAMPLE, MailPreview, make_generic_event +@internal_region_silo_view class DebugGenericIssueEmailView(View): def get(self, request): org = Organization(id=1, slug="example", name="Example") diff --git a/src/sentry/web/frontend/debug/debug_incident_trigger_email.py b/src/sentry/web/frontend/debug/debug_incident_trigger_email.py index ef536d12211c41..2b60e2eff75262 100644 --- a/src/sentry/web/frontend/debug/debug_incident_trigger_email.py +++ b/src/sentry/web/frontend/debug/debug_incident_trigger_email.py @@ -18,6 +18,7 @@ from sentry.models.project import Project from sentry.snuba.models import SnubaQuery from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreviewView @@ -26,6 +27,7 @@ class MockedIncidentTrigger: date_added = timezone.now() +@internal_region_silo_view class DebugIncidentTriggerEmailView(MailPreviewView): @mock.patch( "sentry.incidents.models.incident.IncidentTrigger.objects.get", diff --git a/src/sentry/web/frontend/debug/debug_invalid_identity_email.py b/src/sentry/web/frontend/debug/debug_invalid_identity_email.py index 1f3596e926c77b..fb9d354dc83192 100644 --- a/src/sentry/web/frontend/debug/debug_invalid_identity_email.py +++ b/src/sentry/web/frontend/debug/debug_invalid_identity_email.py @@ -2,11 +2,13 @@ from django.views.generic import View from sentry.tasks.commits import generate_invalid_identity_email +from sentry.web.frontend.base import internal_region_silo_view from social_auth.models import UserSocialAuth from .mail import MailPreview +@internal_region_silo_view class DebugInvalidIdentityEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: assert request.user.is_authenticated diff --git a/src/sentry/web/frontend/debug/debug_mfa_added_email.py b/src/sentry/web/frontend/debug/debug_mfa_added_email.py index 6853396103ca03..9fb12c941a1d11 100644 --- a/src/sentry/web/frontend/debug/debug_mfa_added_email.py +++ b/src/sentry/web/frontend/debug/debug_mfa_added_email.py @@ -6,10 +6,12 @@ from sentry.security.emails import generate_security_email from sentry.users.models.authenticator import Authenticator +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugMfaAddedEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: if isinstance(request.user, AnonymousUser): diff --git a/src/sentry/web/frontend/debug/debug_mfa_removed_email.py b/src/sentry/web/frontend/debug/debug_mfa_removed_email.py index 03cb98317c83cb..b9dca7d0032b08 100644 --- a/src/sentry/web/frontend/debug/debug_mfa_removed_email.py +++ b/src/sentry/web/frontend/debug/debug_mfa_removed_email.py @@ -6,10 +6,12 @@ from sentry.security.emails import generate_security_email from sentry.users.models.authenticator import Authenticator +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugMfaRemovedEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: if isinstance(request.user, AnonymousUser): diff --git a/src/sentry/web/frontend/debug/debug_missing_member_nudge_email.py b/src/sentry/web/frontend/debug/debug_missing_member_nudge_email.py index 766416435ee74b..19e8bbeee8aa0a 100644 --- a/src/sentry/web/frontend/debug/debug_missing_member_nudge_email.py +++ b/src/sentry/web/frontend/debug/debug_missing_member_nudge_email.py @@ -2,10 +2,12 @@ from django.views.generic import View from sentry.models.organization import Organization +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugMissingMembersNudgeView(View): def get(self, request: HttpRequest) -> HttpResponse: self.organization = Organization(id=1, slug="organization", name="My Company") diff --git a/src/sentry/web/frontend/debug/debug_new_release_email.py b/src/sentry/web/frontend/debug/debug_new_release_email.py index 43d53c7563b3ae..d89047b7e92d85 100644 --- a/src/sentry/web/frontend/debug/debug_new_release_email.py +++ b/src/sentry/web/frontend/debug/debug_new_release_email.py @@ -14,10 +14,12 @@ from sentry.notifications.types import GroupSubscriptionReason from sentry.users.models.user import User from sentry.utils.http import absolute_uri +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugNewReleaseEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="organization", name="My Company") diff --git a/src/sentry/web/frontend/debug/debug_new_user_feedback_email.py b/src/sentry/web/frontend/debug/debug_new_user_feedback_email.py index 7699b3da3ebd07..6e8e6e535cab25 100644 --- a/src/sentry/web/frontend/debug/debug_new_user_feedback_email.py +++ b/src/sentry/web/frontend/debug/debug_new_user_feedback_email.py @@ -5,10 +5,12 @@ from sentry.models.project import Project from sentry.utils.http import absolute_uri from sentry.utils.samples import create_sample_event +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugNewUserFeedbackEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="organization", name="My Company") diff --git a/src/sentry/web/frontend/debug/debug_note_email.py b/src/sentry/web/frontend/debug/debug_note_email.py index 09e3d39dec359c..fc507c7d02e3c9 100644 --- a/src/sentry/web/frontend/debug/debug_note_email.py +++ b/src/sentry/web/frontend/debug/debug_note_email.py @@ -1,10 +1,12 @@ from django.http import HttpRequest from sentry.types.activity import ActivityType +from sentry.web.frontend.base import internal_region_silo_view from .mail import ActivityMailDebugView, get_random, make_message +@internal_region_silo_view class DebugNoteEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): random = get_random(request) diff --git a/src/sentry/web/frontend/debug/debug_oauth_authorize.py b/src/sentry/web/frontend/debug/debug_oauth_authorize.py index 0968abee97e90c..b271bee00ac5ad 100644 --- a/src/sentry/web/frontend/debug/debug_oauth_authorize.py +++ b/src/sentry/web/frontend/debug/debug_oauth_authorize.py @@ -2,9 +2,11 @@ from django.views.generic import View from sentry.models.apiapplication import ApiApplication +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.helpers import render_to_response +@internal_region_silo_view class DebugOAuthAuthorizeView(View): def get(self, request: HttpRequest) -> HttpResponse: application = ApiApplication( @@ -28,6 +30,7 @@ def get(self, request: HttpRequest) -> HttpResponse: ) +@internal_region_silo_view class DebugOAuthAuthorizeErrorView(View): def get(self, request: HttpRequest) -> HttpResponse: return render_to_response( diff --git a/src/sentry/web/frontend/debug/debug_onboarding_continuation_email.py b/src/sentry/web/frontend/debug/debug_onboarding_continuation_email.py index f837ba16ccd1e2..67e4b9efb1467a 100644 --- a/src/sentry/web/frontend/debug/debug_onboarding_continuation_email.py +++ b/src/sentry/web/frontend/debug/debug_onboarding_continuation_email.py @@ -4,10 +4,12 @@ from sentry.api.endpoints.organization_onboarding_continuation_email import get_request_builder_args from sentry.models.organization import Organization from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.frontend.debug.mail import MailPreviewAdapter from sentry.web.helpers import render_to_response +@internal_region_silo_view class DebugOrganizationOnboardingContinuationEmail(View): def get(self, request: HttpRequest) -> HttpResponse: platforms = request.GET.getlist("platforms", ["javascript", "python", "flutter"]) diff --git a/src/sentry/web/frontend/debug/debug_organization_integration_request.py b/src/sentry/web/frontend/debug/debug_organization_integration_request.py index 0ccc34143ff058..c01c645798c449 100644 --- a/src/sentry/web/frontend/debug/debug_organization_integration_request.py +++ b/src/sentry/web/frontend/debug/debug_organization_integration_request.py @@ -8,10 +8,12 @@ IntegrationRequestNotification, ) from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from .mail import render_preview_email_for_notification +@internal_region_silo_view class DebugOrganizationIntegrationRequestEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="default", name="Default") diff --git a/src/sentry/web/frontend/debug/debug_organization_invite_request.py b/src/sentry/web/frontend/debug/debug_organization_invite_request.py index 41005e2ef2f8a9..190ceda92f302f 100644 --- a/src/sentry/web/frontend/debug/debug_organization_invite_request.py +++ b/src/sentry/web/frontend/debug/debug_organization_invite_request.py @@ -5,10 +5,12 @@ from sentry.models.organizationmember import InviteStatus, OrganizationMember from sentry.notifications.notifications.organization_request import InviteRequestNotification from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from .mail import render_preview_email_for_notification +@internal_region_silo_view class DebugOrganizationInviteRequestEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="default", name="Default") diff --git a/src/sentry/web/frontend/debug/debug_organization_join_request.py b/src/sentry/web/frontend/debug/debug_organization_join_request.py index 6c024130cca598..9986512f259715 100644 --- a/src/sentry/web/frontend/debug/debug_organization_join_request.py +++ b/src/sentry/web/frontend/debug/debug_organization_join_request.py @@ -5,10 +5,12 @@ from sentry.models.organizationmember import InviteStatus, OrganizationMember from sentry.notifications.notifications.organization_request import JoinRequestNotification from sentry.users.models.user import User +from sentry.web.frontend.base import internal_region_silo_view from .mail import render_preview_email_for_notification +@internal_region_silo_view class DebugOrganizationJoinRequestEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="default", name="Default") diff --git a/src/sentry/web/frontend/debug/debug_password_changed_email.py b/src/sentry/web/frontend/debug/debug_password_changed_email.py index 9649ce7c37ec82..ccae49c5cc252a 100644 --- a/src/sentry/web/frontend/debug/debug_password_changed_email.py +++ b/src/sentry/web/frontend/debug/debug_password_changed_email.py @@ -5,10 +5,12 @@ from sentry.security.emails import generate_security_email from sentry.utils.auth import AuthenticatedHttpRequest +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugPasswordChangedEmailView(View): def get(self, request: AuthenticatedHttpRequest) -> HttpResponse: email = generate_security_email( diff --git a/src/sentry/web/frontend/debug/debug_performance_issue.py b/src/sentry/web/frontend/debug/debug_performance_issue.py index 65240fdfe7c602..b100ee9d9f72f5 100644 --- a/src/sentry/web/frontend/debug/debug_performance_issue.py +++ b/src/sentry/web/frontend/debug/debug_performance_issue.py @@ -9,10 +9,12 @@ get_transaction_data, ) from sentry.utils import json +from sentry.web.frontend.base import internal_region_silo_view from .mail import COMMIT_EXAMPLE, MailPreview, get_shared_context, make_performance_event +@internal_region_silo_view class DebugPerformanceIssueEmailView(View): def get(self, request, sample_name="transaction-n-plus-one"): project = Project.objects.get(id=1) diff --git a/src/sentry/web/frontend/debug/debug_recovery_codes_regenerated_email.py b/src/sentry/web/frontend/debug/debug_recovery_codes_regenerated_email.py index 4c21edbb188556..6f8cfa32759761 100644 --- a/src/sentry/web/frontend/debug/debug_recovery_codes_regenerated_email.py +++ b/src/sentry/web/frontend/debug/debug_recovery_codes_regenerated_email.py @@ -6,10 +6,12 @@ from sentry.security.emails import generate_security_email from sentry.users.models.authenticator import Authenticator from sentry.utils.auth import AuthenticatedHttpRequest +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugRecoveryCodesRegeneratedEmailView(View): def get(self, request: AuthenticatedHttpRequest) -> HttpResponse: authenticator = Authenticator(id=0, type=3, user_id=request.user.id) # u2f diff --git a/src/sentry/web/frontend/debug/debug_regression_email.py b/src/sentry/web/frontend/debug/debug_regression_email.py index 1a8cef20002629..9395f4d51df271 100644 --- a/src/sentry/web/frontend/debug/debug_regression_email.py +++ b/src/sentry/web/frontend/debug/debug_regression_email.py @@ -1,15 +1,18 @@ from django.http import HttpRequest from sentry.types.activity import ActivityType +from sentry.web.frontend.base import internal_region_silo_view from .mail import ActivityMailDebugView +@internal_region_silo_view class DebugRegressionEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.SET_REGRESSION.value} +@internal_region_silo_view class DebugRegressionReleaseEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.SET_REGRESSION.value, "data": {"version": "abcdef"}} diff --git a/src/sentry/web/frontend/debug/debug_resolved_email.py b/src/sentry/web/frontend/debug/debug_resolved_email.py index 11af58ddbbd035..59d7652ecac3fe 100644 --- a/src/sentry/web/frontend/debug/debug_resolved_email.py +++ b/src/sentry/web/frontend/debug/debug_resolved_email.py @@ -1,10 +1,12 @@ from django.http import HttpRequest from sentry.types.activity import ActivityType +from sentry.web.frontend.base import internal_region_silo_view from .mail import ActivityMailDebugView +@internal_region_silo_view class DebugResolvedEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.SET_RESOLVED.value} diff --git a/src/sentry/web/frontend/debug/debug_resolved_in_release_email.py b/src/sentry/web/frontend/debug/debug_resolved_in_release_email.py index d592a43b89bb86..6f908f2d4ba799 100644 --- a/src/sentry/web/frontend/debug/debug_resolved_in_release_email.py +++ b/src/sentry/web/frontend/debug/debug_resolved_in_release_email.py @@ -1,15 +1,18 @@ from django.http import HttpRequest from sentry.types.activity import ActivityType +from sentry.web.frontend.base import internal_region_silo_view from .mail import ActivityMailDebugView +@internal_region_silo_view class DebugResolvedInReleaseEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.SET_RESOLVED_IN_RELEASE.value, "data": {"version": "abcdef"}} +@internal_region_silo_view class DebugResolvedInReleaseUpcomingEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.SET_RESOLVED_IN_RELEASE.value, "data": {"version": ""}} diff --git a/src/sentry/web/frontend/debug/debug_setup_2fa_email.py b/src/sentry/web/frontend/debug/debug_setup_2fa_email.py index 5a1e0541bdc528..8d965e885aa620 100644 --- a/src/sentry/web/frontend/debug/debug_setup_2fa_email.py +++ b/src/sentry/web/frontend/debug/debug_setup_2fa_email.py @@ -3,10 +3,12 @@ from sentry.models.organization import Organization from sentry.models.organizationmember import OrganizationMember +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugSetup2faEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(id=1, slug="organization", name="sentry corp") diff --git a/src/sentry/web/frontend/debug/debug_sso_link_email.py b/src/sentry/web/frontend/debug/debug_sso_link_email.py index fe3e33578b3bdb..339539049d9304 100644 --- a/src/sentry/web/frontend/debug/debug_sso_link_email.py +++ b/src/sentry/web/frontend/debug/debug_sso_link_email.py @@ -3,6 +3,7 @@ from sentry.auth.providers.dummy import DummyProvider from sentry.models.organization import Organization +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview @@ -14,6 +15,7 @@ def get_context(request): return {"organization": org, "actor_email": request.user.email, "provider": provider} +@internal_region_silo_view class DebugSsoLinkedEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context(request) @@ -25,6 +27,7 @@ def get(self, request: HttpRequest) -> HttpResponse: ).render(request) +@internal_region_silo_view class DebugSsoUnlinkedEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context(request) @@ -37,6 +40,7 @@ def get(self, request: HttpRequest) -> HttpResponse: ).render(request) +@internal_region_silo_view class DebugSsoUnlinkedNoPasswordEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context(request) diff --git a/src/sentry/web/frontend/debug/debug_trigger_error.py b/src/sentry/web/frontend/debug/debug_trigger_error.py index b757c6c06694d1..d14bc06f707f17 100644 --- a/src/sentry/web/frontend/debug/debug_trigger_error.py +++ b/src/sentry/web/frontend/debug/debug_trigger_error.py @@ -3,9 +3,11 @@ from django.views.generic import View from sentry.utils.sdk import capture_exception +from sentry.web.frontend.base import internal_region_silo_view from sentry.web.frontend.error_500 import Error500View +@internal_region_silo_view class DebugTriggerErrorView(View): def get(self, request: HttpRequest) -> HttpResponseBase: try: diff --git a/src/sentry/web/frontend/debug/debug_unable_to_delete_repository.py b/src/sentry/web/frontend/debug/debug_unable_to_delete_repository.py index f397c12f8a708b..ce35b7657fdb5c 100644 --- a/src/sentry/web/frontend/debug/debug_unable_to_delete_repository.py +++ b/src/sentry/web/frontend/debug/debug_unable_to_delete_repository.py @@ -3,10 +3,12 @@ from sentry.models.repository import Repository from sentry.plugins.providers.dummy import DummyRepositoryProvider +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugUnableToDeleteRepository(View): def get(self, request: HttpRequest) -> HttpResponse: repo = Repository(name="getsentry/sentry", provider="dummy") diff --git a/src/sentry/web/frontend/debug/debug_unable_to_fetch_commits_email.py b/src/sentry/web/frontend/debug/debug_unable_to_fetch_commits_email.py index 7271678e2aa5a0..fb7e17eb21359d 100644 --- a/src/sentry/web/frontend/debug/debug_unable_to_fetch_commits_email.py +++ b/src/sentry/web/frontend/debug/debug_unable_to_fetch_commits_email.py @@ -5,10 +5,12 @@ from sentry.models.release import Release from sentry.models.repository import Repository from sentry.tasks.commits import generate_fetch_commits_error_email +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreview +@internal_region_silo_view class DebugUnableToFetchCommitsEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: org = Organization(slug="myorg") diff --git a/src/sentry/web/frontend/debug/debug_unassigned_email.py b/src/sentry/web/frontend/debug/debug_unassigned_email.py index b2414ba53466b8..c8eebfa6016921 100644 --- a/src/sentry/web/frontend/debug/debug_unassigned_email.py +++ b/src/sentry/web/frontend/debug/debug_unassigned_email.py @@ -1,10 +1,12 @@ from django.http import HttpRequest from sentry.types.activity import ActivityType +from sentry.web.frontend.base import internal_region_silo_view from .mail import ActivityMailDebugView +@internal_region_silo_view class DebugUnassignedEmailView(ActivityMailDebugView): def get_activity(self, request: HttpRequest, event): return {"type": ActivityType.UNASSIGNED.value, "user_id": request.user.id} diff --git a/src/sentry/web/frontend/debug/debug_uptime_auto_detected_monitor_email.py b/src/sentry/web/frontend/debug/debug_uptime_auto_detected_monitor_email.py index b0dcf2e190fd71..cb183f54382f44 100644 --- a/src/sentry/web/frontend/debug/debug_uptime_auto_detected_monitor_email.py +++ b/src/sentry/web/frontend/debug/debug_uptime_auto_detected_monitor_email.py @@ -3,6 +3,8 @@ from django.http import HttpRequest, HttpResponse from django.views.generic import View +from sentry.web.frontend.base import internal_region_silo_view + from .mail import MailPreview @@ -17,6 +19,7 @@ def get_context(): } +@internal_region_silo_view class DebugUptimeAutoDetectedMonitorEmailView(View): def get(self, request: HttpRequest) -> HttpResponse: context = get_context() diff --git a/src/sentry/web/frontend/debug/debug_weekly_report.py b/src/sentry/web/frontend/debug/debug_weekly_report.py index de4ca6b517c898..7f813d3c1578bc 100644 --- a/src/sentry/web/frontend/debug/debug_weekly_report.py +++ b/src/sentry/web/frontend/debug/debug_weekly_report.py @@ -11,6 +11,7 @@ from sentry.tasks.summaries.weekly_reports import render_template_context from sentry.utils import loremipsum from sentry.utils.dates import floor_to_utc_day, to_datetime +from sentry.web.frontend.base import internal_region_silo_view from .mail import MailPreviewView @@ -20,6 +21,7 @@ def get_random(request): return Random(seed) +@internal_region_silo_view class DebugWeeklyReportView(MailPreviewView): def get_context(self, request): organization = Organization(id=1, slug="myorg", name="MyOrg") diff --git a/src/sentry/web/frontend/debug/mail.py b/src/sentry/web/frontend/debug/mail.py index 8aaa2b6bcfa943..19c98944c9dd59 100644 --- a/src/sentry/web/frontend/debug/mail.py +++ b/src/sentry/web/frontend/debug/mail.py @@ -67,6 +67,7 @@ from sentry.utils.http import absolute_uri from sentry.utils.samples import load_data from sentry.web.decorators import login_required +from sentry.web.frontend.base import control_silo_view, internal_region_silo_view from sentry.web.helpers import render_to_response, render_to_string logger = logging.getLogger(__name__) @@ -410,6 +411,7 @@ def html_body(self): raise +@internal_region_silo_view class ActivityMailDebugView(View): def get_activity(self, request: AuthenticatedHttpRequest, event): raise NotImplementedError @@ -446,6 +448,7 @@ def get(self, request: AuthenticatedHttpRequest) -> HttpResponse: ) +@internal_region_silo_view @login_required def alert(request): random = get_random(request) @@ -489,6 +492,7 @@ def alert(request): ).render(request) +@internal_region_silo_view @login_required def digest(request): random = get_random(request) @@ -611,6 +615,7 @@ def digest(request): ).render(request) +@internal_region_silo_view @login_required def request_access(request): org = Organization(id=1, slug="sentry", name="Sentry org") @@ -631,6 +636,7 @@ def request_access(request): ).render(request) +@internal_region_silo_view @login_required def request_access_for_another_member(request): org = Organization(id=1, slug="sentry", name="Sentry org") @@ -652,6 +658,7 @@ def request_access_for_another_member(request): ).render(request) +@internal_region_silo_view @login_required def invitation(request): org = Organization(id=1, slug="example", name="Example") @@ -673,6 +680,7 @@ def invitation(request): ).render(request) +@internal_region_silo_view @login_required def access_approved(request): org = Organization(id=1, slug="example", name="Example") @@ -690,6 +698,7 @@ def access_approved(request): ).render(request) +@internal_region_silo_view @login_required def confirm_email(request): email = request.user.emails.first() @@ -711,6 +720,7 @@ def confirm_email(request): ).render(request) +@internal_region_silo_view @login_required def recover_account(request): return MailPreview( @@ -731,6 +741,7 @@ def recover_account(request): ).render(request) +@control_silo_view @login_required def relocate_account(request): password_hash, __ = LostPasswordHash.objects.get_or_create(user_id=request.user.id) @@ -753,6 +764,7 @@ def relocate_account(request): ).render(request) +@internal_region_silo_view @login_required def relocation_failed(request): return MailPreview( @@ -767,6 +779,7 @@ def relocation_failed(request): ).render(request) +@internal_region_silo_view @login_required def relocation_started(request): return MailPreview( @@ -781,6 +794,7 @@ def relocation_started(request): ).render(request) +@internal_region_silo_view @login_required def relocation_succeeded(request): return MailPreview( @@ -795,6 +809,7 @@ def relocation_succeeded(request): ).render(request) +@internal_region_silo_view @login_required def org_delete_confirm(request): from sentry.models.auditlogentry import AuditLogEntry diff --git a/src/sentry_plugins/bitbucket/endpoints/webhook.py b/src/sentry_plugins/bitbucket/endpoints/webhook.py index 0f118be67c2178..01707797910cbc 100644 --- a/src/sentry_plugins/bitbucket/endpoints/webhook.py +++ b/src/sentry_plugins/bitbucket/endpoints/webhook.py @@ -19,6 +19,7 @@ from sentry.plugins.providers import RepositoryProvider from sentry.utils import json from sentry.utils.email import parse_email +from sentry.web.frontend.base import region_silo_view logger = logging.getLogger("sentry.webhooks") @@ -79,6 +80,7 @@ def __call__(self, organization_id: int, event): pass +@region_silo_view class BitbucketPluginWebhookEndpoint(View): _handlers = {"repo:push": PushEventWebhook} diff --git a/src/sentry_plugins/github/webhooks/integration.py b/src/sentry_plugins/github/webhooks/integration.py index a16ce37dee6acb..012537974ab341 100644 --- a/src/sentry_plugins/github/webhooks/integration.py +++ b/src/sentry_plugins/github/webhooks/integration.py @@ -10,6 +10,7 @@ from sentry import options from sentry.models.organization import Organization +from sentry.web.frontend.base import all_silo_view from .base import GithubWebhookBase from .events import InstallationEventWebhook, InstallationRepositoryEventWebhook, PushEventWebhook @@ -17,6 +18,7 @@ logger = logging.getLogger(__name__) +@all_silo_view class GithubPluginIntegrationsWebhookEndpoint(GithubWebhookBase): _handlers = { "push": PushEventWebhook, diff --git a/src/sentry_plugins/github/webhooks/non_integration.py b/src/sentry_plugins/github/webhooks/non_integration.py index f44a2cdf026ea5..b8ad11fd1819a3 100644 --- a/src/sentry_plugins/github/webhooks/non_integration.py +++ b/src/sentry_plugins/github/webhooks/non_integration.py @@ -7,12 +7,14 @@ from sentry.models.options.organization_option import OrganizationOption from sentry.models.organization import Organization +from sentry.web.frontend.base import region_silo_view from .base import GithubWebhookBase logger = logging.getLogger("sentry.webhooks") +@region_silo_view class GithubPluginWebhookEndpoint(GithubWebhookBase): def get_logging_data(self, organization): return {"organization_id": organization.id} diff --git a/static/app/data/controlsiloUrlPatterns.ts b/static/app/data/controlsiloUrlPatterns.ts index 844dfa5a4fa04c..9deaac0e1cb832 100644 --- a/static/app/data/controlsiloUrlPatterns.ts +++ b/static/app/data/controlsiloUrlPatterns.ts @@ -2,6 +2,7 @@ // To update it run `getsentry django generate_controlsilo_urls --format=js --output=/path/to/thisfile.ts` const patterns: RegExp[] = [ new RegExp('^remote/heroku/resources(?:/[^/]+)?$'), + new RegExp('^remote/heroku/sso-login/?$'), new RegExp('^remote/beacon/$'), new RegExp('^remote/newsletter/unsubscribe/$'), new RegExp('^remote/github-copilot/oauth/$'), @@ -16,8 +17,13 @@ const patterns: RegExp[] = [ new RegExp('^remote/vercel/v1/installations/[^/]+/resources$'), new RegExp('^remote/vercel/v1/installations/[^/]+/resources/[^/]+$'), new RegExp('^remote/vercel/oauth$'), + new RegExp('^$'), + new RegExp('^support/$'), + new RegExp('^identity/login/[^/]+/$'), new RegExp('^orgredirect/try-business/$'), new RegExp('^orgredirect/'), + new RegExp('^associate/complete/[^/]+/$'), + new RegExp('^associate/[^/]+/$'), new RegExp('^api/0/staff-auth/$'), new RegExp('^api/0/signup/$'), new RegExp('^api/0/audit-logs/$'), @@ -50,6 +56,7 @@ const patterns: RegExp[] = [ new RegExp('^api/0/sponsored_account_request/$'), new RegExp('^api/0/migrate_to_hosted/$'), new RegExp('^api/0/sponsored_education_account/$'), + new RegExp('^organizations/[^/]+/documents/[^/]+/?$'), new RegExp('^api/0/organizations/[^/]+/broadcasts/$'), new RegExp('^api/0/billing-config/$'), new RegExp('^api/0/auth-details/$'), @@ -58,8 +65,15 @@ const patterns: RegExp[] = [ new RegExp('^_admin/'), new RegExp('^500/'), new RegExp('^404/'), + new RegExp('^403-csrf-failure/'), + new RegExp('^debug/mail/relocate-account/$'), new RegExp('^_warmup/$'), new RegExp('^api/client-config/?$'), + new RegExp('^api/relay/.*$'), + new RegExp('^_static/dist/[^/]+/[^/]+$'), + new RegExp('^_static/(?:[^/]+/)?[^/]+/[^/]+$'), + new RegExp('^get-cli/$'), + new RegExp('^get-cli/[^/]+/[^/]+/?$'), new RegExp('^api/0/organizations/[^/]+/api-keys/$'), new RegExp('^api/0/organizations/[^/]+/api-keys/[^/]+/$'), new RegExp('^api/0/organizations/[^/]+/audit-logs/$'), @@ -172,15 +186,28 @@ const patterns: RegExp[] = [ new RegExp('^auth/channel/[^/]+/[^/]+/$'), new RegExp('^auth/link/[^/]+/$'), new RegExp('^auth/2fa/$'), + new RegExp('^auth/2fa/u2fappid\\.json$'), new RegExp('^auth/sso/$'), new RegExp('^auth/logout/$'), new RegExp('^auth/reactivate/$'), new RegExp('^auth/register/$'), new RegExp('^auth/close/$'), + new RegExp('^login-redirect/$'), new RegExp('^account/sudo/$'), + new RegExp('^account/confirm-email/$'), + new RegExp('^account/confirm-email/[^/]+/[^/]+/$'), + new RegExp('^account/confirm-signed-email/[^/]+/$'), new RegExp('^account/user-confirm/[^/]+/$'), + new RegExp('^account/recover/$'), + new RegExp('^account/recover/confirm/[^/]+/[^/]+/$'), + new RegExp('^account/relocation/reclaim/[^/]+/$'), + new RegExp('^account/password/confirm/[^/]+/[^/]+/$'), + new RegExp('^account/relocation/confirm/[^/]+/[^/]+/$'), new RegExp('^account/settings/identities/associate/[^/]+/[^/]+/[^/]+/$'), new RegExp('^account/settings/wizard/[^/]+/$'), + new RegExp('^account/settings/social/associate/complete/[^/]+/$'), + new RegExp('^account/settings/social/associate/[^/]+/$'), + new RegExp('^auth/sso/account/settings/social/associate/complete/[^/]+/$'), new RegExp('^out/$'), new RegExp('^settings/organization/auth/configure/$'), new RegExp('^disabled-member/'), @@ -190,6 +217,10 @@ const patterns: RegExp[] = [ new RegExp('^avatar/[^/]+/$'), new RegExp('^sentry-app-avatar/[^/]+/$'), new RegExp('^doc-integration-avatar/[^/]+/$'), + new RegExp('^_chartcuterie-config\\.js$'), + new RegExp('^robots\\.txt$'), + new RegExp('^\\.well-known/security\\.txt$'), + new RegExp('^\\.well-known/mcp\\.json$'), new RegExp('^extensions/[^/]+/setup/$'), new RegExp('^extensions/jira/ui-hook/$'), new RegExp('^extensions/jira/descriptor/$'), @@ -223,6 +254,7 @@ const patterns: RegExp[] = [ new RegExp('^extensions/discord/configure/$'), new RegExp('^extensions/discord/link-identity/[^/]+/$'), new RegExp('^extensions/discord/unlink-identity/[^/]+/$'), + new RegExp('^plugins/github/installations/webhook/$'), new RegExp('^share/(?:group|issue)/[^/]+/$'), ]; diff --git a/tests/sentry/web/frontend/test_base.py b/tests/sentry/web/frontend/test_base.py index 0e7e8dcd904bdb..96e638e014cd3e 100644 --- a/tests/sentry/web/frontend/test_base.py +++ b/tests/sentry/web/frontend/test_base.py @@ -9,11 +9,11 @@ class ViewSiloLimitTest(APITestCase): def _test_active_on(self, endpoint_mode, active_mode, expect_to_be_active): - @ViewSiloLimit(endpoint_mode) + @ViewSiloLimit([endpoint_mode]) def view_func(request): pass - @ViewSiloLimit(endpoint_mode) + @ViewSiloLimit([endpoint_mode]) class DummyView(BaseView): def get(self, request): return Response("dummy-view", 200) @@ -43,3 +43,23 @@ def test_with_inactive_mode(self) -> None: def test_with_monolith_mode(self) -> None: self._test_active_on(SiloMode.REGION, SiloMode.MONOLITH, True) self._test_active_on(SiloMode.CONTROL, SiloMode.MONOLITH, True) + + def test_sets_silo_limit_on_function(self) -> None: + @ViewSiloLimit([SiloMode.CONTROL]) + def view_func(request): + pass + + assert view_func.silo_limit, "Should have silo_limit set" + assert view_func.silo_limit.modes, "Should have silo_limit.modes set" + assert not view_func.silo_limit.internal, "Not internal by default" + + def test_internal_attribute(self) -> None: + @ViewSiloLimit([SiloMode.REGION], internal=True) + def view_func(request): + pass + + assert view_func.silo_limit, "Should have silo_limit set" + assert view_func.silo_limit.modes, "Should have silo_limit.modes set" + assert len(view_func.silo_limit.modes) == 1 + assert SiloMode.REGION in view_func.silo_limit.modes + assert view_func.silo_limit.internal, "Should be marked as internal"