Files
Craig 601c0d461e TICKET-002: Targets CRUD with single-active-target invariant
- POST auto-closes previous target in one transaction
- GET /targets (ordered), GET /targets/current (404 when none)
- PUT with invariant enforcement (409 on double-active attempt)
- Service-layer historical target lookup for TICKET-004
- Full suite green (62 passed)
2026-07-26 13:08:23 +01:00

49 lines
1.7 KiB
Python

"""Targets router (spec §3.4). Thin handlers — business logic in services/targets.py."""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from database import get_db
from schemas import TargetCreate, TargetRead, TargetUpdate
from services import targets as svc
router = APIRouter(prefix="/api/targets", tags=["targets"])
@router.get("", response_model=list[TargetRead])
def list_targets(db: Session = Depends(get_db)):
return svc.list_targets(db)
@router.get("/current", response_model=TargetRead)
def current_target(db: Session = Depends(get_db)):
target = svc.get_current_target(db)
if target is None:
raise HTTPException(status_code=404, detail="No active target")
return target
@router.post("", response_model=TargetRead, status_code=201)
def create_target(data: TargetCreate, db: Session = Depends(get_db)):
"""Create a new target. Auto-closes the previous active target."""
try:
return svc.create_target(db, data)
except ValueError as e:
raise HTTPException(status_code=422, detail=str(e))
@router.put("/{target_id}", response_model=TargetRead)
def update_target(target_id: int, data: TargetUpdate, db: Session = Depends(get_db)):
"""Update a target's values and/or date range.
The single-active-target invariant is preserved."""
try:
result = svc.update_target(db, target_id, data)
except ValueError as e:
raise HTTPException(status_code=422, detail=str(e))
except svc.ActiveTargetConflictError as e:
raise HTTPException(status_code=409, detail=str(e))
if result is None:
raise HTTPException(status_code=404, detail="Target not found")
return result