From 3bb071d2cddd343285ff5a2d9d9174ea10c5a602 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Tue, 25 Nov 2025 14:56:17 -0800 Subject: [PATCH 1/2] fix(aci): handle fake alert rule ids in AlertRuleDetector lookup --- .../organization_alertrule_detector_index.py | 27 ++++++++++++++-- .../test_organization_alertrule_detector.py | 31 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py b/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py index 334b00a6933f53..0bf99d1ba6eef0 100644 --- a/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py +++ b/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py @@ -16,6 +16,7 @@ RESPONSE_UNAUTHORIZED, ) from sentry.apidocs.parameters import GlobalParams +from sentry.incidents.endpoints.serializers.utils import get_object_id_from_fake_id from sentry.models.organization import Organization from sentry.workflow_engine.endpoints.serializers.alertrule_detector_serializer import ( AlertRuleDetectorSerializer, @@ -24,6 +25,7 @@ AlertRuleDetectorValidator, ) from sentry.workflow_engine.models.alertrule_detector import AlertRuleDetector +from sentry.workflow_engine.models.detector import Detector @region_silo_endpoint @@ -69,7 +71,26 @@ def get(self, request: Request, organization: Organization) -> Response: queryset = queryset.filter(rule_id=rule_id) alert_rule_detector = queryset.first() - if not alert_rule_detector: - raise ResourceDoesNotExist - return Response(serialize(alert_rule_detector, request.user)) + if alert_rule_detector: + return Response(serialize(alert_rule_detector, request.user)) + + # Fallback: if alert_rule_id was provided but no AlertRuleDetector was found, + # try looking up Detector directly using calculated detector_id + if alert_rule_id: + try: + calculated_detector_id = get_object_id_from_fake_id(int(alert_rule_id)) + detector = Detector.objects.get(id=calculated_detector_id) + + if detector: + return Response( + { + "detectorId": str(detector.id), + "alertRuleId": str(alert_rule_id), + "ruleId": None, + } + ) + except (ValueError, Detector.DoesNotExist): + pass + + raise ResourceDoesNotExist diff --git a/tests/sentry/workflow_engine/endpoints/test_organization_alertrule_detector.py b/tests/sentry/workflow_engine/endpoints/test_organization_alertrule_detector.py index e10d5ce93bb94d..b180d63a984f3f 100644 --- a/tests/sentry/workflow_engine/endpoints/test_organization_alertrule_detector.py +++ b/tests/sentry/workflow_engine/endpoints/test_organization_alertrule_detector.py @@ -1,4 +1,5 @@ from sentry.api.serializers import serialize +from sentry.incidents.endpoints.serializers.utils import get_fake_id_from_object_id from sentry.testutils.cases import APITestCase from sentry.testutils.silo import region_silo_test @@ -89,3 +90,33 @@ def test_organization_isolation(self) -> None: def test_get_without_any_filter(self) -> None: self.get_error_response(self.organization.slug, status_code=400) + + def test_fallback_with_fake_alert_rule_id(self) -> None: + """ + Test that when an alert rule doesn't exist, the endpoint falls back to looking up + the Detector by subtracting 10^9 from the alert_rule_id. + """ + # Create a detector with no AlertRuleDetector mapping + detector = self.create_detector(project=self.project) + + # Calculate the fake alert_rule_id + fake_alert_rule_id = get_fake_id_from_object_id(detector.id) + + # Query using the fake alert_rule_id + response = self.get_success_response( + self.organization.slug, alert_rule_id=str(fake_alert_rule_id) + ) + + # Should return a fake AlertRuleDetector response + assert response.data == { + "detectorId": str(detector.id), + "alertRuleId": str(fake_alert_rule_id), + "ruleId": None, + } + + def test_fallback_with_nonexistent_detector(self) -> None: + # Use a fake alert_rule_id that won't map to any real detector + nonexistent_fake_id = get_fake_id_from_object_id(999999) + self.get_error_response( + self.organization.slug, alert_rule_id=str(nonexistent_fake_id), status_code=404 + ) From 70741c8e8e3577d1565e5e1a71b47de6b4461e04 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Tue, 25 Nov 2025 15:10:06 -0800 Subject: [PATCH 2/2] filter by organization --- .../endpoints/organization_alertrule_detector_index.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py b/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py index 0bf99d1ba6eef0..5141ea202afe01 100644 --- a/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py +++ b/src/sentry/workflow_engine/endpoints/organization_alertrule_detector_index.py @@ -80,7 +80,9 @@ def get(self, request: Request, organization: Organization) -> Response: if alert_rule_id: try: calculated_detector_id = get_object_id_from_fake_id(int(alert_rule_id)) - detector = Detector.objects.get(id=calculated_detector_id) + detector = Detector.objects.get( + id=calculated_detector_id, project__organization=organization + ) if detector: return Response(