Threadline.OperatorSurface.Controllers.ExportController
(Threadline v0.5.0)
Copy Markdown
View Source
HTTP-side parity controller for the operator-surface "Download CSV / JSON
/ NDJSON" affordances. Three actions (csv/2, json/2, ndjson/2); each
dispatches through one private dispatch/3 so the format-vs-transport
branching is in one place.
Per request:
- Parse URL params via
Threadline.OperatorSurface.Exports.FilterParams.parse/1(single source of truth shared withTimelineLive). - Re-validate via
Threadline.Query.validate_timeline_filters!/1(single source of truth shared with the lib + Mix task). - Pre-flight
Threadline.Export.count_matching/2withcap: 10_001so multi-million-row tables short-circuit instead of hittingstatement_timeout. - Dispatch:
count <= 5_000: full iodata viato_csv_iodata/2/to_json_document/2andsend_resp(200, iodata).count > 5_000:send_chunked(200)then stream viastream_export_rows(filters, page_size: 1_000) |> Stream.take(10_000) |> Stream.chunk_every(500) |> Enum.reduce_while/3callingPlug.Conn.chunk/2per chunk; halts on client disconnect.
Response headers (set BEFORE send_chunked/2 per Plug API):
Content-Type: text/csv; charset=utf-8/application/json; charset=utf-8/application/x-ndjson; charset=utf-8Content-Disposition: attachment; filename="<canonical>"; filename*=UTF-8''<canonical>(RFC 6266 §4.3 dual-emit)Cache-Control: no-store(audit-data hygiene)
Filename comes from Threadline.OperatorSurface.Exports.Filename.for/2
(UTC, minute granularity, hyphen-not-colon between hours and minutes for
Windows compatibility).