Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/routes/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@
from app.routes.v1.proxmox import router as proxmox_router
from app.routes.v1.admin import router as admin_router
from app.routes.v1.health import router as health_router
from app.schemas.v1.common import ErrorEnvelope

router = APIRouter(prefix="/v1")
# Every v1 error goes through the handlers in app/core/errors, which return
# the Range42 envelope — not FastAPI's default {"detail": [...]}. Declaring it
# once here keeps generated clients honest for all of /v1 (#116).
_ERROR_RESPONSES = {
422: {"model": ErrorEnvelope, "description": "Validation Error"},
}

router = APIRouter(prefix="/v1", responses=_ERROR_RESPONSES)
router.include_router(catalog_router)
router.include_router(projects_router)
router.include_router(deployments_router)
Expand Down
11 changes: 10 additions & 1 deletion app/routes/v1/deployments/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,16 @@ def _filter_event(ev: dict, *, team: int | None, stage: str | None,
return True


@router.get("/{deployment_id}/events")
@router.get(
"/{deployment_id}/events",
response_class=EventSourceResponse,
responses={
200: {
"description": "Server-sent event stream of deployment events",
"content": {"text/event-stream": {"schema": {"type": "string"}}},
},
},
)
async def events_stream(deployment_id: str,
team: int | None = Query(None),
stage: str | None = Query(None),
Expand Down
17 changes: 17 additions & 0 deletions app/schemas/v1/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,20 @@ class ErrorDetail(BaseModel):
field: str
reason: str
hint: str | None = None


class ErrorEnvelope(BaseModel):
"""What every v1 error actually returns.

Mirrors ``app.core.errors._envelope``. Declared so the generated spec
stops advertising FastAPI's default ``{"detail": [...]}`` for 422s —
clients built from the committed spec were deserialising the wrong
shape for every validation failure.
"""

error: str
message: str
code: str
details: list[ErrorDetail] = []
trace_id: str
timestamp: str
Loading
Loading