Skip to content

Commit 1c4782e

Browse files
committed
feat(crons): Add get_detector_for_monitor function
Add a simple function to get the detector associated with a cron monitor, if one exists.
1 parent a37d0c5 commit 1c4782e

File tree

4 files changed

+59
-66
lines changed

4 files changed

+59
-66
lines changed

src/sentry/monitors/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,13 @@ def ensure_cron_detector(monitor: Monitor):
410410
DataSourceDetector.objects.create(data_source=data_source, detector=detector)
411411
except Exception:
412412
logger.exception("Error creating cron detector")
413+
414+
415+
def get_detector_for_monitor(monitor: Monitor) -> Detector | None:
416+
try:
417+
return Detector.objects.get(
418+
datasource__type=DATA_SOURCE_CRON_MONITOR,
419+
datasource__source_id=str(monitor.id),
420+
)
421+
except Detector.DoesNotExist:
422+
return None

tests/sentry/monitors/consumers/test_monitor_consumer.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
ScheduleType,
3030
)
3131
from sentry.monitors.processing_errors.errors import ProcessingErrorsException, ProcessingErrorType
32-
from sentry.monitors.types import DATA_SOURCE_CRON_MONITOR, CheckinItem
32+
from sentry.monitors.types import CheckinItem
33+
from sentry.monitors.utils import get_detector_for_monitor
3334
from sentry.testutils.asserts import assert_org_audit_log_exists
3435
from sentry.testutils.cases import TestCase
3536
from sentry.testutils.helpers.options import override_options
3637
from sentry.testutils.outbox import outbox_runner
3738
from sentry.utils import json
3839
from sentry.utils.outcomes import Outcome
39-
from sentry.workflow_engine.models import Detector
4040

4141

4242
class ExpectNoProcessingError:
@@ -573,10 +573,7 @@ def test_monitor_create(self) -> None:
573573
monitor_environment.next_checkin_latest
574574
== monitor_environment.monitor.get_next_expected_checkin_latest(checkin.date_added)
575575
)
576-
assert Detector.objects.filter(
577-
datasource__type=DATA_SOURCE_CRON_MONITOR,
578-
datasource__source_id=str(monitor_environment.monitor_id),
579-
).exists()
576+
get_detector_for_monitor(monitor_environment.monitor)
580577

581578
def test_monitor_create_owner(self) -> None:
582579
self.send_checkin(

tests/sentry/monitors/endpoints/test_organization_monitor_index.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@
1212
from sentry.constants import ObjectStatus
1313
from sentry.models.rule import Rule, RuleSource
1414
from sentry.monitors.models import Monitor, MonitorStatus, ScheduleType
15-
from sentry.monitors.types import DATA_SOURCE_CRON_MONITOR
15+
from sentry.monitors.utils import get_detector_for_monitor
1616
from sentry.quotas.base import SeatAssignmentResult
1717
from sentry.slug.errors import DEFAULT_SLUG_ERROR_MESSAGE
1818
from sentry.testutils.asserts import assert_org_audit_log_exists
1919
from sentry.testutils.cases import MonitorTestCase
2020
from sentry.testutils.outbox import outbox_runner
2121
from sentry.utils.outcomes import Outcome
22-
from sentry.workflow_engine.models import Detector
2322

2423

2524
class ListOrganizationMonitorsTest(MonitorTestCase):
@@ -404,11 +403,7 @@ def test_simple(self, mock_record: MagicMock) -> None:
404403
data={"upsert": False, **monitor.get_audit_log_data()},
405404
)
406405

407-
assert Detector.objects.filter(
408-
datasource__type=DATA_SOURCE_CRON_MONITOR,
409-
datasource__source_id=str(monitor.id),
410-
).exists()
411-
406+
assert get_detector_for_monitor(monitor) is not None
412407
self.project.refresh_from_db()
413408
assert self.project.flags.has_cron_monitors
414409

tests/sentry/monitors/test_utils.py

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
from django.db import IntegrityError
44

55
from sentry.monitors.types import DATA_SOURCE_CRON_MONITOR
6-
from sentry.monitors.utils import ensure_cron_detector
6+
from sentry.monitors.utils import ensure_cron_detector, get_detector_for_monitor
77
from sentry.testutils.cases import TestCase
8-
from sentry.workflow_engine.models import DataSource, DataSourceDetector, Detector
8+
from sentry.workflow_engine.models import DataSource, Detector
99

1010

1111
class EnsureCronDetectorTest(TestCase):
@@ -14,66 +14,22 @@ def setUp(self):
1414
self.monitor = self.create_monitor(owner_user_id=None)
1515

