Improve the frontend 3
This commit is contained in:
196
services/orchestrator/app/api/orchestration.py
Normal file
196
services/orchestrator/app/api/orchestration.py
Normal file
@@ -0,0 +1,196 @@
|
||||
# ================================================================
|
||||
# services/orchestrator/app/api/orchestration.py
|
||||
# ================================================================
|
||||
"""
|
||||
Orchestration API Endpoints
|
||||
Testing and manual trigger endpoints for orchestration
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel, Field
|
||||
import structlog
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.repositories.orchestration_run_repository import OrchestrationRunRepository
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
router = APIRouter(prefix="/api/v1/tenants/{tenant_id}/orchestrator", tags=["Orchestration"])
|
||||
|
||||
|
||||
# ================================================================
|
||||
# REQUEST/RESPONSE SCHEMAS
|
||||
# ================================================================
|
||||
|
||||
class OrchestratorTestRequest(BaseModel):
|
||||
"""Request schema for testing orchestrator"""
|
||||
test_scenario: Optional[str] = Field(None, description="Test scenario: full, production_only, procurement_only")
|
||||
dry_run: bool = Field(False, description="Dry run mode (no actual changes)")
|
||||
|
||||
|
||||
class OrchestratorTestResponse(BaseModel):
|
||||
"""Response schema for orchestrator test"""
|
||||
success: bool
|
||||
message: str
|
||||
tenant_id: str
|
||||
forecasting_completed: bool = False
|
||||
production_completed: bool = False
|
||||
procurement_completed: bool = False
|
||||
notifications_sent: bool = False
|
||||
summary: dict = {}
|
||||
|
||||
|
||||
# ================================================================
|
||||
# API ENDPOINTS
|
||||
# ================================================================
|
||||
|
||||
@router.post("/test", response_model=OrchestratorTestResponse)
|
||||
async def trigger_orchestrator_test(
|
||||
tenant_id: str,
|
||||
request_data: OrchestratorTestRequest,
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Trigger orchestrator for testing purposes
|
||||
|
||||
This endpoint allows manual triggering of the orchestration workflow
|
||||
for a specific tenant, useful for testing during development.
|
||||
|
||||
Args:
|
||||
tenant_id: Tenant ID to orchestrate
|
||||
request_data: Test request with scenario and dry_run options
|
||||
request: FastAPI request object
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
OrchestratorTestResponse with results
|
||||
"""
|
||||
logger.info("Orchestrator test trigger requested",
|
||||
tenant_id=tenant_id,
|
||||
test_scenario=request_data.test_scenario,
|
||||
dry_run=request_data.dry_run)
|
||||
|
||||
try:
|
||||
# Get scheduler service from app state
|
||||
if not hasattr(request.app.state, 'scheduler_service'):
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Orchestrator scheduler service not available"
|
||||
)
|
||||
|
||||
scheduler_service = request.app.state.scheduler_service
|
||||
|
||||
# Trigger orchestration
|
||||
tenant_uuid = uuid.UUID(tenant_id)
|
||||
result = await scheduler_service.trigger_orchestration_for_tenant(
|
||||
tenant_id=tenant_uuid,
|
||||
test_scenario=request_data.test_scenario
|
||||
)
|
||||
|
||||
# Get the latest run for this tenant
|
||||
repo = OrchestrationRunRepository(db)
|
||||
latest_run = await repo.get_latest_run_for_tenant(tenant_uuid)
|
||||
|
||||
# Build response
|
||||
response = OrchestratorTestResponse(
|
||||
success=result.get('success', False),
|
||||
message=result.get('message', 'Orchestration completed'),
|
||||
tenant_id=tenant_id,
|
||||
forecasting_completed=latest_run.forecasting_status == 'success' if latest_run else False,
|
||||
production_completed=latest_run.production_status == 'success' if latest_run else False,
|
||||
procurement_completed=latest_run.procurement_status == 'success' if latest_run else False,
|
||||
notifications_sent=latest_run.notification_status == 'success' if latest_run else False,
|
||||
summary={
|
||||
'forecasts_generated': latest_run.forecasts_generated if latest_run else 0,
|
||||
'batches_created': latest_run.production_batches_created if latest_run else 0,
|
||||
'pos_created': latest_run.purchase_orders_created if latest_run else 0,
|
||||
'notifications_sent': latest_run.notifications_sent if latest_run else 0
|
||||
}
|
||||
)
|
||||
|
||||
logger.info("Orchestrator test completed",
|
||||
tenant_id=tenant_id,
|
||||
success=response.success)
|
||||
|
||||
return response
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid tenant ID: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error("Orchestrator test failed",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e),
|
||||
exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"Orchestrator test failed: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def orchestrator_health():
|
||||
"""Check orchestrator health"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "orchestrator",
|
||||
"message": "Orchestrator service is running"
|
||||
}
|
||||
|
||||
|
||||
@router.get("/runs", response_model=dict)
|
||||
async def list_orchestration_runs(
|
||||
tenant_id: str,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
List orchestration runs for a tenant
|
||||
|
||||
Args:
|
||||
tenant_id: Tenant ID
|
||||
limit: Maximum number of runs to return
|
||||
offset: Number of runs to skip
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
List of orchestration runs
|
||||
"""
|
||||
try:
|
||||
tenant_uuid = uuid.UUID(tenant_id)
|
||||
repo = OrchestrationRunRepository(db)
|
||||
|
||||
runs = await repo.list_runs(
|
||||
tenant_id=tenant_uuid,
|
||||
limit=limit,
|
||||
offset=offset
|
||||
)
|
||||
|
||||
return {
|
||||
"runs": [
|
||||
{
|
||||
"id": str(run.id),
|
||||
"run_number": run.run_number,
|
||||
"status": run.status.value,
|
||||
"started_at": run.started_at.isoformat() if run.started_at else None,
|
||||
"completed_at": run.completed_at.isoformat() if run.completed_at else None,
|
||||
"duration_seconds": run.duration_seconds,
|
||||
"forecasts_generated": run.forecasts_generated,
|
||||
"batches_created": run.production_batches_created,
|
||||
"pos_created": run.purchase_orders_created
|
||||
}
|
||||
for run in runs
|
||||
],
|
||||
"total": len(runs),
|
||||
"limit": limit,
|
||||
"offset": offset
|
||||
}
|
||||
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid tenant ID: {str(e)}")
|
||||
except Exception as e:
|
||||
logger.error("Error listing orchestration runs",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
Reference in New Issue
Block a user