feat(api): GET /api/products/export — streamed canonical CSV, 409 empty_catalog (§6.4, PUC-9)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 22:05:55 -07:00
parent 1c2a6af986
commit 155f9bd147
2 changed files with 62 additions and 1 deletions
+22 -1
View File
@@ -17,7 +17,7 @@ from typing import Any
import psycopg
from fastapi import Depends, FastAPI, File, Query, Response, UploadFile
from fastapi.responses import JSONResponse, PlainTextResponse
from fastapi.responses import JSONResponse, PlainTextResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
@@ -374,6 +374,27 @@ def create_app(database_url: str | None = None, static_dir: str | Path | None =
_account, sf = gate
return products.summary(conn, sf.id)
@app.get("/api/products/export")
def export_products(
status: str = Query(default="all", pattern="^(all|active|draft|archived)$"),
conn: psycopg.Connection = Depends(get_conn),
sess: dict | None = Depends(get_session),
):
"""Stream the catalog as canonical CSV, optionally status-filtered (§6.4; PUC-9)."""
gate = _merchant_gate(conn, sess)
if isinstance(gate, JSONResponse):
return gate
_account, sf = gate
try:
stream = products.export_catalog(conn, sf.id, status)
except products.EmptyCatalog:
return _error(409, "empty_catalog", "There are no products to export.")
return StreamingResponse(
stream,
media_type="text/csv",
headers={"content-disposition": 'attachment; filename="ecomm-products-export.csv"'},
)
@app.get("/api/products/sample.csv")
def products_sample_csv():
"""The DOC-3 worked-example CSV. Documentation, so no auth gate (§6.4)."""