"""ImpersonationOwsClient implementation.""" from __future__ import annotations import weakref from typing import Any, Callable import httpx from owsclient.base import BaseOwsClient from owsclient.logging_adapter import get_logger from owsclient.m2m.impersonation import ImpersonationM2MTokenManager logger = get_logger(__name__) _IMPERSONATION_OWS_CLIENT_REGISTRY: weakref.WeakSet[ ImpersonationOwsClient ] = weakref.WeakSet() class ImpersonationOwsClient(BaseOwsClient): """Client for synchronous operations with impersonation support.""" def __init__( self, environment: str, service_name: str, *, timeout: httpx.Timeout = httpx.Timeout(5.0), retries: int = 5, m2m_token_manager: ImpersonationM2MTokenManager, correlation_id_getter: Callable[[], str | None] | None = None, ) -> None: """Construct a synchronous client with impersonation support.""" super().__init__( environment, service_name, timeout=timeout, retries=retries, correlation_id_getter=correlation_id_getter, ) if not isinstance(m2m_token_manager, ImpersonationM2MTokenManager): raise TypeError( "m2m_token_manager must be an instance of ImpersonationM2MTokenManager" ) self.m2m_token_manager = m2m_token_manager _IMPERSONATION_OWS_CLIENT_REGISTRY.add(self) def pass_authorization_header( self, impersonated_identity_uuid: str, headers: dict[str, str], ) -> dict[str, str]: """Get Authorization header with impersonation support.""" authorization_header = {} try: token = self.m2m_token_manager.get_token_string( impersonated_identity_uuid=impersonated_identity_uuid ) except Exception as exc: logger.warning( "Failed to get M2M impersonation token", exc_info=exc, extra={ "library": { "name": "python-owsclient", "language": "python", }, }, ) else: authorization_header = {"authorization": f"Bearer {token}"} return {**headers, **authorization_header} def prepare_headers( self, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, ) -> dict[str, str]: """Get all headers with impersonation support.""" headers = headers or {} headers = self.pass_correlation_id_header( headers, correlation_id=correlation_id ) if "authorization" not in headers: headers = self.pass_authorization_header( headers=headers, impersonated_identity_uuid=impersonated_identity_uuid, ) return headers def request( self, service_name: str, method: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a request with impersonation support.""" with httpx.Client( transport=httpx.HTTPTransport(retries=self.retries), timeout=self.timeout, ) as client: return client.request( method=method, url=self.prepare_url(service_name, path=path), headers=self.prepare_headers( headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, ), **kwargs, ) def get( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a GET request with impersonation support.""" return self.request( service_name, method="GET", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def post( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a POST request with impersonation support.""" return self.request( service_name, method="POST", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def head( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a HEAD request with impersonation support.""" return self.request( service_name, method="HEAD", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def put( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a PUT request with impersonation support.""" return self.request( service_name, method="PUT", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def patch( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a PATCH request with impersonation support.""" return self.request( service_name, method="PATCH", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def delete( self, service_name: str, path: str, impersonated_identity_uuid: str, *, headers: dict[str, str] | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a DELETE request with impersonation support.""" return self.request( service_name, method="DELETE", path=path, headers=headers, correlation_id=correlation_id, impersonated_identity_uuid=impersonated_identity_uuid, **kwargs, ) def graphql_query( self, service_name: str, *, query: str, impersonated_identity_uuid: str, operation_name: str | None = None, variables: dict[str, Any] | None = None, headers: dict[str, str] | None = None, identity_id: str | None = None, profile_id: int | None = None, profile_type: str | None = None, correlation_id: str | None = None, **kwargs: Any, ) -> httpx.Response: """Make a request to graphql with impersonation support.""" headers = headers or {} kwargs["json"] = self.pass_graphql_body( query, operation_name=operation_name, variables=variables, ) return self.post( service_name, path="/graphql", impersonated_identity_uuid=impersonated_identity_uuid, headers=self.pass_graphql_headers( headers, identity_id=identity_id, profile_id=profile_id, profile_type=profile_type, ), correlation_id=correlation_id, **kwargs, )