"""Module that provides generation of upload data.""" import uuid from typing import Any from fastapi import HTTPException from pydantic import UUID4 from product_staging import config from product_staging.api import datasources from product_staging.logic.utils.s3 import _content_disposition from product_staging.api.schemas.bulk_session import ( MetadataStatus, UpdateBulkSessionRequest, ) from product_staging.api.schemas.metadata_upload import CompleteMetadataUploadRequest from product_staging.connectors import db from product_staging.constants.error import ( ERROR_MESSAGE_NO_BULK_SESSION, ERROR_MESSAGE_NO_METADATA_FILE, ) from product_staging.logic.utils.s3 import ( complete_multipart_upload, get_multipart_upload_presigned_urls, ) from product_staging.models import bulk_session as bulk_session_model from product_staging.models import ( bulk_session_metadata_file as bulk_session_metadata_file_model, ) from product_staging.models.bulk_session import update_bulk_session @db.db_session_wrap async def create_metadata_upload( original_filename: str, bulk_session_id: UUID4, identity_uuid: UUID4, session=None ): """Creates a metadata upload in S3, and saves related metadata to the database.""" bulk_session = await bulk_session_model.get_bulk_session( bulk_session_id, session=session ) if not bulk_session: raise HTTPException( status_code=404, detail=ERROR_MESSAGE_NO_BULK_SESSION, ) if bulk_session.metadata_status != MetadataStatus.uploading: update = UpdateBulkSessionRequest(metadata_status=MetadataStatus.uploading) await update_bulk_session(bulk_session_id, identity_uuid, update) s3_client = datasources.get_s3_client() s3_filename = f"metadata/{str(uuid.uuid4())}" bulk_session_metadata_file_id = uuid.uuid4() result = await s3_client.create_multipart_upload( Bucket=config.OWS_PRODUCT_STAGING_S3_BUCKET, Key=s3_filename, Metadata={ "bulk_session_id": str(bulk_session_id), "bulk_session_metadata_file_id": str(bulk_session_metadata_file_id), "identity_uuid": str(identity_uuid), }, ) return await bulk_session_metadata_file_model.create_bulk_session_metadata_file( bulk_session_metadata_file_id=bulk_session_metadata_file_id, bulk_session_id=bulk_session_id, upload_token=result["UploadId"], original_filename=original_filename, s3_filename=s3_filename, identity_uuid=identity_uuid, ) async def get_presigned_urls( parts: int, s3_filename: str, file: dict[str, Any] ) -> dict[str, Any]: """Return presigned URLs for a number of parts, a filename.""" bulk_session = file["bulk_session"] if file else None if not bulk_session: raise HTTPException(status_code=404, detail=ERROR_MESSAGE_NO_BULK_SESSION) presigned_urls = await get_multipart_upload_presigned_urls( parts, s3_filename, file["upload_token"] ) return {"presigned_urls": presigned_urls} async def complete_metadata_upload( request: CompleteMetadataUploadRequest, s3_filename: str, file: dict[str, Any], bulk_session: dict[str, Any], ): """Complete metadata request""" await complete_multipart_upload( s3_filename=s3_filename, upload_token=file["upload_token"], parts=request.parts, ) return { "bulk_session_id": bulk_session["bulk_session_id"], "bulk_session_metadata_file_id": file["bulk_session_metadata_file_id"], } async def get_download_link( bulk_session_id: UUID4, bulk_session_metadata_file_id: UUID4, expiration=3600 ): s3_client = datasources.get_s3_client() bulk_session_metadata = ( await bulk_session_metadata_file_model.get_bulk_session_metadata_file( bulk_session_id, bulk_session_metadata_file_id ) ) if not bulk_session_metadata: raise HTTPException(status_code=404, detail=ERROR_MESSAGE_NO_METADATA_FILE) return await s3_client.generate_presigned_url( "get_object", Params={ "Bucket": config.OWS_PRODUCT_STAGING_S3_BUCKET, "Key": bulk_session_metadata["s3_filename"], "ResponseContentDisposition": _content_disposition( bulk_session_metadata["original_filename"] ), }, ExpiresIn=expiration, )