From 07aacf96672d35f855918f9112d60c3cfaee6ba4 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Tue, 2 Apr 2024 12:29:14 +0200 Subject: [PATCH 1/2] feat(crons): Allow to upsert monitors --- sentry_sdk/crons/decorator.py | 12 +++-- tests/crons/test_crons.py | 69 +++++++++++++++++++++++++++-- tests/crons/test_crons_async_py3.py | 16 +++++-- 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/sentry_sdk/crons/decorator.py b/sentry_sdk/crons/decorator.py index 38653ca161..092e91bc9b 100644 --- a/sentry_sdk/crons/decorator.py +++ b/sentry_sdk/crons/decorator.py @@ -5,7 +5,7 @@ from sentry_sdk.utils import now if TYPE_CHECKING: - from typing import Optional, Type + from typing import Any, Dict, Optional, Type from types import TracebackType if PY2: @@ -47,15 +47,18 @@ def test(arg): ``` """ - def __init__(self, monitor_slug=None): - # type: (Optional[str]) -> None + def __init__(self, monitor_slug=None, monitor_config=None): + # type: (Optional[str], Optional[Dict[str, Any]]) -> None self.monitor_slug = monitor_slug + self.monitor_config = monitor_config def __enter__(self): # type: () -> None self.start_timestamp = now() self.check_in_id = capture_checkin( - monitor_slug=self.monitor_slug, status=MonitorStatus.IN_PROGRESS + monitor_slug=self.monitor_slug, + status=MonitorStatus.IN_PROGRESS, + monitor_config=self.monitor_config, ) def __exit__(self, exc_type, exc_value, traceback): @@ -72,4 +75,5 @@ def __exit__(self, exc_type, exc_value, traceback): check_in_id=self.check_in_id, status=status, duration=duration_s, + monitor_config=self.monitor_config, ) diff --git a/tests/crons/test_crons.py b/tests/crons/test_crons.py index 0b31494acf..1f50a33751 100644 --- a/tests/crons/test_crons.py +++ b/tests/crons/test_crons.py @@ -33,6 +33,22 @@ def _break_world_contextmanager(name): return "Hello, {}".format(name) +@sentry_sdk.monitor(monitor_slug="ghi789", monitor_config=None) +def _no_monitor_config(): + return + + +@sentry_sdk.monitor( + monitor_slug="ghi789", + monitor_config={ + "schedule": {"type": "crontab", "value": "0 0 * * *"}, + "failure_issue_threshold": 5, + }, +) +def _with_monitor_config(): + return + + def test_decorator(sentry_init): sentry_init() @@ -45,7 +61,9 @@ def test_decorator(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="abc123", status="in_progress"), + mock.call( + monitor_slug="abc123", status="in_progress", monitor_config=None + ), ] ) @@ -70,7 +88,9 @@ def test_decorator_error(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="def456", status="in_progress"), + mock.call( + monitor_slug="def456", status="in_progress", monitor_config=None + ), ] ) @@ -93,7 +113,9 @@ def test_contextmanager(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="abc123", status="in_progress"), + mock.call( + monitor_slug="abc123", status="in_progress", monitor_config=None + ), ] ) @@ -118,7 +140,9 @@ def test_contextmanager_error(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="def456", status="in_progress"), + mock.call( + monitor_slug="def456", status="in_progress", monitor_config=None + ), ] ) @@ -194,6 +218,8 @@ def test_monitor_config(sentry_init, capture_envelopes): monitor_config = { "schedule": {"type": "crontab", "value": "0 0 * * *"}, + "failure_issue_threshold": 5, + "recovery_threshold": 5, } capture_checkin(monitor_slug="abc123", monitor_config=monitor_config) @@ -211,6 +237,41 @@ def test_monitor_config(sentry_init, capture_envelopes): assert "monitor_config" not in check_in +def test_decorator_monitor_config(sentry_init, capture_envelopes): + sentry_init() + envelopes = capture_envelopes() + + _with_monitor_config() + + assert len(envelopes) == 2 + + for check_in_envelope in envelopes: + assert len(check_in_envelope.items) == 1 + check_in = check_in_envelope.items[0].payload.json + + assert check_in["monitor_slug"] == "ghi789" + assert check_in["monitor_config"] == { + "schedule": {"type": "crontab", "value": "0 0 * * *"}, + "failure_issue_threshold": 5, + } + + +def test_decorator_no_monitor_config(sentry_init, capture_envelopes): + sentry_init() + envelopes = capture_envelopes() + + _no_monitor_config() + + assert len(envelopes) == 2 + + for check_in_envelope in envelopes: + assert len(check_in_envelope.items) == 1 + check_in = check_in_envelope.items[0].payload.json + + assert check_in["monitor_slug"] == "ghi789" + assert "monitor_config" not in check_in + + def test_capture_checkin_sdk_not_initialized(): # Tests that the capture_checkin does not raise an error when Sentry SDK is not initialized. # sentry_init() is intentionally omitted. diff --git a/tests/crons/test_crons_async_py3.py b/tests/crons/test_crons_async_py3.py index 6e00b594bd..53ec96d713 100644 --- a/tests/crons/test_crons_async_py3.py +++ b/tests/crons/test_crons_async_py3.py @@ -49,7 +49,9 @@ async def test_decorator(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="abc123", status="in_progress"), + mock.call( + monitor_slug="abc123", status="in_progress", monitor_config=None + ), ] ) @@ -75,7 +77,9 @@ async def test_decorator_error(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="def456", status="in_progress"), + mock.call( + monitor_slug="def456", status="in_progress", monitor_config=None + ), ] ) @@ -99,7 +103,9 @@ async def test_contextmanager(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="abc123", status="in_progress"), + mock.call( + monitor_slug="abc123", status="in_progress", monitor_config=None + ), ] ) @@ -125,7 +131,9 @@ async def test_contextmanager_error(sentry_init): # Check for initial checkin fake_capture_checkin.assert_has_calls( [ - mock.call(monitor_slug="def456", status="in_progress"), + mock.call( + monitor_slug="def456", status="in_progress", monitor_config=None + ), ] ) From bfec2e364379fc457498b38711da0de88303c093 Mon Sep 17 00:00:00 2001 From: Ivana Kellyerova Date: Tue, 2 Apr 2024 13:43:28 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Daniel Szoke --- sentry_sdk/crons/decorator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/crons/decorator.py b/sentry_sdk/crons/decorator.py index 092e91bc9b..5bedcb48b0 100644 --- a/sentry_sdk/crons/decorator.py +++ b/sentry_sdk/crons/decorator.py @@ -5,7 +5,7 @@ from sentry_sdk.utils import now if TYPE_CHECKING: - from typing import Any, Dict, Optional, Type + from typing import Any, Optional, Type from types import TracebackType if PY2: @@ -48,7 +48,7 @@ def test(arg): """ def __init__(self, monitor_slug=None, monitor_config=None): - # type: (Optional[str], Optional[Dict[str, Any]]) -> None + # type: (Optional[str], Optional[dict[str, Any]]) -> None self.monitor_slug = monitor_slug self.monitor_config = monitor_config