Implement FastAPI Temporal MVP pipeline
This commit is contained in:
112
app/application/services/review_service.py
Normal file
112
app/application/services/review_service.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Review application service."""
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.api.schemas.review import PendingReviewResponse, SubmitReviewRequest, SubmitReviewResponse
|
||||
from app.application.services.workflow_service import WorkflowService
|
||||
from app.domain.enums import OrderStatus, ReviewTaskStatus
|
||||
from app.infra.db.models.asset import AssetORM
|
||||
from app.infra.db.models.order import OrderORM
|
||||
from app.infra.db.models.review_task import ReviewTaskORM
|
||||
from app.infra.db.models.workflow_run import WorkflowRunORM
|
||||
from app.workers.workflows.types import ReviewSignalPayload
|
||||
|
||||
|
||||
class ReviewService:
|
||||
"""Application service for review flows."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.workflow_service = WorkflowService()
|
||||
|
||||
async def list_pending_reviews(self, session) -> list[PendingReviewResponse]:
|
||||
"""Return all pending review tasks."""
|
||||
|
||||
result = await session.execute(
|
||||
select(ReviewTaskORM, WorkflowRunORM)
|
||||
.join(WorkflowRunORM, WorkflowRunORM.order_id == ReviewTaskORM.order_id)
|
||||
.where(ReviewTaskORM.status == ReviewTaskStatus.PENDING)
|
||||
.order_by(ReviewTaskORM.created_at.asc())
|
||||
)
|
||||
|
||||
return [
|
||||
PendingReviewResponse(
|
||||
review_task_id=review_task.id,
|
||||
order_id=review_task.order_id,
|
||||
workflow_id=workflow_run.workflow_id,
|
||||
current_step=workflow_run.current_step,
|
||||
created_at=review_task.created_at,
|
||||
)
|
||||
for review_task, workflow_run in result.all()
|
||||
]
|
||||
|
||||
async def submit_review(self, session, order_id: int, payload: SubmitReviewRequest) -> SubmitReviewResponse:
|
||||
"""Persist the review submission and signal the Temporal workflow."""
|
||||
|
||||
order = await session.get(OrderORM, order_id)
|
||||
if order is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Order not found")
|
||||
if order.status != OrderStatus.WAITING_REVIEW:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Order is not waiting for review",
|
||||
)
|
||||
|
||||
workflow_result = await session.execute(
|
||||
select(WorkflowRunORM).where(WorkflowRunORM.order_id == order_id)
|
||||
)
|
||||
workflow_run = workflow_result.scalar_one_or_none()
|
||||
if workflow_run is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Workflow not found")
|
||||
|
||||
if payload.selected_asset_id is not None:
|
||||
asset = await session.get(AssetORM, payload.selected_asset_id)
|
||||
if asset is None or asset.order_id != order_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Selected asset does not belong to the order",
|
||||
)
|
||||
|
||||
pending_result = await session.execute(
|
||||
select(ReviewTaskORM)
|
||||
.where(
|
||||
ReviewTaskORM.order_id == order_id,
|
||||
ReviewTaskORM.status == ReviewTaskStatus.PENDING,
|
||||
)
|
||||
.order_by(ReviewTaskORM.created_at.desc())
|
||||
)
|
||||
review_task = pending_result.scalars().first()
|
||||
if review_task is None:
|
||||
review_task = ReviewTaskORM(order_id=order_id, status=ReviewTaskStatus.SUBMITTED)
|
||||
session.add(review_task)
|
||||
|
||||
review_task.status = ReviewTaskStatus.SUBMITTED
|
||||
review_task.decision = payload.decision
|
||||
review_task.reviewer_id = payload.reviewer_id
|
||||
review_task.selected_asset_id = payload.selected_asset_id
|
||||
review_task.comment = payload.comment
|
||||
await session.commit()
|
||||
|
||||
try:
|
||||
await self.workflow_service.signal_review(
|
||||
workflow_run.workflow_id,
|
||||
ReviewSignalPayload(
|
||||
decision=payload.decision,
|
||||
reviewer_id=payload.reviewer_id,
|
||||
selected_asset_id=payload.selected_asset_id,
|
||||
comment=payload.comment,
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail=f"Failed to signal Temporal workflow: {exc}",
|
||||
) from exc
|
||||
|
||||
return SubmitReviewResponse(
|
||||
order_id=order_id,
|
||||
workflow_id=workflow_run.workflow_id,
|
||||
decision=payload.decision,
|
||||
status="submitted",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user