таски готовы
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# Lazy loading of route modules to avoid import issues in tests
|
||||
# This allows tests to import routes without triggering all module imports
|
||||
|
||||
__all__ = ['plugins', 'tasks', 'settings', 'connections', 'environments', 'mappings', 'migration', 'git', 'storage', 'admin']
|
||||
__all__ = ['plugins', 'tasks', 'settings', 'connections', 'environments', 'mappings', 'migration', 'git', 'storage', 'admin', 'reports']
|
||||
|
||||
def __getattr__(name):
|
||||
if name in __all__:
|
||||
|
||||
131
backend/src/api/routes/reports.py
Normal file
131
backend/src/api/routes/reports.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# [DEF:ReportsRouter:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @SEMANTICS: api, reports, list, detail, pagination, filters
|
||||
# @PURPOSE: FastAPI router for unified task report list and detail retrieval endpoints.
|
||||
# @LAYER: UI (API)
|
||||
# @RELATION: DEPENDS_ON -> backend.src.services.reports.report_service.ReportsService
|
||||
# @RELATION: DEPENDS_ON -> backend.src.dependencies
|
||||
# @INVARIANT: Endpoints are read-only and do not trigger long-running tasks.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
|
||||
from ...dependencies import get_task_manager, has_permission
|
||||
from ...core.task_manager import TaskManager
|
||||
from ...core.logger import belief_scope
|
||||
from ...models.report import ReportCollection, ReportDetailView, ReportQuery, ReportStatus, TaskType
|
||||
from ...services.reports.report_service import ReportsService
|
||||
# [/SECTION]
|
||||
|
||||
router = APIRouter(prefix="/api/reports", tags=["Reports"])
|
||||
|
||||
|
||||
# [DEF:_parse_csv_enum_list:Function]
|
||||
# @PURPOSE: Parse comma-separated query value into enum list.
|
||||
# @PRE: raw may be None/empty or comma-separated values.
|
||||
# @POST: Returns enum list or raises HTTP 400 with deterministic machine-readable payload.
|
||||
# @PARAM: raw (Optional[str]) - Comma-separated enum values.
|
||||
# @PARAM: enum_cls (type) - Enum class for validation.
|
||||
# @PARAM: field_name (str) - Query field name for diagnostics.
|
||||
# @RETURN: List - Parsed enum values.
|
||||
def _parse_csv_enum_list(raw: Optional[str], enum_cls, field_name: str) -> List:
|
||||
if raw is None or not raw.strip():
|
||||
return []
|
||||
values = [item.strip() for item in raw.split(",") if item.strip()]
|
||||
parsed = []
|
||||
invalid = []
|
||||
for value in values:
|
||||
try:
|
||||
parsed.append(enum_cls(value))
|
||||
except ValueError:
|
||||
invalid.append(value)
|
||||
if invalid:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={
|
||||
"message": f"Invalid values for '{field_name}'",
|
||||
"field": field_name,
|
||||
"invalid_values": invalid,
|
||||
"allowed_values": [item.value for item in enum_cls],
|
||||
},
|
||||
)
|
||||
return parsed
|
||||
# [/DEF:_parse_csv_enum_list:Function]
|
||||
|
||||
|
||||
# [DEF:list_reports:Function]
|
||||
# @PURPOSE: Return paginated unified reports list.
|
||||
# @PRE: authenticated/authorized request and validated query params.
|
||||
# @POST: returns {items,total,page,page_size,has_next,applied_filters}.
|
||||
# @POST: deterministic error payload for invalid filters.
|
||||
@router.get("", response_model=ReportCollection)
|
||||
async def list_reports(
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
task_types: Optional[str] = Query(None, description="Comma-separated task types"),
|
||||
statuses: Optional[str] = Query(None, description="Comma-separated statuses"),
|
||||
time_from: Optional[datetime] = Query(None),
|
||||
time_to: Optional[datetime] = Query(None),
|
||||
search: Optional[str] = Query(None, max_length=200),
|
||||
sort_by: str = Query("updated_at"),
|
||||
sort_order: str = Query("desc"),
|
||||
task_manager: TaskManager = Depends(get_task_manager),
|
||||
_=Depends(has_permission("tasks", "READ")),
|
||||
):
|
||||
with belief_scope("list_reports"):
|
||||
try:
|
||||
parsed_task_types = _parse_csv_enum_list(task_types, TaskType, "task_types")
|
||||
parsed_statuses = _parse_csv_enum_list(statuses, ReportStatus, "statuses")
|
||||
query = ReportQuery(
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
task_types=parsed_task_types,
|
||||
statuses=parsed_statuses,
|
||||
time_from=time_from,
|
||||
time_to=time_to,
|
||||
search=search,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail={
|
||||
"message": "Invalid query parameters",
|
||||
"code": "INVALID_REPORT_QUERY",
|
||||
"reason": str(exc),
|
||||
},
|
||||
)
|
||||
|
||||
service = ReportsService(task_manager)
|
||||
return service.list_reports(query)
|
||||
# [/DEF:list_reports:Function]
|
||||
|
||||
|
||||
# [DEF:get_report_detail:Function]
|
||||
# @PURPOSE: Return one normalized report detail with diagnostics and next actions.
|
||||
# @PRE: authenticated/authorized request and existing report_id.
|
||||
# @POST: returns normalized detail envelope or 404 when report is not found.
|
||||
@router.get("/{report_id}", response_model=ReportDetailView)
|
||||
async def get_report_detail(
|
||||
report_id: str,
|
||||
task_manager: TaskManager = Depends(get_task_manager),
|
||||
_=Depends(has_permission("tasks", "READ")),
|
||||
):
|
||||
with belief_scope("get_report_detail", f"report_id={report_id}"):
|
||||
service = ReportsService(task_manager)
|
||||
detail = service.get_report_detail(report_id)
|
||||
if not detail:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail={"message": "Report not found", "code": "REPORT_NOT_FOUND"},
|
||||
)
|
||||
return detail
|
||||
# [/DEF:get_report_detail:Function]
|
||||
|
||||
# [/DEF:ReportsRouter:Module]
|
||||
Reference in New Issue
Block a user