"""OWS Assets API client.""" from datetime import datetime, timezone from functools import lru_cache from typing import Any import httpx from owsclient import OwsClient from owsresponse import response from transcoding import config class AssetOwnerNotDeterminableException(Exception): """Exception raised when asset owner cannot be determined (404).""" class AssetOwnerLookupException(Exception): """Exception raised when asset owner lookup fails due to a server or network error.""" client = OwsClient( config.ENVIRONMENT, config.SERVICE_NAME, retries=config.OWS_RETRIES, timeout=httpx.Timeout(config.OWS_TIMEOUT), ) def post_asset_final_status( filename: str, status: str, description: str, final_assets: list[dict[str, Any]], message: dict[str, Any], ) -> response.Response: """Call ows-asset post asset final handler. Args: filename (str): filename. status (str): Status. description (str): Status description. final_assets (list): Final assets. message (dict): Status message. Returns: response.Response: True if success else raise Exception. """ data = { "filename": filename, "status": status, "description": description, "message": message, "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%fZ"), "final_assets": final_assets, } resp = client.post( service_name=config.OWS_ASSETS_SERVICE_NAME, path=config.POST_ASSET_FINAL_PATH, json=data, ) if resp.status_code == 404: # This happens when an asset has been replaced by a new one while it is still being processed. # The replaced asset has been marked as deleted, so we get a 404 when trying to update status for it. pass elif resp.status_code == 409: # This happens when final assets have already been created for this update and this lambda has # been called more than once for the same asset due to "at least once" processing. pass elif resp.status_code != 200: error_text = "Failed to post to ows-assets from {source}. Status: {status}; text: {text}".format( source=config.SERVICE_NAME, status=resp.status_code, text=resp.text ) raise Exception(error_text) return response.Response(data) # Ownership of an asset is not expected to change over time, but size is limited to avoid flooding the memory @lru_cache(maxsize=512) def get_asset_owner(filename: str) -> dict[str, Any]: """Get asset upload owner by look up via filename in ows-assets. Args: filename (str): Asset's filename. Returns: dict: Asset owner info. """ resp = client.get( service_name=config.OWS_ASSETS_SERVICE_NAME, path=config.GET_ASSET_OWNER_PATH_TEMPLATE.format(filename=filename), ) if resp.status_code == 404: raise AssetOwnerNotDeterminableException( f"Owner for asset with filename {filename} cannot be determined." ) elif resp.status_code != 200: error_text = "Failed to get from ows-assets in {source}. Status: {status}; text: {text}".format( source=config.SERVICE_NAME, status=resp.status_code, text=resp.text ) raise AssetOwnerLookupException(error_text) return resp.json() # type: ignore[no-any-return]