from core_domain import ServiceHealth from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import text from sqlalchemy.orm import Session from app.application.services import TeamApplicationService, build_team_application_service from app.bootstrap.settings import TeamServiceSettings from app.db.session import get_db from app.domain.repositories import ( TeamDefinitionRepository, TeamRunRepository, TeamVersionRepository, ) from app.schemas.team import ( TeamCreateRequest, TeamResponse, TeamRunCreateRequest, TeamRunExecuteRequest, TeamRunExecuteResponse, TeamRunResponse, TeamRunStatusUpdateRequest, TeamStatusUpdateRequest, TeamVersionCreateRequest, TeamVersionResponse, TeamWorkerExecuteNextRequest, TeamWorkerExecuteNextResponse, ) router = APIRouter() def get_team_settings() -> TeamServiceSettings: return TeamServiceSettings() def get_team_application_service( db: Session = Depends(get_db), settings: TeamServiceSettings = Depends(get_team_settings)) -> TeamApplicationService: return build_team_application_service( team_repository=TeamDefinitionRepository(db), team_version_repository=TeamVersionRepository(db), team_run_repository=TeamRunRepository(db), settings=settings) @router.get("/health", response_model=ServiceHealth) def health_check(db: Session = Depends(get_db)) -> ServiceHealth: db.execute(text("SELECT 1")) return ServiceHealth(service="team-service", status="ok", database="ok") @router.post("", response_model=TeamResponse) def create_team( payload: TeamCreateRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamResponse: entity = service.create_team(payload) return TeamResponse.from_entity(entity) @router.get("", response_model=list[TeamResponse]) def list_teams( service: TeamApplicationService = Depends(get_team_application_service)) -> list[TeamResponse]: return [TeamResponse.from_entity(item) for item in service.list_teams()] @router.patch("/{team_id}/status", response_model=TeamResponse) def update_team_status( team_id: str, payload: TeamStatusUpdateRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamResponse: entity = service.update_team_status(team_id=team_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"team not found: {team_id}") return TeamResponse.from_entity(entity) @router.post("/versions", response_model=TeamVersionResponse) def create_team_version( payload: TeamVersionCreateRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamVersionResponse: try: entity = service.create_team_version(payload) except ValueError as exc: raise HTTPException(status_code=422, detail=str(exc)) from exc return TeamVersionResponse.from_entity(entity) @router.get("/versions", response_model=list[TeamVersionResponse]) def list_team_versions( team_id: str = Query(...), service: TeamApplicationService = Depends(get_team_application_service)) -> list[TeamVersionResponse]: return [ TeamVersionResponse.from_entity(item) for item in service.list_team_versions(team_id=team_id) ] @router.post("/runs", response_model=TeamRunResponse) def create_team_run( payload: TeamRunCreateRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamRunResponse: try: entity = service.create_team_run(payload) except ValueError as exc: raise HTTPException(status_code=422, detail=str(exc)) from exc return TeamRunResponse.from_entity(entity) @router.get("/runs", response_model=list[TeamRunResponse]) def list_team_runs( team_id: str | None = Query(default=None), session_id: str | None = Query(default=None), service: TeamApplicationService = Depends(get_team_application_service)) -> list[TeamRunResponse]: return [ TeamRunResponse.from_entity(item) for item in service.list_team_runs( team_id=team_id, session_id=session_id) ] @router.post("/runs/{team_run_id}/status", response_model=TeamRunResponse) def update_team_run_status( team_run_id: str, payload: TeamRunStatusUpdateRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamRunResponse: entity = service.update_team_run_status(team_run_id=team_run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"team_run not found: {team_run_id}") return TeamRunResponse.from_entity(entity) @router.post("/runs/{team_run_id}/execute", response_model=TeamRunExecuteResponse) def execute_team_run( team_run_id: str, payload: TeamRunExecuteRequest, service: TeamApplicationService = Depends(get_team_application_service)) -> TeamRunExecuteResponse: entity = service.execute_team_run(team_run_id=team_run_id, payload=payload) if entity is None: raise HTTPException(status_code=404, detail=f"team_run not found: {team_run_id}") output_json = entity.output_json or {} member_run_count = output_json.get("member_run_count") dry_run = output_json.get("dry_run") return TeamRunExecuteResponse( run=TeamRunResponse.from_entity(entity), member_run_count=member_run_count if isinstance(member_run_count, int) else 0, dry_run=dry_run if isinstance(dry_run, bool) else payload.dry_run) @router.post("/workers/execute-next", response_model=TeamWorkerExecuteNextResponse) def execute_next_worker_task( payload: TeamWorkerExecuteNextRequest, settings: TeamServiceSettings = Depends(get_team_settings), service: TeamApplicationService = Depends(get_team_application_service)) -> TeamWorkerExecuteNextResponse: result = service.execute_next_claimed_team_run( worker_key=payload.worker_key, lease_seconds=payload.lease_seconds or settings.worker_lease_seconds, dry_run=payload.dry_run if payload.dry_run is not None else settings.worker_dry_run) if result is None: raise HTTPException(status_code=404, detail="queued team_run not found") entity, released_lease_count = result output_json = entity.output_json or {} member_run_count = output_json.get("member_run_count") dry_run = output_json.get("dry_run") return TeamWorkerExecuteNextResponse( run=TeamRunResponse.from_entity(entity), member_run_count=member_run_count if isinstance(member_run_count, int) else 0, dry_run=dry_run if isinstance(dry_run, bool) else settings.worker_dry_run, released_lease_count=released_lease_count)