Skip to content

Commit

Permalink
Ignore GUID generation if url is in IGNORE_REGEX_URLS
Browse files Browse the repository at this point in the history
  • Loading branch information
Saurav Sharma committed Jul 4, 2024
1 parent 9a9b00a commit 00732ca
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 4 deletions.
3 changes: 2 additions & 1 deletion demoproj/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
"""
from django.urls import path

from demoproj.views.sync_views import index_view, no_guid, rest_view
from demoproj.views.sync_views import index_view, no_guid, rest_view, no_guid_regex
from demoproj.views.async_views import index_view as asgi_index_view
from demoproj.views.async_views import django_guid_api_usage

urlpatterns = [
path('', index_view, name='index'),
path('api', rest_view, name='drf'),
path('no-guid', no_guid, name='no_guid'),
path('no-guid-regex', no_guid_regex, name='no_guid_regex'),
path('asgi', asgi_index_view, name='asgi_index'),
path('api-usage', django_guid_api_usage, name='django_guid_api_usage'),
]
9 changes: 9 additions & 0 deletions demoproj/views/sync_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ def no_guid(request: 'HttpRequest') -> JsonResponse:
return JsonResponse({'detail': f'It worked also! Useless function response is {useless_response}'})


def no_guid_regex(request: 'HttpRequest') -> JsonResponse:
"""
Example view with a URL in the IGNORE_REGEX_URLS list - no GUID will be in these logs
"""
logger.info('This log message should NOT have a GUID - the URL is in IGNORE_REGEX_URLS')
useless_response = useless_function()
return JsonResponse({'detail': f'It worked also! Useless function response is {useless_response}'})


@api_view(('GET',))
def rest_view(request: 'Request') -> Response:
"""
Expand Down
4 changes: 4 additions & 0 deletions django_guid/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def expose_header(self) -> bool:
def ignore_urls(self) -> List[str]:
return list({url.strip('/') for url in self.settings.get('IGNORE_URLS', [])})

@property
def ignore_regex_urls(self) -> List[str]:
return list({url.strip('/') for url in self.settings.get('IGNORE_REGEX_URLS', [])})

@property
def validate_guid(self) -> bool:
return self.settings.get('VALIDATE_GUID', True)
Expand Down
6 changes: 3 additions & 3 deletions django_guid/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.core.exceptions import ImproperlyConfigured

from django_guid.context import guid
from django_guid.utils import get_id_from_header, ignored_url
from django_guid.utils import get_id_from_header, ignored_url, ignored_regex_url

try:
from django.utils.decorators import sync_and_async_middleware
Expand All @@ -28,7 +28,7 @@ def process_incoming_request(request: 'HttpRequest') -> None:
Processes an incoming request. This function is called before the view and later middleware.
Same logic for both async and sync views.
"""
if not ignored_url(request=request):
if (not ignored_url(request=request)) and (not ignored_regex_url(request=request)):
# Process request and store the GUID in a contextvar
guid.set(get_id_from_header(request))

Expand All @@ -42,7 +42,7 @@ def process_outgoing_request(response: 'HttpResponse', request: 'HttpRequest') -
"""
Process an outgoing request. This function is called after the view and before later middleware.
"""
if not ignored_url(request=request):
if (not ignored_url(request=request)) and (not ignored_regex_url(request=request)):
if settings.return_header:
response[settings.guid_header_name] = guid.get() # Adds the GUID to the response header
if settings.expose_header:
Expand Down
18 changes: 18 additions & 0 deletions django_guid/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import re
import uuid
from typing import TYPE_CHECKING, Optional, Union

Expand Down Expand Up @@ -91,3 +92,20 @@ def validate_guid(original_guid: str) -> bool:
return bool(uuid.UUID(original_guid, version=4).hex)
except ValueError:
return False


def ignored_regex_url(request: Union['HttpRequest', 'HttpResponse']) -> bool:
"""
Support for Regex added
Checks if the current URL is defined in the `IGNORE_REGEX_URLS` setting.
:return: Boolean
"""
endpoint = request.path.strip('/')

IGNORE_URLS = []
for url in settings.ignore_regex_urls:
url_regex = url.replace('*', r'[\s\S]*') # noqa
url_regex = '^' + url_regex + '$'
IGNORE_URLS.append(re.compile(url_regex))
return any(url.match(endpoint) for url in IGNORE_URLS)
24 changes: 24 additions & 0 deletions tests/functional/test_sync_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,27 @@ def test_url_ignored(client, caplog):
('Received signal `request_finished`, clearing guid', None),
]
assert [(x.message, x.correlation_id) for x in caplog.records] == expected


def test_url_ignored_with_regex(client, caplog, monkeypatch):
"""
Test that a URL specified in IGNORE_URLS is ignored.
:param client: Django client
:param caplog: Caplog fixture
"""
from django.conf import settings as django_settings

mocked_settings = deepcopy(django_settings.DJANGO_GUID)
mocked_settings['IGNORE_REGEX_URLS'] = {'no-*'}
with override_settings(DJANGO_GUID=mocked_settings):
settings = Settings()
monkeypatch.setattr('django_guid.utils.settings', settings)
client.get('/no-guid-regex', **{'HTTP_Correlation-ID': 'bad-guid'})
# No log message should have a GUID, aka `None` on index 1.
expected = [
('sync middleware called', None),
('This log message should NOT have a GUID - the URL is in IGNORE_REGEX_URLS', None),
('Some warning in a function', None),
('Received signal `request_finished`, clearing guid', None),
]
assert [(x.message, x.correlation_id) for x in caplog.records] == expected

0 comments on commit 00732ca

Please sign in to comment.