"""Connector to ows-permissions microservice.""" import logging from dataclasses import dataclass from typing import AsyncGenerator, List, Optional, Union import httpx from owsclient import AsyncOwsClient from pydantic import BaseModel, Field logger = logging.getLogger(__name__) DEFAULT_OWS_PERMISSIONS_REQUEST_TIMEOUT = httpx.Timeout(10) RESOURCE_TYPE_TENANT_TYPE_MAPPING = { "vendor": "account", "subaccount": "subaccount", "companybrand": "company_brand", } class OwsPermissionsResource(BaseModel): """Representation of an ows-permissions resource.""" resource_type: str = Field(alias="type") resource_id: Union[int, str] = Field(alias="id") uuid: str def is_supported_resource_type(self) -> bool: """Return if ows-pdp supports this resource type as a tenant type.""" return self.resource_type.lower() in RESOURCE_TYPE_TENANT_TYPE_MAPPING def get_tenant_type(self) -> str: """Return the tenant_type mapping for the supported resource type.""" return RESOURCE_TYPE_TENANT_TYPE_MAPPING[self.resource_type.lower()] class OwsPagination(BaseModel): """Representation of ows pagination.""" pagination_type: str = Field(alias="type") total_records: int def get_next_offset(self, item_count: int, offset: int) -> int: """Return next offset to get next page of data.""" if self._has_more_pages(item_count, offset): return offset + item_count return 0 def _has_more_pages(self, item_count: int, offset: int) -> bool: """Return true when more pages are available.""" assert item_count >= 0 assert offset >= 0 return offset + item_count < self.total_records class AdminableResourcesResponse(BaseModel): """Representation of response for get_my_adminable_resources.""" items: List[OwsPermissionsResource] pagination: OwsPagination DEFAULT_OFFSET = 0 DEFAULT_LIMIT = 200 @dataclass class OwsPermissionsClient: """Connector to ows-permissions microservice.""" async_ows_client: AsyncOwsClient service_name = "ows-permissions" async def get_my_adminable_resources( self, offset: Optional[int] = DEFAULT_OFFSET, limit: Optional[int] = DEFAULT_LIMIT, ) -> AdminableResourcesResponse: """Get resources for which authenticated user can admin.""" try: response = await self.async_ows_client.get( self.service_name, path="/identity/admin/resources/all/", params={ "offset": offset, "limit": limit, }, timeout=DEFAULT_OWS_PERMISSIONS_REQUEST_TIMEOUT, ) except httpx.ConnectTimeout: logger.error( "Connection timeout while connecting to ows-permissions.", exc_info=True ) return AdminableResourcesResponse.model_validate_json( b'{"items": [], "pagination": {"type": "standard", "total_records": 0}}' ) response.raise_for_status() return AdminableResourcesResponse.model_validate_json(response.content) async def _get_my_adminable_resources_generator( self, ) -> AsyncGenerator[List[OwsPermissionsResource], None]: """Paginate over get_my_adminable_resources.""" offset = DEFAULT_OFFSET limit = DEFAULT_LIMIT response = await self.get_my_adminable_resources(offset=offset, limit=limit) next_offset = response.pagination.get_next_offset( item_count=len(response.items), offset=offset, ) yield response.items while next_offset: response = await self.get_my_adminable_resources( offset=next_offset, limit=limit ) next_offset = response.pagination.get_next_offset( item_count=len(response.items), offset=next_offset, ) yield response.items async def collect_get_my_adminable_resources(self) -> List[OwsPermissionsResource]: """Collect items from iterating over _get_my_adminable_resources_generator.""" result: List[OwsPermissionsResource] = [] async for items in self._get_my_adminable_resources_generator(): result.extend(items) return result