diff --git a/autobot-backend/api/analytics_debt.py b/autobot-backend/api/analytics_debt.py index 681558ac7..7b2c1dade 100644 --- a/autobot-backend/api/analytics_debt.py +++ b/autobot-backend/api/analytics_debt.py @@ -520,7 +520,7 @@ def _get_latest_debt_data() -> Optional[Dict[str, Any]]: operation="calculate_debt", error_code_prefix="DEBT", ) -@router.post("/calculate") +@router.post("/calculate", response_model=None) async def calculate_technical_debt( request: DebtCalculationRequest, admin_check: bool = Depends(check_admin_permission) ): @@ -569,7 +569,7 @@ async def calculate_technical_debt( operation="get_debt_summary", error_code_prefix="DEBT", ) -@router.get("/summary") +@router.get("/summary", response_model=None) async def get_debt_summary(admin_check: bool = Depends(check_admin_permission)): """ Get summary of current technical debt (Issue #543 - refactored). @@ -601,7 +601,7 @@ async def get_debt_summary(admin_check: bool = Depends(check_admin_permission)): operation="get_debt_by_category", error_code_prefix="DEBT", ) -@router.get("/by-category/{category}") +@router.get("/by-category/{category}", response_model=None) async def get_debt_by_category( category: str, admin_check: bool = Depends(check_admin_permission) ): @@ -701,7 +701,7 @@ def _calculate_trend_change(trend_data: List[Dict[str, Any]]) -> tuple: operation="get_debt_trends", error_code_prefix="DEBT", ) -@router.get("/trends") +@router.get("/trends", response_model=None) async def get_debt_trends( days: int = Query(default=30, ge=1, le=365), admin_check: bool = Depends(check_admin_permission), @@ -733,7 +733,7 @@ async def get_debt_trends( operation="get_roi_priorities", error_code_prefix="DEBT", ) -@router.get("/roi-priorities") +@router.get("/roi-priorities", response_model=None) async def get_roi_priorities( limit: int = Query(default=20, ge=1, le=100), admin_check: bool = Depends(check_admin_permission), @@ -859,7 +859,7 @@ def _generate_markdown_report(debt_data: dict) -> str: operation="get_debt_report", error_code_prefix="DEBT", ) -@router.get("/report") +@router.get("/report", response_model=None) async def get_debt_report( format: str = Query(default="json", description="json or markdown"), admin_check: bool = Depends(check_admin_permission), diff --git a/autobot-backend/api/analytics_embedding_patterns.py b/autobot-backend/api/analytics_embedding_patterns.py index 5451fb240..7aa9d2b28 100644 --- a/autobot-backend/api/analytics_embedding_patterns.py +++ b/autobot-backend/api/analytics_embedding_patterns.py @@ -536,7 +536,7 @@ def get_embedding_analyzer() -> EmbeddingPatternAnalyzer: # ============================================================================= -@router.post("/record") +@router.post("/record", response_model=None) async def record_embedding_usage( request: EmbeddingUsageRequest, admin_check: bool = Depends(check_admin_permission), @@ -557,7 +557,7 @@ async def record_embedding_usage( ) -@router.get("/stats") +@router.get("/stats", response_model=None) async def get_embedding_stats( days: int = Query(default=7, ge=1, le=90, description="Number of days to analyze"), model: Optional[str] = Query(None, description="Filter by model"), @@ -579,7 +579,7 @@ async def get_embedding_stats( ) -@router.get("/model-comparison") +@router.get("/model-comparison", response_model=None) async def get_model_comparison( admin_check: bool = Depends(check_admin_permission), ): @@ -599,7 +599,7 @@ async def get_model_comparison( ) -@router.get("/optimization-recommendations") +@router.get("/optimization-recommendations", response_model=None) async def get_optimization_recommendations( admin_check: bool = Depends(check_admin_permission), ): @@ -619,7 +619,7 @@ async def get_optimization_recommendations( ) -@router.get("/health") +@router.get("/health", response_model=None) async def embedding_analytics_health( admin_check: bool = Depends(check_admin_permission), ): diff --git a/autobot-backend/api/analytics_reporting.py b/autobot-backend/api/analytics_reporting.py index c6bb35cd3..3981cb9cd 100644 --- a/autobot-backend/api/analytics_reporting.py +++ b/autobot-backend/api/analytics_reporting.py @@ -290,7 +290,7 @@ def _build_unified_report_response( category=ErrorCategory.API, error_code_prefix="UNIFIED", ) -@router.get("/report") +@router.get("/report", response_model=None) async def get_unified_report(): """ Get unified analytics report aggregating all analytics sources. @@ -329,7 +329,7 @@ async def get_unified_report(): category=ErrorCategory.API, error_code_prefix="UNIFIED", ) -@router.get("/summary") +@router.get("/summary", response_model=None) async def get_quick_summary(): """ Get a quick summary of code health. @@ -365,7 +365,7 @@ async def get_quick_summary(): category=ErrorCategory.API, error_code_prefix="UNIFIED", ) -@router.get("/trends") +@router.get("/trends", response_model=None) async def get_trends(): """ Get analytics trends over time. diff --git a/autobot-backend/api/elevation.py b/autobot-backend/api/elevation.py index b24b065ba..a63247361 100644 --- a/autobot-backend/api/elevation.py +++ b/autobot-backend/api/elevation.py @@ -16,6 +16,15 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel +from api.schemas_common import ( + ElevationAuthorizeResponse, + ElevationExecuteResponse, + ElevationHealthResponse, + ElevationPendingResponse, + ElevationRequestResponse, + ElevationStatusResponse, + SuccessResponse, +) from auth_middleware import check_admin_permission from autobot_shared.error_boundaries import ErrorCategory, with_error_handling @@ -58,7 +67,7 @@ class ElevationAuthorization(BaseModel): operation="request_elevation", error_code_prefix="ELEVATION", ) -@router.post("/request") +@router.post("/request", response_model=ElevationRequestResponse) async def request_elevation(request: ElevationRequest): """Request elevation for a privileged operation""" request_id = str(uuid.uuid4()) @@ -88,7 +97,7 @@ async def request_elevation(request: ElevationRequest): operation="authorize_elevation", error_code_prefix="ELEVATION", ) -@router.post("/authorize") +@router.post("/authorize", response_model=ElevationAuthorizeResponse) async def authorize_elevation(auth: ElevationAuthorization): """Authorize an elevation request with password""" request_id = auth.request_id @@ -153,7 +162,7 @@ async def authorize_elevation(auth: ElevationAuthorization): operation="get_elevation_status", error_code_prefix="ELEVATION", ) -@router.get("/status/{request_id}") +@router.get("/status/{request_id}", response_model=ElevationStatusResponse) async def get_elevation_status(request_id: str): """Check the status of an elevation request""" async with _pending_requests_lock: @@ -176,7 +185,7 @@ async def get_elevation_status(request_id: str): operation="execute_elevated_command", error_code_prefix="ELEVATION", ) -@router.post("/execute/{session_token}") +@router.post("/execute/{session_token}", response_model=ElevationExecuteResponse) async def execute_elevated_command(session_token: str, command: str): """Execute a command with elevated privileges using session token""" async with _elevation_sessions_lock: @@ -222,7 +231,7 @@ async def execute_elevated_command(session_token: str, command: str): operation="get_pending_requests", error_code_prefix="ELEVATION", ) -@router.get("/pending") +@router.get("/pending", response_model=ElevationPendingResponse) async def get_pending_requests_endpoint(): """Get all pending elevation requests""" async with _pending_requests_lock: @@ -313,7 +322,7 @@ async def run_elevated_command(command: str) -> dict: operation="revoke_elevation_session", error_code_prefix="ELEVATION", ) -@router.delete("/session/{session_token}") +@router.delete("/session/{session_token}", response_model=SuccessResponse) async def revoke_elevation_session(session_token: str): """Revoke an elevation session""" if session_token in elevation_sessions: @@ -328,7 +337,7 @@ async def revoke_elevation_session(session_token: str): operation="elevation_health_check", error_code_prefix="ELEVATION", ) -@router.get("/health") +@router.get("/health", response_model=ElevationHealthResponse) async def elevation_health_check(): """Health check for elevation system""" return { diff --git a/autobot-backend/api/registry.py b/autobot-backend/api/registry.py index b49854c67..6568a533c 100644 --- a/autobot-backend/api/registry.py +++ b/autobot-backend/api/registry.py @@ -12,6 +12,14 @@ from fastapi import APIRouter +from api.schemas_common import ( + RegistryEndpointsResponse, + RegistryHealthResponse, + RegistryRouterDetailResponse, + RegistryTagRoutersResponse, + RegistryTagsResponse, + RegistryValidateResponse, +) from autobot_shared.error_boundaries import ErrorCategory, with_error_handling # Create FastAPI router @@ -481,7 +489,7 @@ def get_endpoint_documentation() -> List[Dict]: operation="list_endpoints", error_code_prefix="REGISTRY", ) -@router.get("/endpoints") +@router.get("/endpoints", response_model=RegistryEndpointsResponse) async def list_endpoints(): """List all registered API endpoints""" return { @@ -495,7 +503,7 @@ async def list_endpoints(): operation="list_routers", error_code_prefix="REGISTRY", ) -@router.get("/routers") +@router.get("/routers", response_model=None) async def list_routers(): """List all registered routers with full configuration""" routers_data = {} @@ -519,7 +527,7 @@ async def list_routers(): operation="get_router_details", error_code_prefix="REGISTRY", ) -@router.get("/router/{router_name}") +@router.get("/router/{router_name}", response_model=RegistryRouterDetailResponse) async def get_router_details(router_name: str): """Get details for a specific router""" config = registry.get_router_by_name(router_name) @@ -544,7 +552,7 @@ async def get_router_details(router_name: str): operation="list_tags", error_code_prefix="REGISTRY", ) -@router.get("/tags") +@router.get("/tags", response_model=RegistryTagsResponse) async def list_tags(): """List all unique tags across all routers""" all_tags = set() @@ -558,7 +566,7 @@ async def list_tags(): operation="get_routers_by_tag", error_code_prefix="REGISTRY", ) -@router.get("/tags/{tag}") +@router.get("/tags/{tag}", response_model=RegistryTagRoutersResponse) async def get_routers_by_tag(tag: str): """Get all routers with a specific tag""" routers_with_tag = registry.get_routers_by_tag(tag) @@ -574,7 +582,7 @@ async def get_routers_by_tag(tag: str): operation="validate_dependencies", error_code_prefix="REGISTRY", ) -@router.get("/validate") +@router.get("/validate", response_model=RegistryValidateResponse) async def validate_dependencies(): """Validate router dependencies""" errors = registry.validate_dependencies() @@ -586,7 +594,7 @@ async def validate_dependencies(): operation="registry_health", error_code_prefix="REGISTRY", ) -@router.get("/health") +@router.get("/health", response_model=RegistryHealthResponse) async def registry_health(): """Health check for registry system""" return { diff --git a/autobot-backend/api/rum.py b/autobot-backend/api/rum.py index be5757d78..cec7afb12 100644 --- a/autobot-backend/api/rum.py +++ b/autobot-backend/api/rum.py @@ -16,6 +16,15 @@ from fastapi import APIRouter, HTTPException from pydantic import BaseModel +from api.schemas_common import ( + DataResponse, + RUMClearResponse, + RUMConfigResponse, + RUMDisableResponse, + RUMEventResponse, + RUMMetricsResponse, + RUMStatusResponse, +) from autobot_shared.error_boundaries import ErrorCategory, with_error_handling from autobot_shared.time_utils import parse_utc_iso from monitoring.prometheus_metrics import get_metrics_manager @@ -275,7 +284,7 @@ def setup_rum_logger(): operation="configure_rum", error_code_prefix="RUM", ) -@router.post("/config") +@router.post("/config", response_model=RUMConfigResponse) async def configure_rum(config: RumConfig): """Configure RUM monitoring settings""" try: @@ -312,7 +321,7 @@ async def configure_rum(config: RumConfig): operation="log_rum_event", error_code_prefix="RUM", ) -@router.post("/event") +@router.post("/event", response_model=RUMEventResponse) async def log_rum_event(event: RumEvent): """Log a RUM event from the frontend""" try: @@ -371,7 +380,7 @@ async def log_rum_event(event: RumEvent): operation="disable_rum", error_code_prefix="RUM", ) -@router.post("/disable") +@router.post("/disable", response_model=RUMDisableResponse) async def disable_rum(): """Disable RUM monitoring""" try: @@ -393,7 +402,7 @@ async def disable_rum(): operation="clear_rum_data", error_code_prefix="RUM", ) -@router.post("/clear") +@router.post("/clear", response_model=RUMClearResponse) async def clear_rum_data(): """Clear all RUM data""" try: @@ -426,7 +435,7 @@ async def clear_rum_data(): operation="export_rum_data", error_code_prefix="RUM", ) -@router.get("/export") +@router.get("/export", response_model=DataResponse) async def export_rum_data(): """Export RUM data for analysis""" @@ -471,7 +480,7 @@ async def export_rum_data(): operation="get_rum_status", error_code_prefix="RUM", ) -@router.get("/status") +@router.get("/status", response_model=RUMStatusResponse) async def get_rum_status(): """Get current RUM status and statistics""" @@ -676,7 +685,7 @@ def _dispatch_and_tally_rum_metrics(metrics_manager, metrics: RumMetrics) -> int operation="receive_rum_metrics", error_code_prefix="RUM", ) -@router.post("/metrics") +@router.post("/metrics", response_model=RUMMetricsResponse) async def receive_rum_metrics(metrics: RumMetrics): """ Receive RUM metrics from frontend and record them to Prometheus. diff --git a/autobot-backend/api/schemas_common.py b/autobot-backend/api/schemas_common.py index acd7f177f..4830c45a8 100644 --- a/autobot-backend/api/schemas_common.py +++ b/autobot-backend/api/schemas_common.py @@ -3010,3 +3010,289 @@ class KnowledgeSyncQueuePruneResponse(BaseModel): pruned: int + + +# --------------------------------------------------------------------------- +# workflow_export.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class WorkflowExportResponse(BaseModel): + """Response for GET /export/{workflow_id}.""" + + success: bool + export: Dict[str, Any] + + +class WorkflowValidateImportResponse(BaseModel): + """Response for POST /validate.""" + + success: bool + valid: bool + issues: List[Any] + + +class WorkflowImportResponse(BaseModel): + """Response for POST /import and POST /share/{share_id}/clone.""" + + success: bool + workflow_id: str + + +class WorkflowShareResponse(BaseModel): + """Response for POST /share.""" + + success: bool + share_id: str + + +class WorkflowUnshareResponse(BaseModel): + """Response for DELETE /share/{share_id}.""" + + success: bool + share_id: str + + +class WorkflowListSharesResponse(BaseModel): + """Response for GET /shares.""" + + success: bool + shares: List[Any] + total_count: int + + +# --------------------------------------------------------------------------- +# rum.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class RUMConfigResponse(BaseModel): + """Response for POST /rum/config.""" + + status: str + message: str + config: Dict[str, Any] + + +class RUMEventResponse(BaseModel): + """Response for POST /rum/event (both success and disabled/error paths). + + 'session_event_count' is absent on the disabled/error path — Optional. + """ + + status: str + message: str + session_event_count: Optional[int] = None + + +class RUMDisableResponse(BaseModel): + """Response for POST /rum/disable.""" + + status: str + message: str + + +class RUMClearResponse(BaseModel): + """Response for POST /rum/clear.""" + + status: str + message: str + events_cleared: int + sessions_cleared: int + + +class RUMStatusResponse(BaseModel): + """Response for GET /rum/status.""" + + status: str + rum_status: Dict[str, Any] + + +class RUMMetricsResponse(BaseModel): + """Response for POST /rum/metrics (both success and error paths). + + 'metrics_recorded' is absent on the error path — Optional. + """ + + status: str + message: str + session_id: str + metrics_recorded: Optional[int] = None + + +# --------------------------------------------------------------------------- +# registry.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class RegistryEndpointsResponse(BaseModel): + """Response for GET /endpoints.""" + + endpoints: List[Any] + total: int + + +class RegistryRouterDetailResponse(BaseModel): + """Response for GET /router/{router_name}. + + Includes a possible 'error' key when not found; extra fields allowed. + """ + + model_config = {"extra": "allow"} + + +class RegistryTagsResponse(BaseModel): + """Response for GET /tags.""" + + tags: List[str] + + +class RegistryTagRoutersResponse(BaseModel): + """Response for GET /tags/{tag}.""" + + tag: str + routers: List[Any] + count: int + + +class RegistryValidateResponse(BaseModel): + """Response for GET /validate.""" + + valid: bool + errors: Dict[str, Any] + + +class RegistryHealthResponse(BaseModel): + """Response for GET /health.""" + + status: str + total_routers: int + enabled_routers: int + disabled_routers: int + + +# --------------------------------------------------------------------------- +# elevation.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class ElevationRequestResponse(BaseModel): + """Response for POST /elevation/request.""" + + success: bool + request_id: str + message: str + + +class ElevationAuthorizeResponse(BaseModel): + """Response for POST /elevation/authorize.""" + + success: bool + session_token: str + expires_in: int + message: str + + +class ElevationStatusResponse(BaseModel): + """Response for GET /elevation/status/{request_id}.""" + + success: bool + request_id: str + status: str + operation: str + timestamp: Any # datetime object serialised as string by FastAPI + + +class ElevationExecuteResponse(BaseModel): + """Response for POST /elevation/execute/{session_token}.""" + + success: bool + output: str + error: str + return_code: int + + +class ElevationPendingResponse(BaseModel): + """Response for GET /elevation/pending.""" + + success: bool + pending_requests: Dict[str, Any] + count: int + + +class ElevationHealthResponse(BaseModel): + """Response for GET /elevation/health.""" + + status: str + service: str + active_sessions: int + pending_requests: int + timestamp: str + + +# --------------------------------------------------------------------------- +# usage.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class UsageSummaryPeriod(BaseModel): + days: int + start: str + end: str + + +class UsageSummaryTokens(BaseModel): + input: int + output: int + total: int + + +class UsageSummaryResponse(BaseModel): + """Response for GET /usage/summary.""" + + period: UsageSummaryPeriod + tokens: UsageSummaryTokens + cost_usd: float + requests: int + daily_costs: Dict[str, Any] + by_model: Dict[str, Any] + active_users: int + + +class UsageByUserAllResponse(BaseModel): + """Response for GET /usage/by-user.""" + + timestamp: str + users: List[Any] + total_users: int + + +class UsageRecordResponse(BaseModel): + """Response for POST /usage/record.""" + + recorded: bool + cost_usd: float + record_id: Optional[str] = None + + +# --------------------------------------------------------------------------- +# structured_thinking_mcp.py schemas (Issue #5317) +# --------------------------------------------------------------------------- + + +class StructuredThinkingClearResponse(BaseModel): + """Response for POST /mcp/clear_history.""" + + success: bool + session_id: str + thoughts_cleared: int + message: str + + +class StructuredThinkingSessionsResponse(BaseModel): + """Response for GET /sessions.""" + + session_count: int + sessions: List[Any] + + diff --git a/autobot-backend/api/structured_thinking_mcp.py b/autobot-backend/api/structured_thinking_mcp.py index e16df44d9..76733e79e 100644 --- a/autobot-backend/api/structured_thinking_mcp.py +++ b/autobot-backend/api/structured_thinking_mcp.py @@ -30,6 +30,10 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field +from api.schemas_common import ( + StructuredThinkingClearResponse, + StructuredThinkingSessionsResponse, +) from auth_middleware import check_admin_permission from autobot_shared.error_boundaries import ErrorCategory, with_error_handling from type_defs.common import Metadata @@ -342,7 +346,7 @@ def _build_stage_progression( operation="get_structured_thinking_mcp_tools", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.get("/mcp/tools") +@router.get("/mcp/tools", response_model=List[MCPTool]) async def get_structured_thinking_mcp_tools() -> List[MCPTool]: """ Get available MCP tools for structured thinking. @@ -362,7 +366,7 @@ async def get_structured_thinking_mcp_tools() -> List[MCPTool]: operation="process_thought_mcp", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.post("/mcp/process_thought") +@router.post("/mcp/process_thought", response_model=None) async def process_thought_mcp(request: ProcessThoughtRequest) -> Metadata: """ Process and record a thought within the structured cognitive framework. @@ -433,7 +437,7 @@ async def process_thought_mcp(request: ProcessThoughtRequest) -> Metadata: operation="generate_summary_mcp", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.post("/mcp/generate_summary") +@router.post("/mcp/generate_summary", response_model=None) async def generate_summary_mcp(request: GenerateSummaryRequest) -> Metadata: """ Generate a comprehensive summary of the thinking process. @@ -500,7 +504,7 @@ async def generate_summary_mcp(request: GenerateSummaryRequest) -> Metadata: operation="clear_history_mcp", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.post("/mcp/clear_history") +@router.post("/mcp/clear_history", response_model=StructuredThinkingClearResponse) async def clear_history_mcp(request: ClearHistoryRequest) -> Metadata: """Clear the thinking history for a session""" session_id = request.session_id or "default" @@ -527,7 +531,7 @@ async def clear_history_mcp(request: ClearHistoryRequest) -> Metadata: operation="get_structured_session", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.get("/sessions/{session_id}") +@router.get("/sessions/{session_id}", response_model=None) async def get_structured_session(session_id: str) -> Metadata: """Get complete structured thinking session""" async with _structured_sessions_lock: @@ -566,7 +570,7 @@ async def get_structured_session(session_id: str) -> Metadata: operation="list_structured_sessions", error_code_prefix="STRUCTURED_THINKING_MCP", ) -@router.get("/sessions") +@router.get("/sessions", response_model=StructuredThinkingSessionsResponse) async def list_structured_sessions() -> Metadata: """List all active structured thinking sessions""" async with _structured_sessions_lock: diff --git a/autobot-backend/api/usage.py b/autobot-backend/api/usage.py index 515e21ad7..c7a667253 100644 --- a/autobot-backend/api/usage.py +++ b/autobot-backend/api/usage.py @@ -23,6 +23,11 @@ from fastapi.responses import StreamingResponse from pydantic import BaseModel +from api.schemas_common import ( + UsageByUserAllResponse, + UsageRecordResponse, + UsageSummaryResponse, +) from auth_middleware import check_admin_permission, get_current_user from autobot_shared.error_boundaries import ErrorCategory, with_error_handling from autobot_shared.time_utils import now_utc, utc_timestamp @@ -57,7 +62,7 @@ class UsageRecordRequest(BaseModel): operation="get_usage_summary", error_code_prefix="USAGE", ) -@router.get("/summary") +@router.get("/summary", response_model=UsageSummaryResponse) async def get_usage_summary( days: int = Query(default=30, ge=1, le=365, description="Number of days to include"), admin_check: bool = Depends(check_admin_permission), @@ -105,7 +110,7 @@ async def get_usage_summary( operation="get_usage_by_user_all", error_code_prefix="USAGE", ) -@router.get("/by-user") +@router.get("/by-user", response_model=UsageByUserAllResponse) async def get_usage_by_user_all( admin_check: bool = Depends(check_admin_permission), ) -> dict[str, Any]: @@ -129,7 +134,7 @@ async def get_usage_by_user_all( operation="get_usage_by_user_single", error_code_prefix="USAGE", ) -@router.get("/by-user/{user_id}") +@router.get("/by-user/{user_id}", response_model=None) async def get_usage_by_user_single( user_id: str, admin_check: bool = Depends(check_admin_permission), @@ -148,7 +153,7 @@ async def get_usage_by_user_single( operation="get_my_usage", error_code_prefix="USAGE", ) -@router.get("/me") +@router.get("/me", response_model=None) async def get_my_usage( current_user: dict = Depends(get_current_user), ) -> dict[str, Any]: @@ -186,7 +191,7 @@ async def get_my_usage( operation="record_usage_event", error_code_prefix="USAGE", ) -@router.post("/record") +@router.post("/record", response_model=UsageRecordResponse) async def record_usage_event( body: UsageRecordRequest, current_user: dict = Depends(get_current_user), @@ -230,7 +235,7 @@ async def record_usage_event( operation="export_usage_csv", error_code_prefix="USAGE", ) -@router.get("/export/csv") +@router.get("/export/csv", response_model=None) async def export_usage_csv( days: int = Query(default=30, ge=1, le=365, description="Days of data to export"), admin_check: bool = Depends(check_admin_permission), diff --git a/autobot-backend/api/workflow_export.py b/autobot-backend/api/workflow_export.py index 7c1487658..7dbe7418b 100644 --- a/autobot-backend/api/workflow_export.py +++ b/autobot-backend/api/workflow_export.py @@ -16,6 +16,14 @@ from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field +from api.schemas_common import ( + WorkflowExportResponse, + WorkflowImportResponse, + WorkflowListSharesResponse, + WorkflowShareResponse, + WorkflowUnshareResponse, + WorkflowValidateImportResponse, +) from auth_middleware import get_current_user from autobot_shared.error_boundaries import ErrorCategory, with_error_handling from services.workflow_automation.routes import get_workflow_manager @@ -86,7 +94,7 @@ def _get_sharing_service() -> WorkflowSharingService: @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.get("/export/{workflow_id}") +@router.get("/export/{workflow_id}", response_model=WorkflowExportResponse) async def export_workflow( workflow_id: str, current_user: dict = Depends(get_current_user), @@ -125,7 +133,7 @@ async def export_workflow( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.post("/validate") +@router.post("/validate", response_model=WorkflowValidateImportResponse) async def validate_import( body: ImportWorkflowRequest, current_user: dict = Depends(get_current_user), @@ -152,7 +160,7 @@ async def validate_import( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.post("/import") +@router.post("/import", response_model=WorkflowImportResponse) async def import_workflow( body: ImportWorkflowRequest, current_user: dict = Depends(get_current_user), @@ -191,7 +199,7 @@ async def import_workflow( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.post("/share") +@router.post("/share", response_model=WorkflowShareResponse) async def share_workflow( body: ShareWorkflowRequest, current_user: dict = Depends(get_current_user), @@ -230,7 +238,7 @@ async def share_workflow( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.delete("/share/{share_id}") +@router.delete("/share/{share_id}", response_model=WorkflowUnshareResponse) async def unshare_workflow( share_id: str, current_user: dict = Depends(get_current_user), @@ -255,7 +263,7 @@ async def unshare_workflow( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.get("/shares") +@router.get("/shares", response_model=WorkflowListSharesResponse) async def list_shared_workflows( current_user: dict = Depends(get_current_user), sharing: WorkflowSharingService = Depends(_get_sharing_service), @@ -277,7 +285,7 @@ async def list_shared_workflows( @with_error_handling(category=ErrorCategory.SERVER_ERROR) -@router.post("/share/{share_id}/clone") +@router.post("/share/{share_id}/clone", response_model=WorkflowImportResponse) async def clone_shared_workflow( share_id: str, body: CloneWorkflowRequest,