Files
Aidem-Media-DLL-Analysis/ams/api/routes/snapshots.py
Patryk Gensch 8386196653 Add FastAPI catalog backend (games/snapshots/diff) + tests
Modular-monolith backend over SQLAlchemy (SQLite by default, Postgres-ready
via DATABASE_URL). The full snapshot.json is stored verbatim; diffing reads it
back through the ams.diff engine, so the DB never mirrors the snapshot schema.

- ams.api.db/models/schemas/service : Game 1-N Snapshot, sha256-deduped upsert
- routes: POST/GET /games, POST/GET /snapshots (import, deduped), GET /diff
          (?old&new[&owner]) running compute_diff on stored snapshots, /health
- ams.api.importer : bulk CLI loader (python -m ams.api.importer --game ...)
- run: uvicorn ams.api.app:create_app --factory

11 tests pass (6 diff + 5 API via TestClient over the golden pair). Smoke-tested
live on uvicorn: import -> /snapshots -> /diff returns the BlooMoo deltas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:27:24 +02:00

42 lines
1.5 KiB
Python

from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Body, Depends, HTTPException, Query
from sqlalchemy import select
from sqlalchemy.orm import Session
from .. import models, schemas, service
from ..db import get_db
router = APIRouter(prefix="/snapshots", tags=["snapshots"])
@router.post("", response_model=schemas.SnapshotOut, status_code=201)
def create_snapshot(
data: dict[str, Any] = Body(..., description="a full engine-surface snapshot.json"),
game: str | None = Query(None, description="link to / create this game by name"),
db: Session = Depends(get_db),
) -> models.Snapshot:
if not service.looks_like_snapshot(data):
raise HTTPException(422, "body is not an engine-surface snapshot (missing binary/types)")
return service.import_snapshot(db, data, game)
@router.get("", response_model=list[schemas.SnapshotOut])
def list_snapshots(
game_id: int | None = Query(None), db: Session = Depends(get_db)
) -> list[models.Snapshot]:
q = select(models.Snapshot)
if game_id is not None:
q = q.where(models.Snapshot.game_id == game_id)
return list(db.scalars(q.order_by(models.Snapshot.id)))
@router.get("/{snapshot_id}", response_model=schemas.SnapshotDetail)
def get_snapshot(snapshot_id: int, db: Session = Depends(get_db)) -> models.Snapshot:
snap = db.get(models.Snapshot, snapshot_id)
if snap is None:
raise HTTPException(404, "snapshot not found")
return snap