Add return type annotations and use 201 for upload
All endpoint functions, lifespan context manager, and get_connection now have explicit return types. Upload endpoint returns 201 Created instead of 200 to follow REST conventions.
This commit is contained in:
parent
69ce1df5a1
commit
661568f2cb
5 changed files with 19 additions and 15 deletions
|
|
@ -46,7 +46,7 @@ def _to_response(job) -> AnalysisResponse:
|
|||
|
||||
|
||||
@router.post("", response_model=AnalysisResponse)
|
||||
async def create_analysis(body: CreateAnalysisRequest, service: ServiceDep):
|
||||
async def create_analysis(body: CreateAnalysisRequest, service: ServiceDep) -> AnalysisResponse:
|
||||
"""Create a new analysis job for a document."""
|
||||
if not body.documentId or not body.documentId.strip():
|
||||
raise HTTPException(status_code=400, detail="documentId is required")
|
||||
|
|
@ -72,14 +72,14 @@ async def create_analysis(body: CreateAnalysisRequest, service: ServiceDep):
|
|||
|
||||
|
||||
@router.get("", response_model=list[AnalysisResponse])
|
||||
async def list_analyses(service: ServiceDep):
|
||||
async def list_analyses(service: ServiceDep) -> list[AnalysisResponse]:
|
||||
"""List all analysis jobs."""
|
||||
jobs = await service.find_all()
|
||||
return [_to_response(j) for j in jobs]
|
||||
|
||||
|
||||
@router.get("/{job_id}", response_model=AnalysisResponse)
|
||||
async def get_analysis(job_id: str, service: ServiceDep):
|
||||
async def get_analysis(job_id: str, service: ServiceDep) -> AnalysisResponse:
|
||||
"""Get a single analysis job."""
|
||||
job = await service.find_by_id(job_id)
|
||||
if not job:
|
||||
|
|
@ -88,7 +88,9 @@ async def get_analysis(job_id: str, service: ServiceDep):
|
|||
|
||||
|
||||
@router.post("/{job_id}/rechunk", response_model=list[ChunkResponse])
|
||||
async def rechunk_analysis(job_id: str, body: RechunkRequest, service: ServiceDep):
|
||||
async def rechunk_analysis(
|
||||
job_id: str, body: RechunkRequest, service: ServiceDep
|
||||
) -> list[ChunkResponse]:
|
||||
"""Re-chunk a completed analysis with new chunking options."""
|
||||
try:
|
||||
chunks = await service.rechunk(job_id, body.chunkingOptions.model_dump())
|
||||
|
|
@ -107,7 +109,7 @@ async def rechunk_analysis(job_id: str, body: RechunkRequest, service: ServiceDe
|
|||
|
||||
|
||||
@router.delete("/{job_id}", status_code=204)
|
||||
async def delete_analysis(job_id: str, service: ServiceDep):
|
||||
async def delete_analysis(job_id: str, service: ServiceDep) -> None:
|
||||
"""Delete an analysis job."""
|
||||
deleted = await service.delete(job_id)
|
||||
if not deleted:
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ def _to_response(doc) -> DocumentResponse:
|
|||
)
|
||||
|
||||
|
||||
@router.post("/upload", response_model=DocumentResponse)
|
||||
async def upload(file: UploadFile):
|
||||
@router.post("/upload", response_model=DocumentResponse, status_code=201)
|
||||
async def upload(file: UploadFile) -> DocumentResponse:
|
||||
"""Upload a PDF document."""
|
||||
if not file.filename:
|
||||
raise HTTPException(status_code=400, detail="No filename provided")
|
||||
|
|
@ -50,14 +50,14 @@ async def upload(file: UploadFile):
|
|||
|
||||
|
||||
@router.get("", response_model=list[DocumentResponse])
|
||||
async def list_documents():
|
||||
async def list_documents() -> list[DocumentResponse]:
|
||||
"""List all documents."""
|
||||
docs = await document_service.find_all()
|
||||
return [_to_response(d) for d in docs]
|
||||
|
||||
|
||||
@router.get("/{doc_id}", response_model=DocumentResponse)
|
||||
async def get_document(doc_id: str):
|
||||
async def get_document(doc_id: str) -> DocumentResponse:
|
||||
"""Get a single document."""
|
||||
doc = await document_service.find_by_id(doc_id)
|
||||
if not doc:
|
||||
|
|
@ -66,7 +66,7 @@ async def get_document(doc_id: str):
|
|||
|
||||
|
||||
@router.delete("/{doc_id}", status_code=204)
|
||||
async def delete_document(doc_id: str):
|
||||
async def delete_document(doc_id: str) -> None:
|
||||
"""Delete a document and its file."""
|
||||
deleted = await document_service.delete(doc_id)
|
||||
if not deleted:
|
||||
|
|
@ -78,7 +78,7 @@ async def preview(
|
|||
doc_id: str,
|
||||
page: int = Query(1, ge=1),
|
||||
dpi: int = Query(150, ge=72, le=300),
|
||||
):
|
||||
) -> Response:
|
||||
"""Generate a PNG preview of a specific PDF page."""
|
||||
doc = await document_service.find_by_id(doc_id)
|
||||
if not doc:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ Conversion engine is selected via CONVERSION_ENGINE env var:
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
|
@ -72,7 +73,7 @@ def _build_analysis_service() -> AnalysisService:
|
|||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||
await init_db()
|
||||
app.state.analysis_service = _build_analysis_service()
|
||||
logger.info("Docling Studio backend ready (engine=%s)", settings.conversion_engine)
|
||||
|
|
@ -98,7 +99,7 @@ app.include_router(analyses_router)
|
|||
|
||||
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
def health() -> dict[str, str]:
|
||||
"""Health check endpoint."""
|
||||
return {
|
||||
"status": "ok",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
import aiosqlite
|
||||
|
|
@ -82,7 +83,7 @@ async def get_db() -> aiosqlite.Connection:
|
|||
|
||||
|
||||
@asynccontextmanager
|
||||
async def get_connection():
|
||||
async def get_connection() -> AsyncIterator[aiosqlite.Connection]:
|
||||
"""Context manager that opens and auto-closes a database connection."""
|
||||
db = await get_db()
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class TestDocumentEndpoints:
|
|||
"/api/documents/upload",
|
||||
files={"file": ("uploaded.pdf", b"fake-pdf-content", "application/pdf")},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.status_code == 201
|
||||
data = resp.json()
|
||||
assert data["id"] == "new-1"
|
||||
assert data["filename"] == "uploaded.pdf"
|
||||
|
|
|
|||
Loading…
Reference in a new issue