import contextvars import uuid from collections.abc import Iterable, Mapping from contextvars import ContextVar from dataclasses import asdict, dataclass from typing import Any from fansifter_common import constants from fansifter_common.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) -> contextvars.Token[Any]: """Set the current correlation id.""" return _correlation_id.set(correlation_id) def reset_correlation_id(token: contextvars.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]: 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, ) -> contextvars.Token[Any]: """Set the current request context.""" return _request_context.set(request_context) def reset_request_context(token: contextvars.Token[Any]) -> None: """Reset the current request context.""" _request_context.reset(token)