1616
def test_creates_data_source_and_detector_for_new_monitor(self):
17-
assert not DataSource.objects.filter(
18-
type=DATA_SOURCE_CRON_MONITOR,
19-
organization_id=self.monitor.organization_id,
20-
source_id=str(self.monitor.id),
21-
).exists()
22-
17+
assert not get_detector_for_monitor(self.monitor)
2318
ensure_cron_detector(self.monitor)
24-
data_source = DataSource.objects.get(
25-
type=DATA_SOURCE_CRON_MONITOR,
26-
organization_id=self.monitor.organization_id,
27-
source_id=str(self.monitor.id),
28-
)
29-
assert data_source is not None
30-
detector = Detector.objects.get(
31-
type="monitor_check_in_failure",
32-
project_id=self.monitor.project_id,
33-
name=self.monitor.name,
34-
)
19+
detector = get_detector_for_monitor(self.monitor)
3520
assert detector is not None
21+
assert detector.type == "monitor_check_in_failure"
22+
assert detector.project_id == self.monitor.project_id
23+
assert detector.name == self.monitor.name
3624
assert detector.owner_user_id == self.monitor.owner_user_id
3725
assert detector.owner_team_id == self.monitor.owner_team_id
38-
assert DataSourceDetector.objects.filter(
39-
data_source=data_source,
40-
detector=detector,
41-
).exists()
4226

4327
def test_idempotent_for_existing_data_source(self):
4428
ensure_cron_detector(self.monitor)
45-
data_source = DataSource.objects.get(
46-
type=DATA_SOURCE_CRON_MONITOR,
47-
organization_id=self.monitor.organization_id,
48-
source_id=str(self.monitor.id),
49-
)
50-
detector = Detector.objects.get(
51-
type="monitor_check_in_failure",
52-
project_id=self.monitor.project_id,
53-
name=self.monitor.name,
54-
)
55-
link = DataSourceDetector.objects.get(
56-
data_source=data_source,
57-
detector=detector,
58-
)
29+
detector = get_detector_for_monitor(self.monitor)
5930
ensure_cron_detector(self.monitor)
60-
data_source_after = DataSource.objects.get(
61-
type=DATA_SOURCE_CRON_MONITOR,
62-
organization_id=self.monitor.organization_id,
63-
source_id=str(self.monitor.id),
64-
)
65-
detector_after = Detector.objects.get(
66-
type="monitor_check_in_failure",
67-
project_id=self.monitor.project_id,
68-
name=self.monitor.name,
69-
)
70-
link_after = DataSourceDetector.objects.get(
71-
data_source=data_source,
72-
detector=detector,
73-
)
74-
assert data_source.id == data_source_after.id
31+
detector_after = get_detector_for_monitor(self.monitor)
7532
assert detector.id == detector_after.id
76-
assert link.id == link_after.id
7733

7834
def test_with_owner_user(self):
7935
self.monitor.owner_user_id = self.user.id
@@ -117,3 +73,38 @@ def test_atomic_transaction_rollback(self):
11773
assert not DataSource.objects.filter(
11874
type=DATA_SOURCE_CRON_MONITOR, source_id=str(self.monitor.id)
11975
).exists()
76+
77+
78+
class GetDetectorForMonitorTest(TestCase):
79+
def setUp(self):
80+
super().setUp()
81+
self.monitor = self.create_monitor()
82+
83+
def test_returns_none_when_no_detector_exists(self):
84+
detector = get_detector_for_monitor(self.monitor)
85+
assert detector is None
86+
87+
def test_returns_detector_when_exists(self):
88+
ensure_cron_detector(self.monitor)
89+
90+
detector = get_detector_for_monitor(self.monitor)
91+
assert detector is not None
92+
assert detector.type == "monitor_check_in_failure"
93+
assert detector.project_id == self.monitor.project_id
94+
assert detector.name == self.monitor.name
95+
96+
def test_returns_correct_detector_for_specific_monitor(self):
97+
monitor1 = self.monitor
98+
monitor2 = self.create_monitor(name="Monitor 2")
99+
100+
ensure_cron_detector(monitor1)
101+
ensure_cron_detector(monitor2)
102+
103+
detector1 = get_detector_for_monitor(monitor1)
104+
detector2 = get_detector_for_monitor(monitor2)
105+
106+
assert detector1 is not None
107+
assert detector2 is not None
108+
assert detector1.id != detector2.id
109+
assert detector1.name == monitor1.name
110+
assert detector2.name == monitor2.name

0 commit comments

Comments
 (0)