1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
import logging
import uuid
from typing import TYPE_CHECKING, Optional, Union
from django_guid.config import settings
if TYPE_CHECKING:
from django.http import HttpRequest, HttpResponse
logger = logging.getLogger('django_guid')
def get_correlation_id_from_header(request: 'HttpRequest') -> str:
"""
Returns either the provided GUID or a new one depending on if the provided GUID is valid or not.
:param request: HttpRequest object
:return: GUID
"""
given_guid: str = str(request.headers.get(settings.guid_header_name))
if not settings.validate_guid:
logger.debug('Returning ID from header without validating it as a GUID')
return given_guid
elif validate_guid(given_guid):
logger.debug('%s is a valid GUID', given_guid)
return given_guid
else:
new_guid = generate_guid()
if all(letter.isalnum() or letter == '-' for letter in given_guid):
logger.warning('%s is not a valid GUID. New GUID is %s', given_guid, new_guid)
else:
logger.warning('Non-alnum %s provided. New GUID is %s', settings.guid_header_name, new_guid)
return new_guid
def get_id_from_header(request: 'HttpRequest') -> str:
"""
Checks if the request contains the header specified in the Django settings.
If it does, we fetch the header and attempt to validate the contents as GUID.
If no header is found, we generate a GUID to be injected instead.
:param request: HttpRequest object
:return: GUID
"""
header: str = request.headers.get(settings.guid_header_name) # Case insensitive headers.get added in Django2.2
if header:
logger.info('%s found in the header', settings.guid_header_name)
request.correlation_id = get_correlation_id_from_header(request)
else:
request.correlation_id = generate_guid()
logger.info(
'Header `%s` was not found in the incoming request. Generated new GUID: %s',
settings.guid_header_name,
request.correlation_id,
)
return request.correlation_id
def ignored_url(request: Union['HttpRequest', 'HttpResponse']) -> bool:
"""
Checks if the current URL is defined in the `IGNORE_URLS` setting.
:return: Boolean
"""
return request.get_full_path().strip('/') in settings.ignore_urls
def generate_guid(uuid_length: Optional[int] = None) -> str:
"""
Generates an UUIDv4/GUID as a string.
:return: GUID
"""
if settings.uuid_format == 'string':
guid = str(uuid.uuid4())
else:
guid = uuid.uuid4().hex
if uuid_length is None:
return guid[: settings.uuid_length]
return guid[:uuid_length]
def validate_guid(original_guid: str) -> bool:
"""
Validates a GUID.
:param original_guid: string to validate
:return: bool
"""
try:
return bool(uuid.UUID(original_guid, version=4).hex)
except ValueError:
return False
|