import logging import uuid from contextvars import ContextVar from dataclasses import asdict, dataclass, field from typing import Any, Dict, Iterable, List, Mapping, Optional from owslib import constants from owslib.dictutil import exclude logger = logging.getLogger(__name__) correlation_id: ContextVar[Optional[str]] = ContextVar("correlation_id", default=None) def generate_correlation_id() -> str: return str(uuid.uuid4()) @dataclass class RequestContext: context_type: Optional[str] = constants.CONTEXT_TYPE_NONE requestor_service_name: Optional[str] = None identity_id: Optional[str] = None identity_uuid: Optional[str] = None profile_type: Optional[str] = None profile_id: Optional[int] = None profile_uuid: Optional[uuid.UUID] = None roles: List[str] = field(default_factory=list) orchard_user_id: Optional[str] = None authorization: Optional[str] = None brand: str = constants.DEFAULT_BRAND def dict( self, exclude_empty: bool = False, exclude_keys: Optional[Iterable[str]] = 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], label_profile: bool = False ) -> RequestContext: # account context headers grass_account_type = headers.get(constants.HEADER_GRASS_ACCOUNT_TYPE) grass_account_id = headers.get(constants.HEADER_GRASS_ACCOUNT_ID) orchard_user_id = headers.get(constants.HEADER_ORCHARD_USER_ID) # 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) # roles roles_str: Optional[str] = headers.get(constants.HEADER_ORCHARD_ROLES) if roles_str: roles = roles_str.split(",") else: roles = [] # add the requesting microservice name requestor_service_name = headers.get(constants.ORCHARD_REQUESTOR_SERVICE) context_type: Optional[str] = constants.CONTEXT_TYPE_ERROR # profile based context takes precedence if (profile_type and profile_id) or profile_uuid: context_type = constants.CONTEXT_TYPE_PROFILE # account based context elif (grass_account_type and grass_account_id) or orchard_user_id: if orchard_user_id and label_profile: if orchard_user_id.startswith("alw"): profile_id = orchard_user_id.lstrip("alw:") profile_type = constants.PROFILE_TYPE_LABEL context_type = constants.CONTEXT_TYPE_PROFILE if orchard_user_id.startswith("oa"): profile_id = orchard_user_id.lstrip("oa:") profile_type = constants.PROFILE_TYPE_ORCH_ADMIN context_type = constants.CONTEXT_TYPE_PROFILE else: context_type = constants.CONTEXT_TYPE_ACCOUNT profile_type = grass_account_type profile_id = grass_account_id # this attribute only exists in this context orchard_user_id = orchard_user_id # only identity id or identity_uuid elif (context_type != constants.CONTEXT_TYPE_ACCOUNT) and ( identity_id or identity_uuid ): context_type = constants.CONTEXT_TYPE_PROFILE # no context elif ( not grass_account_type and not grass_account_id and not orchard_user_id and not profile_type and not profile_id and not identity_id and not identity_uuid ): context_type = constants.CONTEXT_TYPE_NONE authorization: Optional[str] = headers.get("authorization") # TODO: get brand from jwt brand = constants.DEFAULT_BRAND request_context = RequestContext( requestor_service_name=requestor_service_name, context_type=context_type, 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, roles=roles, orchard_user_id=orchard_user_id, authorization=authorization, brand=brand, ) return request_context def _parse_profile_id(profile_id: Any) -> Optional[int]: return int(profile_id) if profile_id and str.isnumeric(profile_id) else None request_context: ContextVar[Optional[RequestContext]] = ContextVar( "request_context", default=None )