mirror of
https://github.com/azaion/ui.git
synced 2026-06-21 11:51:10 +00:00
[AZ-485] [AZ-486] Cycle 1 docs refresh (Step 13)
ci/woodpecker/push/build-arm Pipeline was successful
ci/woodpecker/push/build-arm Pipeline was successful
Phase B cycle 1 was a structural refactor only: F4 (barrel imports + STC-ARCH-01) and F7 (endpoint builders + STC-ARCH-02). This commit brings docs in line with source after the cycle, no code changes. Module docs (12 consumers): swap every /api/<service>/... literal in code snippets and integration tables for the matching endpoints.* builder; note the barrel import migration in Dependencies. New module doc: src__api__endpoints.md (public surface, F4 barrel re-export note, STC-ARCH-02 enforcement, contract-test reference). Architecture compliance baseline: mark F4 + F7 CLOSED with commit hashes (23746ec,8a461a2). 01_api-transport component description: add endpoints.ts + barrel to Internal Interfaces, close the F7 caveat, extend Module Inventory. ripple_log_cycle1.md: Task Step 0.5 reverse-dep analysis records the import-graph closure (no extra docs needed beyond the direct set). Carry-over reports landed alongside the docs: - test_run_report_phase_b_cycle1.md (Step 11 outcome) - implementation_report_refactor_phase_b_cycle1.md (cycle summary) State file: trimmed to the autodev <30-line target; Steps 14 + 15 recorded as SKIPPED with rationale (no security or perf surface changed in this cycle); pointer moved to Step 16 (Deploy). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
# Module: `src/api/endpoints.ts`
|
||||
|
||||
> **Source**: `src/api/endpoints.ts` (79 lines)
|
||||
> **Topo batch**: B2 (leaf — no internal imports)
|
||||
> **Introduced**: AZ-486 (2026-05-11, commit `8a461a2`), closing architecture baseline finding F7.
|
||||
|
||||
## Purpose
|
||||
|
||||
Single source of truth for every `/api/<service>/<path>` URL the UI talks to. Replaces the hardcoded string literals that previously lived at each `api.*` / `createSSE` call site (and at every `src={...}` URL for API-served images / videos). The `endpoints` object is the canonical wire-contract documentation: each builder produces a character-identical string to the literal it superseded, so MSW handlers + e2e stubs + the nginx routing table all keep matching.
|
||||
|
||||
Together with the `STC-ARCH-02` static gate (see [Configuration](#configuration)), this module enforces "no hardcoded API path literals in `src/`" as a build-time invariant rather than a code-review aspiration.
|
||||
|
||||
## Public interface
|
||||
|
||||
```ts
|
||||
export const endpoints = {
|
||||
admin: {
|
||||
authRefresh: () => string
|
||||
authLogin: () => string
|
||||
authLogout: () => string
|
||||
users: () => string
|
||||
user: (id: string) => string
|
||||
classes: () => string
|
||||
class: (id: string | number) => string
|
||||
},
|
||||
annotations: {
|
||||
classes: () => string
|
||||
settingsUser: () => string
|
||||
settingsSystem: () => string
|
||||
settingsDirectories: () => string
|
||||
annotations: () => string
|
||||
annotationsByMedia: (mediaId: string, pageSize?: number) => string // pageSize default = 1000
|
||||
annotationImage: (annotationId: string) => string
|
||||
annotationThumbnail: (annotationId: string) => string
|
||||
annotationEvents: () => string
|
||||
media: (queryString: string) => string
|
||||
mediaFile: (mediaId: string) => string
|
||||
mediaItem: (mediaId: string) => string
|
||||
mediaBatch: () => string
|
||||
dataset: (queryString: string) => string
|
||||
datasetItem: (annotationId: string) => string
|
||||
datasetBulkStatus: () => string
|
||||
datasetClassDistribution: () => string
|
||||
},
|
||||
flights: {
|
||||
collection: (queryString?: string) => string // GET ?pageSize=... lists; POST (no qs) creates
|
||||
aircrafts: () => string
|
||||
aircraft: (id: string) => string
|
||||
flight: (id: string) => string
|
||||
flightWaypoints: (id: string) => string
|
||||
flightWaypoint: (flightId: string, waypointId: string) => string
|
||||
flightLiveGps: (id: string) => string
|
||||
},
|
||||
detect: {
|
||||
media: (mediaId: string) => string // POST → trigger detection for a media item
|
||||
},
|
||||
} as const
|
||||
```
|
||||
|
||||
The whole object is `as const`, so each leaf's return type is the narrow string literal where possible (e.g. `'/api/admin/auth/refresh'`) and the parameterised builders carry a `string` return.
|
||||
|
||||
## Internal logic
|
||||
|
||||
- **Pure data + template strings.** No side effects, no I/O, no caching. Every builder is a one-line `() => '...'` or arrow returning a template literal.
|
||||
- **Function form (not constants)**, per direction at task-creation time:
|
||||
- Parameterised paths (e.g. `flight(id)`) need a function anyway. Keeping every entry as a function — even the constant ones — gives a single uniform call shape at every site (`endpoints.x.y()`) so reviewers don't have to remember which entries take parens and which don't.
|
||||
- Per-builder tree-shaking under Vite's production rollup remains intact.
|
||||
- **Query strings owned by the caller for variable-shape paths.** Where the query is dynamic (`flights.collection`, `annotations.media`, `annotations.dataset`), the caller builds a `URLSearchParams.toString()` and the builder owns only the path + `?`. This keeps the wire contract identical to pre-refactor literals at every callsite.
|
||||
|
||||
## Public API (barrel re-export)
|
||||
|
||||
`src/api/index.ts` re-exports `endpoints` alongside `api`, `createSSE`, `setToken`, `getToken`, `getApiBase`, `setNavigateToLogin`. Consumers OUTSIDE the `01_api-transport` component MUST import from the barrel (`import { endpoints } from '@/api'` or `from '../api'`) — direct imports of `src/api/endpoints` from other components are blocked by `STC-ARCH-01` (F4 closure, see `src__api__client.md`).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **Internal**: none.
|
||||
- **External**: none.
|
||||
|
||||
## Consumers (intra-repo)
|
||||
|
||||
After the AZ-486 migration, `endpoints` is imported by:
|
||||
|
||||
- `src/api/client.ts` — internal `refreshToken()` helper uses `endpoints.admin.authRefresh()`.
|
||||
- `src/auth/AuthContext.tsx` — `authRefresh`, `authLogin`, `authLogout`.
|
||||
- `src/components/FlightContext.tsx` — `flights.collection`, `flights.flight`, `annotations.settingsUser`.
|
||||
- `src/components/DetectionClasses.tsx` — `admin.classes`, `admin.class`.
|
||||
- `src/features/admin/AdminPage.tsx` — `admin.users`, `admin.user`.
|
||||
- `src/features/annotations/AnnotationsPage.tsx` — annotation CRUD endpoints, `detect.media`.
|
||||
- `src/features/annotations/AnnotationsSidebar.tsx` — `annotations.annotationEvents` (SSE), bulk-status, dataset endpoints.
|
||||
- `src/features/annotations/CanvasEditor.tsx` — `annotations.annotationImage`, `annotations.annotationThumbnail`.
|
||||
- `src/features/annotations/MediaList.tsx` — `annotations.media`, `annotations.mediaFile`, `annotations.mediaItem`, `annotations.mediaBatch`.
|
||||
- `src/features/annotations/VideoPlayer.tsx` — `annotations.mediaFile`.
|
||||
- `src/features/dataset/DatasetPage.tsx` — `annotations.dataset*` family, `annotations.classes`, `annotations.annotationImage`.
|
||||
- `src/features/flights/FlightsPage.tsx` — full `flights.*` surface + `annotations.settingsUser`.
|
||||
- `src/features/settings/SettingsPage.tsx` — `annotations.settings*`, `flights.aircrafts`.
|
||||
|
||||
This is the full intra-repo consumer list — `STC-ARCH-02` guarantees no production-source caller falls outside it.
|
||||
|
||||
## Data models
|
||||
|
||||
None defined here. Path-string output only.
|
||||
|
||||
## Configuration
|
||||
|
||||
The module IS the API-path configuration. The only "config" is the nginx routing table that maps each `/api/<service>/...` prefix to a concrete backend service — see `src__api__client.md` → External integrations for the live table.
|
||||
|
||||
**Static enforcement (`STC-ARCH-02`)**:
|
||||
|
||||
- **Script**: `scripts/check-arch-imports.mjs --mode=api-literals`.
|
||||
- **Wired into**: `scripts/run-tests.sh` (functional profile, static group) — runs before any unit test.
|
||||
- **What it forbids**: any `/api/<service>/...` literal in `[`'"]` quoting under `src/`.
|
||||
- **Exempt files**: this file (`src/api/endpoints.ts`) and `src/**/*.test.ts(x)` only.
|
||||
- **Bypass policy**: none. Adding a new exempt path requires updating the exempt regex in the script AND a `module-layout.md` rule revision in the same commit.
|
||||
|
||||
## External integrations
|
||||
|
||||
This module integrates nothing directly. It documents — as TypeScript values — the wire contract for every external integration the SPA has, as routed by `nginx.conf`. See the routing table in `src__api__client.md` → External integrations for the per-prefix backend mapping.
|
||||
|
||||
## Security
|
||||
|
||||
- **No bearer plumbing here.** Token injection still happens in `client.ts` (`Authorization` header) and `sse.ts` (`access_token` query parameter). Builders return URLs **without** the token.
|
||||
- **No URL-encoding** of interpolated `id` / `mediaId` / `queryString` parameters. All current callsites pass already-safe values (UUIDs, ints, pre-built `URLSearchParams.toString()` output). If any future caller passes user-controlled text, the builder must add `encodeURIComponent` (see open question below).
|
||||
- **No CSRF surface change** — same posture as the pre-refactor literals.
|
||||
|
||||
## Tests
|
||||
|
||||
- **`src/api/endpoints.test.ts`** (36 Vitest assertions): pins every builder's output to its exact pre-refactor URL string. This is the contract documentation — any wire-contract change MUST update this test in the same commit as the backend / MSW / e2e stub change. Includes one barrel-re-export assertion (`endpoints` is reachable via `import { endpoints } from '../api'`).
|
||||
- **`tests/architecture_imports.test.ts`** (AZ-486 / STC-ARCH-02 suite, 6 cases): verifies the static gate passes on the migrated codebase AND fails when a synthetic single-quoted / double-quoted / template-literal `/api/<service>/...` literal is introduced in `src/`. Also verifies the `*.test.ts` and `//` comment exemptions.
|
||||
|
||||
## Notes / open questions
|
||||
|
||||
- **`detect.media` only exposes the single-segment path** that the UI uses today (`POST /api/detect/<mediaId>`). The full `detect/` service has more endpoints (per the nginx table) but no UI callsite consumes them. Add new builders only when a real callsite needs them — don't pre-populate.
|
||||
- **`flights.collection` overloads its return** on whether `queryString` is provided. Acceptable while the contract is "GET with `?pageSize`, POST without" — but if a third flights-collection verb (DELETE? PUT?) is ever added with its own query shape, split into named builders rather than threading more conditional logic through one.
|
||||
- **No URL-encoding of interpolated params** (see Security). Add `encodeURIComponent` at the first callsite that needs it, plus a contract-test case in `endpoints.test.ts`. Currently safe across all 36 pinned URLs.
|
||||
- **Wire-contract test coverage is exact-string, not shape.** This is deliberate: a "looks like a path" matcher would silently accept a hyphen-to-underscore change that breaks the backend. Updating these strings IS a wire-contract change — treat the test as a release-gate.
|
||||
Reference in New Issue
Block a user