# Session Log: 2026-04-13 — Dataforth ## Summary Continuation of the test datasheet pipeline work. Prior session (2026-04-12) confirmed PostgreSQL migration complete; Hoffman provided the new Swagger API URL; awaiting OAuth credentials. Today: reviewed the full API spec, prepared a structured question list for a Zoom call with Hoffman, and discussed architecture options (raw file upload vs. structured record push vs. direct DB). Also helped user triage an unrelated Neptune Exchange mail-flow issue (tsorensen → external bounce). User resolved on their own before I got into it. ## Work completed ### API spec review Pulled `https://www.dataforth.com/swagger/v1/swagger.json` and mapped endpoints. **Base URL:** `https://www.dataforth.com` (presumed; Swagger UI at `/swagger/index.html`) **Authentication (IdentityServer-style)** - Flow: **OAuth2 Authorization Code + PKCE** - Authorization URL: `https://login.dataforth.com/connect/authorize` - Token URL: `https://login.dataforth.com/connect/token` - Scopes: `openid`, `profile`, `dataforth.web` - Swagger's own test client: `client_id = dataforth.swagger` (NOT for our use) - OIDC discovery expected at: `https://login.dataforth.com/.well-known/openid-configuration` **All endpoints** | Path | Method | |------|--------| | `/api/v1/Admin/refresh-cache` | POST | | `/api/v1/Admin/cache-status` | GET | | `/api/v1/Categories` | GET | | `/api/v1/Categories/{id}` | GET | | `/api/v1/Categories/by-catalog-node/{catalogNodeId}` | GET | | `/api/v1/OrderableProducts/{orderableProductId}/Attributes` | POST | | `/api/v1/OrderableProducts/{orderableProductId}/Attributes/{attributeId}` | PUT/DELETE | | `/api/v1/Products`, `/{id}`, `/by-part-number/{partNumber}` | GET | | `/api/v1/product-series`, `/{id}`, `/by-designation/{designation}`, `/by-catalog-node/{catalogNodeId}` | GET | | `/api/v1/ProductType`, `/{productTypeId}/products` | GET | | `/api/v1/TestReportDataFiles` | POST (single upload) | | `/api/v1/TestReportDataFiles` | GET (paginated list) | | `/api/v1/TestReportDataFiles/bulk` | POST (batch upload) | | `/api/v1/TestReportDataFiles/{serialNumber}` | GET / DELETE | | `/api/v1/TestReportDataFiles/stats` | GET | **TestReportDataFiles payload shapes** - POST single: `{ SerialNumber: string(max 50), Content: string(min 1) }` → `{ SerialNumber, ContentHash, Created }` - POST bulk: `{ Items: [CreateTestReportRequest, ...] }` → `{ TotalReceived, Created, Updated, Unchanged, Errors[] }` - GET single: `{ SerialNumber, Content, CreatedAtUtc, UpdatedAtUtc }` - GET stats: `{ TotalCount, LatestCreatedAtUtc, LatestUpdatedAtUtc }` - Server handles dedup via ContentHash → client doesn't need to pre-check. ### Architecture discussion Three options for delivering datasheets: - **A: Raw file blob via current API** — works today, zero new API work, simple client code - **B: Structured records via new endpoints** — cleaner long-term; we already have parsed data in AD2's PostgreSQL `TestDataDB` (2.8M records post-2026-04-12 migration). Requires Hoffman to add endpoints - **C: Direct DB access** — rejected (coupling, security, DBA nightmare) Preferred path: whichever is less work for Hoffman. Frame it as offering flexibility — we can send raw text, structured JSON, or even CSV. ### Questions prepared for John Hoffman Zoom call Produced a prioritized list (MUST / SHOULD / NICE) covering: - Batch size + payload size + rate limits (MUST) - Idempotency + dedup semantics (MUST) - Cutover plan from old DataforthWebShare path (MUST) - Request: enable `client_credentials` grant on a new client for the AD2 uploader (SHOULD) - Staging endpoint availability (SHOULD) - PDF handling (`X:\For_Web_PDF`) — same endpoint or different? (SHOULD) - Product linkage — does a TestReport need to link to a Product/Series record? (SHOULD) - Monitoring + error visibility on his side (NICE) - SLA / escalation contact (NICE) ### Pending from Hoffman (as of end-of-session 2026-04-13) - OAuth credentials (he said "today") - Clarification on client_credentials grant support - Answers to the MUST questions above after the Zoom ## Pipeline context (unchanged from 2026-04-12) ### Current state - **Stage 1**: DOS test stations → D2TESTNAS (192.168.0.9, rsync daemon, module "test" → /data/test) ✓ - **Stage 2**: NAS → AD2 via `Sync-FromNAS-rsync.ps1` scheduled every 15 min ✓ - **Stage 3**: DFWDS.exe validates + renames — **config wiped in crypto attack**; `C:\DFWDS\DFWDS_NAMES.TXT` missing. Check Haubner D: for backup. - **Stage 4**: Website upload — **BROKEN**; this is what we're rebuilding via the new API - **Stage 5**: PDF generation — ~4,773 PDFs in `X:\For_Web_PDF`, origin unclear ### Data locations - Incoming: `X:\Test_Datasheets` (staging) - Validated: `X:\For_Web` (~501K files) ← uploader source - PDFs: `X:\For_Web_PDF` (~4.7K files) - Rejected: `X:\Bad_Datasheets` (~18K) - DFWDS logs: `X:\Datasheets_Log` - `X:` = `\\ad2\webshare` ### Datasheet format Plain text, ~50 lines. Header: Dataforth address/phone. Fields: Date, Model (e.g. SCM5B41-03), SN (e.g. 178439-1), accuracy test table, final test results. Filename: `{SN}.txt` (e.g. `178439-1.txt`). ### Credentials used/referenced - **Old upload path** (being replaced): `DataforthWebShare / Data6277` - **New API**: OAuth client credentials pending from Hoffman - **Neptune Exchange** (for today's mail triage): `ACG\administrator` / `Gptf*77ttb##` — requires VPN ## Next session plan 1. Receive OAuth creds from Hoffman (client_id + client_secret, ideally client_credentials grant enabled) 2. Store credentials in `D:\vault\clients\dataforth\dataforth-api-oauth.sops.yaml` 3. Stand up a one-page POC: get token, POST one test report, verify via GET 4. If POC works → implement full uploader on AD2: - Language: PowerShell (fits existing scripts) or Python (already used in `projects/dataforth-dos/datasheet-pipeline/implementation/`) - State tracking: local manifest (serial → hash + last-upload-time) or use server's ContentHash response - Use `/bulk` endpoint in batches (size TBD with Hoffman) - Scheduled task on AD2, 15-min or hourly cadence - Initial backfill script for 501K files — run off-hours 5. Parallel-run with old webshare path until confident, then retire old path ## Reference URLs - Swagger UI: https://www.dataforth.com/swagger/index.html - Swagger JSON: https://www.dataforth.com/swagger/v1/swagger.json - Authorization URL: https://login.dataforth.com/connect/authorize - Token URL: https://login.dataforth.com/connect/token - Expected OIDC discovery: https://login.dataforth.com/.well-known/openid-configuration