Skip to content

Commit 20c7da8

Browse files
authored
[#1403] Frontend-react data layer: HTTP + TanStack Query + OpenAPI codegen (#1428)
* [#1403] Frontend-react data layer: HTTP client + TanStack Query + OpenAPI codegen HTTP wrapper at src/lib/http.ts handles bearer + CSRF, /api/v1/<resource> to /api/v1/g/{slug}/<resource> rewriting, single-flight refresh on 401, and surfaces non-2xx as HttpError so React Query can react via onError. Storage keys (inventario_token, inventario_csrf_token, inventario_user) match the legacy Vue frontend so a session survives the dual-bundle INVENTARIO_FRONTEND switch (#1401). QueryClient defaults: staleTime 30s, retry 1 (skipped for 4xx). Providers wraps QueryClientProvider + ReactQueryDevtools (dev only) + ThemeProvider; main.tsx uses it. OpenAPI codegen converts go/docs/swagger.json (Swagger 2.0) to OAS3 via swagger2openapi, then emits src/types/api.d.ts via openapi-typescript. Exposed as npm run codegen / codegen:check, make codegen-frontend-react / codegen-frontend-react-check, and a new frontend-react-codegen workflow that fails the build on drift. Sample feature slice at features/session/ proves the layer end-to-end: useCurrentUser query and an optimistic useLogout mutation with cache rollback on error. Tests: 40 (Vitest + MSW v2) covering each prefix rewrite, /auth/* pass-through, slug encoding, CSRF only on mutations + rotation from response headers, AbortSignal forwarding, 4xx/5xx HttpError, the full refresh+retry path, refresh-failure clear+navigate, background /auth/me 401 non-redirect, and single-flight refresh dedup. .npmrc pins legacy-peer-deps=true because openapi-typescript@7 declares peer typescript@^5.x while the scaffold is on TS 6 — the peer is build-time only and emit goes through openapi-typescript's bundled compiler, so the mismatch is benign. Drop the flag once openapi-typescript ships TS 6 support. Closes #1403. * [#1403] Document legacy-peer-deps workaround with upstream PR link The .npmrc workaround is metadata-only — npm ls confirms typescript@6.0.3 is the only version installed. Add a TODO link to openapi-ts/openapi-typescript#2774 so it's clear when the flag can come off. * [#1403] Address CI failures + Copilot review - Dockerfile: copy frontend-react/.npmrc into the React builder stage so npm ci picks up legacy-peer-deps=true (Build linux/{amd64,arm64} fix). - ApiResponse helper: try application/vnd.api+json first then fall back to application/json, and accept any 2xx status. Most Inventario endpoints serve JSON:API, so the previous 'application/json'-only shape resolved to never for them. - useLogout: removeQueries() instead of setQueryData(..., null) so the cache stays type-true to the query's CurrentUser shape; consumers see data === undefined rather than a surprise null at runtime. - Drop the meCalls > 1 assertion in the optimistic-logout test — it encoded an implementation detail (invalidate-after-remove timing) that did not survive the removeQueries switch. New assertion pins the real invariant: cache stays cleared after a successful logout. - Prettier: re-format http.ts, http.test.ts, types/index.ts, generated api.d.ts, and the session test (Lint Frontend (React) job fix). * [#1403] Exclude generated api.d.ts from prettier The previous commit's prettier sweep reformatted src/types/api.d.ts, which then disagreed with what scripts/codegen.mjs produces — the codegen-drift CI job rejected the result. Add .prettierignore to keep the generator's emit untouched, and restore api.d.ts to the codegen-true output.
1 parent 59ef8da commit 20c7da8

25 files changed

Lines changed: 8158 additions & 33 deletions
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Frontend React Codegen Drift
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
11+
cancel-in-progress: true
12+
13+
permissions:
14+
contents: read
15+
pull-requests: read
16+
17+
jobs:
18+
codegen-drift:
19+
name: Check OpenAPI → TS codegen sync
20+
runs-on: ubuntu-latest
21+
defaults:
22+
run:
23+
working-directory: frontend-react
24+
steps:
25+
- name: Checkout code
26+
uses: actions/checkout@v6
27+
28+
- id: vars
29+
uses: ./.github/actions/vars
30+
31+
- name: Set up Node.js
32+
uses: actions/setup-node@v6
33+
with:
34+
node-version: ${{ steps.vars.outputs.node_version }}
35+
cache: 'npm'
36+
cache-dependency-path: frontend-react/package-lock.json
37+
38+
- name: Install dependencies
39+
run: npm ci
40+
41+
- name: Verify src/types/api.d.ts is up to date
42+
run: npm run codegen:check

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ FROM node:24.14.1-alpine AS frontend-react-builder
3232

3333
WORKDIR /app/frontend-react
3434

35-
COPY frontend-react/package*.json ./
35+
# .npmrc carries `legacy-peer-deps=true` (openapi-typescript@7's stale TS5
36+
# peer dec — see frontend-react/.npmrc); without it `npm ci` exits non-zero.
37+
COPY frontend-react/package*.json frontend-react/.npmrc ./
3638

3739
RUN --mount=type=cache,target=/root/.npm \
3840
npm ci

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,17 @@ lint-frontend-react:
237237
typecheck-frontend-react:
238238
$(CD) $(FRONTEND_REACT_DIR) && npm run typecheck
239239

240+
# Regenerate React frontend's TypeScript types from go/docs/swagger.json.
241+
# Run after `go tool swag init --output docs` whenever a swagger annotation
242+
# changes. CI guards against drift via `codegen-frontend-react-check`.
243+
.PHONY: codegen-frontend-react
244+
codegen-frontend-react:
245+
$(CD) $(FRONTEND_REACT_DIR) && npm run codegen
246+
247+
.PHONY: codegen-frontend-react-check
248+
codegen-frontend-react-check:
249+
$(CD) $(FRONTEND_REACT_DIR) && npm run codegen:check
250+
240251
# Check that all Go entity schema changes have a corresponding migration.
241252
# Requires POSTGRES_TEST_DSN to point to a PostgreSQL instance that has all migrations applied.
242253
.PHONY: lint-migrations

frontend-react/.npmrc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# openapi-typescript@7.13.0 (latest stable as of 2026-04-29) declares
2+
# `peerDependencies.typescript: "^5.x"`, but the entire repo runs TypeScript
3+
# 6.0.3 (frontend/, frontend-react/, e2e/). The peer is build-time only —
4+
# openapi-typescript emits its .d.ts via its own bundled compiler, our generated
5+
# types are consumed by *our* TS 6.0.3 — so the mismatch is metadata-only.
6+
# Verified: `npm ls typescript` shows 6.0.3 everywhere, no parallel TS5 install.
7+
#
8+
# Drop this once openapi-ts/openapi-typescript#2774 ("feat: add TypeScript 6
9+
# support") merges and a release ships:
10+
# https://github.com/openapi-ts/openapi-typescript/pull/2774
11+
legacy-peer-deps=true

frontend-react/.prettierignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Auto-generated files. Owned by their respective generators; running
2+
# prettier here just diverges from the codegen output and trips the
3+
# drift-check CI job.
4+
src/types/api.d.ts
5+
dist/
6+
coverage/
7+
node_modules/

frontend-react/eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default [
4747
},
4848
},
4949
{
50-
files: ["vite.config.ts", "vitest.config.ts", "eslint.config.js"],
50+
files: ["vite.config.ts", "vitest.config.ts", "eslint.config.js", "scripts/**/*.{js,mjs,ts}"],
5151
languageOptions: {
5252
globals: { ...globals.node, ...globals.es2022 },
5353
},

0 commit comments

Comments
 (0)