Skip to content

feat(api): typed OpenAPI client consumed by the SPA (Phase 0 finish)#23

Merged
dnplkndll merged 2 commits into
modernizationfrom
phase0-typed-api-client
Jul 7, 2026
Merged

feat(api): typed OpenAPI client consumed by the SPA (Phase 0 finish)#23
dnplkndll merged 2 commits into
modernizationfrom
phase0-typed-api-client

Conversation

@dnplkndll

@dnplkndll dnplkndll commented Jun 22, 2026

Copy link
Copy Markdown

What changed

Finishes Phase 0's typed-client gate: the OpenAPI schema + a typed TypeScript client are committed, the SPA consumes the client, and CI drift-gates it against the live API.

Most of the plumbing already existed (drf-spectacular configured, /api/schema/ + swagger/redoc routes, gen_api_client.sh, a CI step). What was missing — and is delivered here — is the committed client + actual consumption + a drift gate.

Committed artifacts (source of truth)

  • generated/openapi.yaml — OpenAPI 3 schema from frepplectl spectacular --validate (covers the DRF input/master-data CRUD).
  • frontend/lib/api-types.ts — typed client (openapi-typescript, pinned 7.0.0). Now committed (it was gitignored before it had a consumer) so the SPA builds anywhere without a Django runtime.

SPA consumption

  • frontend/lib/apiSchema.ts — typed entrypoint re-exporting the order schemas + StatusCa7Enum.
  • orders.tsORDER_STATUSES … as const satisfies readonly OrderStatus[] type-couples the Orders screen to the API enum: a renamed/removed status in Django now breaks the frontend typecheck. (records.ts Column.options widened to readonly.)
  • ✅ Frontend tsc --noEmit clean + 73 vitest tests pass.

Drift gate

  • tools/modernization/gen_api_client.sh regenerates schema → client → strict typecheck, pinning openapi-typescript to the frontend's version so CI regeneration is byte-identical.
  • ubuntu24.yml runs it then git diff --exit-code — the committed schema/client can't drift from the live API.

Scope notes

  • The streaming output endpoints (pivot/pegging) are plain Django views (no DRF serializer) → absent from the schema by design; they keep hand-written shapes (dynamic time-bucket columns).
  • The typed client immediately earned its keep, surfacing two contract nuances (the ManufacturingOrder schema has no item field; bulk-CRUD operationId collisions) — noted in the plan as non-blocking cleanups.

Large diff is the two generated files (~25k lines) — review the hand-written apiSchema.ts / orders.ts / script / workflow. No UI change → no screenshots. Per convention, not auto-merging.


Self-review pass

  • Squashed to 2 clean commits: hand-written code/config/docs, then the generated artifacts (so reviewers can skip the ~25k generated lines).
  • Trimmed apiSchema.ts to the consumed surface (removed speculative OrderPatch/EditableOrderField).
  • gen_api_client.sh reads the openapi-typescript version from either dependencies or devDependencies (robustness).
  • Confirmed the ManufacturingOrder serializer genuinely omits item (the MO grid's item is view/annotation-derived) — kept as a documented non-blocking finding.
  • No visual change (compile-time type coupling only) → no screenshots.

Finish Phase 0's typed-client gate: wire the SPA to consume a typed client
generated from the live OpenAPI schema, and drift-gate it in CI so it can't
go stale.

- frontend/lib/apiSchema.ts: typed entrypoint re-exporting the order schemas +
  the StatusCa7Enum from the generated client (frontend/lib/api-types.ts).
- frontend/lib/orders.ts: ORDER_STATUSES is now
  `... as const satisfies readonly OrderStatus[]`, type-coupling the Orders
  screen to the API enum (a renamed/removed status breaks the typecheck).
- frontend/lib/records.ts: Column.options widened to `readonly string[]` to
  accept the const-asserted list.
- frontend/.gitignore: stop ignoring lib/api-types.ts now that it has a consumer
  (committed so the SPA builds without a Django runtime; the Docker frontend
  build has no Django to regenerate it).
- tools/modernization/gen_api_client.sh: regenerate schema -> client -> strict
  typecheck, pinning openapi-typescript to the frontend's version so CI
  regeneration is byte-identical to a local run.
- .github/workflows/ubuntu24.yml: after regenerating, `git diff --exit-code`
  drift-gates the committed schema + client; runs on PRs into modernization
  (base_ref) and uploads the regenerated client even on a gate failure.

The streaming OUTPUT endpoints (pivot/pegging) are plain Django views (no DRF
serializer) so they're absent from the schema by design and keep hand-written
shapes. Two contract nuances the typed client surfaced (MO schema has no `item`
field; bulk-CRUD operationId collisions) are noted in the plan as non-blocking.

Frontend `tsc --noEmit` clean; 73 vitest tests pass.
…, CI-verified)

Generated artifacts, regenerated + drift-checked by ubuntu24.yml:
- generated/openapi.yaml      (drf-spectacular, with a migrated DB so the
                               model-choices enum is complete)
- frontend/lib/api-types.ts   (openapi-typescript 7.0.0)
Reviewers can skip these; they're machine-generated and gate-verified.
@dnplkndll dnplkndll force-pushed the phase0-typed-api-client branch from d4629cf to 407d4fb Compare June 22, 2026 18:48
@dnplkndll dnplkndll merged commit 6d58f1f into modernization Jul 7, 2026
7 checks passed
@dnplkndll dnplkndll deleted the phase0-typed-api-client branch July 7, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant