Bound ?limit and ?offset on /jobs and /dlq

This commit is contained in:
Yiorgis Gozadinos 2026-05-26 16:05:47 +03:00
parent 761956eb70
commit 3c608046e2
No known key found for this signature in database
3 changed files with 20 additions and 6 deletions

View file

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, Query, status
from haiku.rag.ingester.api.server import APIState, get_state
from haiku.rag.ingester.queue.models import Job, JobStatus
@ -9,8 +9,8 @@ router = APIRouter(prefix="/dlq", tags=["dlq"])
@router.get("", response_model=list[Job])
async def list_dlq(
source_id: str | None = None,
limit: int = 50,
offset: int = 0,
limit: int = Query(50, ge=1, le=500),
offset: int = Query(0, ge=0),
state: APIState = Depends(get_state),
) -> list[Job]:
"""Jobs that exhausted retries or hit a permanent error."""

View file

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, Query, status
from haiku.rag.ingester.api.schemas import CancelResponse
from haiku.rag.ingester.api.server import APIState, get_state
@ -12,8 +12,8 @@ async def list_jobs(
status: JobStatus | None = None,
source_id: str | None = None,
uri: str | None = None,
limit: int = 50,
offset: int = 0,
limit: int = Query(50, ge=1, le=500),
offset: int = Query(0, ge=0),
state: APIState = Depends(get_state),
) -> list[Job]:
return await state.job_repo.list_jobs(

View file

@ -195,6 +195,20 @@ async def test_list_jobs_returns_recent_first(state, jobs):
assert [j["id"] for j in payload] == [j2.id, j1.id]
@pytest.mark.asyncio
async def test_list_jobs_rejects_out_of_range_limit_and_offset(state):
"""Limit is capped at 500 and >=1; offset is >=0. Without bounds a
malicious or careless ?limit=10000000 would block the event loop on
serialization."""
async with _client(state) as client:
for q in ("/jobs?limit=0", "/jobs?limit=501", "/jobs?offset=-1"):
resp = await client.get(q)
assert resp.status_code == 422, q
for q in ("/dlq?limit=0", "/dlq?limit=501", "/dlq?offset=-1"):
resp = await client.get(q)
assert resp.status_code == 422, q
@pytest.mark.asyncio
async def test_list_jobs_filters_by_source_and_status(state, jobs):
# Enqueue b first so claim_next reaches it before the a row.