"""Request context module.""" import uuid from collections.abc import Iterable, Mapping from contextvars import ContextVar, Token from dataclasses import asdict, dataclass from typing import Any from owscontext import constants from owscontext.utils.dictutil import exclude _correlation_id: ContextVar[str | None] = ContextVar("correlation_id", default=None) def get_correlation_id() -> str | None: """Get the current correlation id.""" return _correlation_id.get() def set_correlation_id(correlation_id: str | None) -> Token[Any]: """Set the current correlation id.""" return _correlation_id.set(correlation_id) def reset_correlation_id(token: Token[Any]) -> None: """Reset the current correlation id.""" _correlation_id.reset(token) @dataclass class RequestContext: """Request context.""" requestor_service_name: str | None = None identity_id: str | None = None identity_uuid: str | None = None profile_type: str | None = None profile_id: int | None = None profile_uuid: uuid.UUID | None = None authorization: str | None = None def dict( self, exclude_empty: bool = False, exclude_keys: Iterable[str] | None = None ) -> dict[str, Any]: """Return the context as a dictionary.""" d = asdict(self) if exclude_keys: d = exclude(d, keys=exclude_keys) if exclude_empty: return {k: v for k, v in d.items() if v} return d def request_context_from_headers(headers: Mapping[str, str]) -> RequestContext: """Create a request context from headers.""" # profile context headers profile_type = headers.get(constants.HEADER_ORCHARD_PROFILE_TYPE) profile_id = headers.get(constants.HEADER_ORCHARD_PROFILE_ID) profile_uuid = headers.get(constants.HEADER_ORCHARD_PROFILE_UUID) # identity identity_id = headers.get(constants.HEADER_ORCHARD_IDENTITY_ID) identity_uuid = headers.get(constants.HEADER_ORCHARD_IDENTITY_UUID) # add the requesting microservice name requestor_service_name = headers.get(constants.ORCHARD_REQUESTOR_SERVICE) authorization: str | None = headers.get("authorization") return RequestContext( requestor_service_name=requestor_service_name, identity_id=identity_id, identity_uuid=identity_uuid, profile_type=profile_type, profile_id=_parse_profile_id(profile_id), profile_uuid=uuid.UUID(profile_uuid) if profile_uuid else None, authorization=authorization, ) def _parse_profile_id(profile_id: Any) -> int | None: return int(profile_id) if profile_id and str.isnumeric(profile_id) else None _request_context: ContextVar[RequestContext | None] = ContextVar( "request_context", default=None ) def get_request_context() -> RequestContext | None: """Get the current request context.""" return _request_context.get() def set_request_context( request_context: RequestContext | None, ) -> Token[Any]: """Set the current request context.""" return _request_context.set(request_context) def reset_request_context(token: Token[Any]) -> None: """Reset the current request context.""" _request_context.reset(token)