Skip to content

Commit efd1654

Browse files
authored
Merge branch 'develop' into missing-migrations
2 parents fac8f4c + 7ad2792 commit efd1654

File tree

5 files changed

+103
-21
lines changed

5 files changed

+103
-21
lines changed

src/hope/apps/utils/sentry.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
from functools import wraps
22
import logging
3-
from typing import TYPE_CHECKING, Callable, ParamSpec, TypeVar
3+
from typing import TYPE_CHECKING, Callable
44

5-
from sentry_sdk import configure_scope, set_tag
5+
import sentry_sdk
6+
from sentry_sdk import set_tag
67

78
if TYPE_CHECKING:
89
from sentry_sdk.types import Event, Hint
910

1011
log = logging.getLogger(__name__)
1112

12-
P = ParamSpec("P")
13-
R = TypeVar("R")
14-
1513

1614
def sentry_tags[**P, R](func: Callable[P, R]) -> Callable[P, R]:
1715
"""Add sentry tags 'celery' and 'celery_task'."""
1816

1917
@wraps(func)
2018
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
21-
with configure_scope() as scope:
22-
scope.set_tag("celery", True)
23-
scope.set_tag("celery_task", func.__name__)
19+
scope = sentry_sdk.get_isolation_scope()
20+
scope.set_tag("celery", True)
21+
scope.set_tag("celery_task", func.__name__)
2422

25-
return func(*args, **kwargs)
23+
return func(*args, **kwargs)
2624

2725
return wrapper
2826

src/hope/middlewares/sentry.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from django.http import HttpRequest
66
import pycountry
7-
from sentry_sdk import configure_scope
7+
import sentry_sdk
88

99

1010
def is_country_name(country_name: str) -> bool:
@@ -25,14 +25,14 @@ def __init__(self, get_response: Callable) -> None:
2525
# Note: must be listed AFTER AuthenticationMiddleware
2626
def __call__(self, request: HttpRequest) -> Any:
2727
sys.stderr.isatty = lambda: False # type: ignore # I guess this is a hack to make Sentry not use colors in the terminal?
28-
with configure_scope() as scope:
29-
business_area = request.headers.get("Business-Area")
30-
if not business_area:
31-
# example: api/rest/ukraine/rdi/upload/; api/admin/account/role/;
32-
# all api urls with BA has pattern 'api/rest/BA/etc'
33-
pattern = r"api/rest/(?P<country>[^/]+)/"
34-
match = re.search(pattern, request.path)
35-
business_area = match.group("country") if match else self.business_area
36-
scope.set_tag("username", request.user.username)
37-
scope.set_tag("business_area", business_area)
38-
return self.get_response(request)
28+
scope = sentry_sdk.get_isolation_scope()
29+
business_area = request.headers.get("Business-Area")
30+
if not business_area:
31+
# example: api/rest/ukraine/rdi/upload/; api/admin/account/role/;
32+
# all api urls with BA has pattern 'api/rest/BA/etc'
33+
pattern = r"api/rest/(?P<country>[^/]+)/"
34+
match = re.search(pattern, request.path)
35+
business_area = match.group("country") if match else self.business_area
36+
scope.set_tag("username", request.user.username)
37+
scope.set_tag("business_area", business_area)
38+
return self.get_response(request)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
from hope.apps.utils.sentry import sentry_tags
4+
5+
6+
@patch("hope.apps.utils.sentry.sentry_sdk")
7+
def test_sentry_tags_sets_celery_scope_tags_and_preserves_return_value(
8+
mocked_sentry: MagicMock,
9+
) -> None:
10+
scope = MagicMock()
11+
mocked_sentry.get_isolation_scope.return_value = scope
12+
13+
@sentry_tags
14+
def my_task(value: int) -> int:
15+
return value * 2
16+
17+
result = my_task(21)
18+
19+
assert result == 42
20+
assert my_task.__name__ == "my_task"
21+
mocked_sentry.get_isolation_scope.assert_called_once_with()
22+
scope.set_tag.assert_any_call("celery", True)
23+
scope.set_tag.assert_any_call("celery_task", "my_task")

tests/unit/middlewares/__init__.py

Whitespace-only changes.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
from hope.middlewares.sentry import SentryScopeMiddleware
4+
5+
6+
@patch("hope.middlewares.sentry.sentry_sdk")
7+
def test_call_uses_business_area_header_when_present(mocked_sentry: MagicMock) -> None:
8+
scope = MagicMock()
9+
mocked_sentry.get_isolation_scope.return_value = scope
10+
response_sentinel = object()
11+
get_response = MagicMock(return_value=response_sentinel)
12+
middleware = SentryScopeMiddleware(get_response)
13+
request = MagicMock()
14+
request.headers = {"Business-Area": "ukraine"}
15+
request.path = "/api/rest/afghanistan/rdi/upload/"
16+
request.user.username = "alice"
17+
18+
result = middleware(request)
19+
20+
assert result is response_sentinel
21+
get_response.assert_called_once_with(request)
22+
scope.set_tag.assert_any_call("username", "alice")
23+
scope.set_tag.assert_any_call("business_area", "ukraine")
24+
25+
26+
@patch("hope.middlewares.sentry.sentry_sdk")
27+
def test_call_derives_business_area_from_api_rest_path_when_header_missing(
28+
mocked_sentry: MagicMock,
29+
) -> None:
30+
scope = MagicMock()
31+
mocked_sentry.get_isolation_scope.return_value = scope
32+
get_response = MagicMock()
33+
middleware = SentryScopeMiddleware(get_response)
34+
request = MagicMock()
35+
request.headers = {}
36+
request.path = "/api/rest/ukraine/rdi/upload/"
37+
request.user.username = "bob"
38+
39+
middleware(request)
40+
41+
scope.set_tag.assert_any_call("username", "bob")
42+
scope.set_tag.assert_any_call("business_area", "ukraine")
43+
44+
45+
@patch("hope.middlewares.sentry.sentry_sdk")
46+
def test_call_falls_back_to_default_business_area_when_path_does_not_match(
47+
mocked_sentry: MagicMock,
48+
) -> None:
49+
scope = MagicMock()
50+
mocked_sentry.get_isolation_scope.return_value = scope
51+
get_response = MagicMock()
52+
middleware = SentryScopeMiddleware(get_response)
53+
request = MagicMock()
54+
request.headers = {}
55+
request.path = "/api/admin/account/role/"
56+
request.user.username = "carol"
57+
58+
middleware(request)
59+
60+
scope.set_tag.assert_any_call("username", "carol")
61+
scope.set_tag.assert_any_call("business_area", "NO_BA")

0 commit comments

Comments
 (0)