"""Connector to the ows-pdp microservice.""" import logging from json import JSONDecodeError from typing import Callable import httpx from boto3 import session from fansifter_common.adapters.aws.secretsmanager import SecretsManager from owsclient import M2MTokenManager, OwsClient from pydantic import UUID4, ValidationError from tenacity import ( retry, retry_if_exception_type, stop_after_attempt, wait_random_exponential, ) from backfill import config from backfill.connectors.ows_pdp.models.attach_detach_roles_request import ( AttachDetachRolesRequest, ) from backfill.connectors.ows_pdp.models.roles_response import RolesResponse logger = logging.getLogger(__name__) class RetryableException(Exception): pass class OwsPdpClient: """Connector to ows-pdp microservice.""" ows_client: OwsClient service_name = "ows-pdp" def __init__(self, ows_client: OwsClient) -> None: """Init method.""" self.ows_client = ows_client @retry( stop=stop_after_attempt(3), wait=wait_random_exponential(multiplier=1, max=15), retry=retry_if_exception_type(RetryableException), ) def attach_detach_roles_by_identity_tenant( self, identity_uuid: UUID4, attach_detach_roles_request: AttachDetachRolesRequest, ) -> RolesResponse: """Attach and detach roles on the identity.""" response = self.ows_client.put( self.service_name, path=f"/identity/{identity_uuid}/tenant/{attach_detach_roles_request.tenant_uuid}/attach-and-detach/roles/", json=attach_detach_roles_request.to_dict(), ) if response.status_code in (502, 503, 504): logger.warning( "%d on attach/detach roles, pending tenacity retry logic", response.status_code, ) raise RetryableException() if response.status_code != 200: logger.warning( "Request to attach/detach roles failed: %s", str(response.content) ) raise httpx.RequestError( message=f"Request to attach/detach roles failed: {str(response.content)}" ) try: result = RolesResponse.from_json(str(response.content, encoding="utf-8")) except (JSONDecodeError, ValidationError) as e: raise httpx.DecodingError( f"Response from attach/detach roles could not be parsed: {str(response.content)}" ) from e if not result: raise httpx.DecodingError( f"Response from attach/detach roles could not be parsed: {str(response.content)}" ) return result def get_ows_pdp_connector( environment: str, correlation_id_getter: Callable[[], str | None] | None = None ) -> OwsPdpClient: """Create an ows-pdp client.""" secrets_manager = SecretsManager( session=session.Session(), ) m2m_token_manager = M2MTokenManager( secrets_manager=secrets_manager, environment=environment, service_name=config.SERVICE_NAME, ) ows_client = OwsClient( environment=environment, service_name=config.SERVICE_NAME, m2m_token_manager=m2m_token_manager, correlation_id_getter=correlation_id_getter, ) return OwsPdpClient( ows_client=ows_client, )