"""Task logic layer.""" import json import time from typing import Any from fastapi import HTTPException from pydantic import UUID4, HttpUrl from product_staging import config from product_staging.api.schemas.task import TemplateTaskStatusResponse from product_staging.constants.task import POPULATE_TEMPLATE_TIMEOUT from product_staging.logic.utils import fargate, s3 from product_staging.models import task as task_model async def create_template_task( *, identity_uuid: UUID4, artist_urls: list[HttpUrl], album_urls: list[HttpUrl] ) -> UUID4: """ Persist a task entry, run in fargate, and return the task token UUID. """ task = await task_model.create_task(identity_uuid=identity_uuid) fargate.run_task( task_name=f"{config.ENVIRONMENT}-{config.SPOTIFY_EXPORT_CATALOG_JOB_NAME}", container_name=config.SPOTIFY_EXPORT_CATALOG_JOB_NAME, env_vars={ "TASK_TOKEN": str(task.token), "IDENTITY_UUID": str(identity_uuid), "ARTISTS": json.dumps(list(map(lambda url: str(url), artist_urls))), "ALBUMS": json.dumps(list(map(lambda url: str(url), album_urls))), }, ) return task.token async def get_template_task_status( *, token: UUID4, identity_uuid: UUID4, ) -> TemplateTaskStatusResponse: """Return the current template task status for the requesting identity. Responses: - `in_progress`: task is still running and has not exceeded timeout. - `timeout`: task is still in progress but has exceeded `POPULATE_TEMPLATE_TIMEOUT` seconds. - `success`: task finished successfully and includes a presigned `download_url`. - `failure`: task completed with failure. Raises: - HTTPException(404): task does not exist or is not owned by `identity_uuid`. """ task = await task_model.get_task(token=token) if not task or task.created_by != identity_uuid: raise HTTPException(status_code=404, detail="Task not found") if task.status == "in_progress": if time.time() - task.created_at >= POPULATE_TEMPLATE_TIMEOUT: return TemplateTaskStatusResponse(status="timeout") return TemplateTaskStatusResponse(status="in_progress") if task.status == "success": assert task.payload is not None, "Completed task must have payload" key = task.payload.get("key") assert key is not None, "Completed task must have key" return TemplateTaskStatusResponse( status="success", download_url=await s3.generate_download_link( key=key, filename=key, ), ) return TemplateTaskStatusResponse(status="failure") async def mark_task_success( *, token: UUID4, identity_uuid: UUID4, payload: dict[str, Any] ) -> dict[str, Any]: """Mark a Fargate task as successful and attach callback payload.""" task = await task_model.complete_task(token=token, payload=payload) if task.created_by != identity_uuid: raise HTTPException( status_code=404, detail=f"Task not found. Task identity uuid {task.created_by} does not " f"match request identity uuid {identity_uuid}", ) assert task.payload return task.payload async def mark_task_failure( *, token: UUID4, identity_uuid: UUID4, payload: dict[str, Any] ) -> dict[str, Any]: """Mark a Fargate task as failed and attach callback payload.""" task = await task_model.fail_task(token=token, payload=payload) if task.created_by != identity_uuid: raise HTTPException( status_code=404, detail=f"Task not found. Task identity uuid {task.created_by} does not " f"match request identity uuid {identity_uuid}", ) assert task.payload return task.payload