Skip to content

feat(slice-4f): wire Admin section pages to existing /api endpoints#40

Merged
lopadova merged 6 commits into
mainfrom
task/v1.7-slice-4f-admin
May 20, 2026
Merged

feat(slice-4f): wire Admin section pages to existing /api endpoints#40
lopadova merged 6 commits into
mainfrom
task/v1.7-slice-4f-admin

Conversation

@lopadova
Copy link
Copy Markdown
Contributor

Summary

v1.7 slice 4f — Admin section pages (API tokens, Org & project, Audit-admin) now read from the existing `/api/tokens` / `/api/orgs` / `/api/audit` endpoints with graceful fixture fallback.

Admin

  • PageTokens → GET `/api/tokens` (x-aqa-org). Live tokens replace the fixture rows when the server responds.
  • PageOrg → GET `/api/orgs`. Subtitle reflects the live org slugs (joined) or falls back to "padosoft" / "No orgs configured".
  • PageAdminAudit → GET `/api/audit` (x-aqa-org). Same normalizer + cancellation guard as PageAudit (slice 4e).

Scope note

Users / Roles / SSO are intentionally deferred — no server scaffolding exists for those (would require new schemas + routes). Slice 4f ships what's wirable against the current route surface.

E2E

  • 4 new tests in `admin-section.e2e.ts` (tokens live, orgs live, orgs fallback, audit-admin live).

Test plan

  • `bun run --filter @aqa/admin test:e2e -- --workers=2` (132 passed, 1 skipped)
  • `bun run typecheck`
  • `bun run lint` (4 preexisting warnings)

🤖 Generated with Claude Code

… to existing endpoints

Admin:
- PageTokens fetches GET /api/tokens (x-aqa-org), replaces the
  local fixture with the server payload. Existing 3-row mock stays
  as fallback for mock-data mode.
- PageOrg fetches GET /api/orgs and joins the live org list into
  the page subtitle (falls back to "padosoft" / "No orgs
  configured" depending on response).
- PageAdminAudit shares the same /api/audit wire as PageAudit
  (slice 4e) with admin-view subtitle copy. Schema normalizer +
  unmount-safe cancellation guard applied (same lessons as 4e).

Scope note: Users / Roles / SSO are intentionally deferred — no
server scaffolding exists for them yet (would require new
schemas + routes). Slice 4f ships what's wirable against the
current route surface.

E2E: 4 new tests in admin-section.e2e.ts (tokens live, orgs live,
orgs fallback, audit-admin live). Admin e2e total: 132/133
(+4, 1 skipped). Typecheck + lint clean.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fe1aa79fd0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/admin/src/app.tsx Outdated
if (cancelled || !res.ok) return;
const body = await res.json();
if (cancelled) return;
if (Array.isArray(body?.tokens)) setTokens(body.tokens);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Normalize /api/tokens records before storing in UI state

GET /api/tokens returns ApiToken objects (display_name, last_used_at, prefix, etc.), but this code stores body.tokens directly and later renders fields like t.name, t.kind, and t.last_used; with real server data those columns become blank/incorrect (and the “live” view silently degrades) even though tests pass with a fixture-shaped mock. Map the server payload into the table’s expected view model before setTokens.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Wires the Admin section pages in @aqa/admin to existing server endpoints (/api/tokens, /api/orgs, /api/audit) while keeping fixture-mode fallbacks, and adds Playwright E2E coverage to validate the wiring.

Changes:

  • Added live-fetch wiring for Org & project, API tokens, and Admin audit pages with graceful fallback behavior.
  • Updated Admin audit rendering to normalize server event shapes into the existing AuditChainViewer demo format.
  • Added new Playwright E2E tests for the Admin section live/fallback behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/admin/src/app.tsx Adds live fetch() calls for /api/orgs, /api/tokens, /api/audit and updates page subtitles/viewer input based on live data.
packages/admin/test/e2e/admin-section.e2e.ts Adds E2E wiring tests for Admin pages (tokens/orgs/audit-admin).

Comment on lines +12413 to +12420
const res = await fetch(apiUrl('/api/tokens'), {
headers: { 'x-aqa-org': 'padosoft' },
});
if (cancelled || !res.ok) return;
const body = await res.json();
if (cancelled) return;
if (Array.isArray(body?.tokens)) setTokens(body.tokens);
} catch {
Comment thread packages/admin/src/app.tsx Outdated
Comment on lines +12376 to +12377
// hashed token records (no raw secret); the create flow (POST
// /api/tokens) returns the freshly-issued raw token exactly once.
Comment on lines +34 to +37
name: 'Live token from server',
kind: 'service',
last_used: '2026-05-19T12:00:00Z',
scopes: ['runs:write'],
Comment on lines +12261 to +12266
const sub =
orgs === null
? 'padosoft'
: orgs.length === 0
? 'No orgs configured · using mock org "padosoft"'
: orgs.map((o) => o.slug ?? o.name ?? '?').join(', ');
…t/UX fixes

Copilot iter-1 (5):
- PageTokens was storing the raw /api/tokens payload (@aqa/schemas
  ApiToken: display_name/prefix/owner/last_used_at/scopes) directly,
  but the page renders fixture-style fields (name/kind/last_used).
  Added an adapter that maps display_name→name, last_used_at→
  last_used, and heuristically derives kind from owner (svc_/bot_/
  ci_/service_ prefix → service, otherwise user). A future server
  schema can expose `kind` directly to remove the heuristic.
- Comment about POST /api/tokens issuing a raw token "exactly
  once" was inaccurate (current server schema only defines
  prefix). Updated the comment.
- E2E mock rewritten to a schema-conforming ApiToken payload so
  the test exercises the adapter and would catch a real contract
  drift.
- Org subtitle copy aligned with the PR description: when the
  live list is empty, just "No orgs configured" (was "No orgs
  configured · using mock org \"padosoft\"").

Tests: admin-section e2e 4/4 green.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines +68 to +69
{ slug: 'padosoft', name: 'Padosoft' },
{ slug: 'acme', name: 'Acme Corp' },
});

test('Audit (admin) page fetches /api/audit and renders the live count', async ({ page }) => {
await page.route('**/api/audit**', async (route) => {
…route stub

Copilot iter-2 (2):
- Org mock payload now matches @aqa/schemas Tenancy.Org
  (schema_version/slug/display_name/created_at) instead of the
  truncated {slug, name}. Page consumes slug for the subtitle
  which still works, but the test now exercises the actual
  contract.
- Audit-admin route stub gated on GET so a future admin
  POST/DELETE on /api/audit isn't silently hijacked.

Tests: admin-section e2e 4/4 green.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +12383 to +12399
() => [
{
id: 'tok_ci',
name: 'CI · GitHub Actions',
kind: 'service',
last_used: '2026-05-18T13:48:00Z',
scopes: ['runs:write', 'findings:read'],
created_at: '2026-04-12T08:00:00Z',
},
{
id: 'tok_sara',
name: 'Sara · CLI laptop',
kind: 'user',
last_used: '2026-05-18T11:14:00Z',
scopes: ['*'],
created_at: '2026-04-02T09:00:00Z',
},
… enum

Copilot iter-3 (1):
- Fallback fixture used pre-schema scopes ('runs:write', '*') that
  don't match ApiTokenScope. Switched to schema-valid values
  ('runs:create', 'findings:read', 'admin:everything',
  'audit:read'). Future token-creation/revocation flows will
  validate cleanly against the same enum the server uses.

Tests: admin-section e2e 4/4 green.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

Comment on lines +12382 to +12386
// PR #40 Copilot iter 3: fallback scope values use the actual
// ApiTokenScope enum (runs:create / findings:edit /
// admin:everything / audit:read), not the pre-schema strings
// ("runs:write" / "*") that wouldn't validate against the server.
const FALLBACK_TOKENS = React.useMemo(
Comment thread packages/admin/src/app.tsx Outdated
: 'user',
last_used: t.last_used_at ?? t.last_used ?? null,
scopes: Array.isArray(t.scopes) ? t.scopes : [],
created_at: t.created_at ?? new Date().toISOString(),
Comment thread packages/admin/src/app.tsx Outdated
Comment on lines +12652 to +12666
<AuditChainViewer
demoGood={
liveEvents !== null
? liveEvents.map((ev) => ({
at: ev.ts ?? ev.at ?? '',
actor:
typeof ev.actor === 'string'
? ev.actor
: ev.actor?.id || ev.actor?.type || 'system',
kind: ev.kind ?? 'event',
payload: ev.payload ?? {},
prev_hash: ev.prev_hash ?? '0'.repeat(64),
hash: ev.hash ?? '0'.repeat(64),
}))
: AUDIT_EVENTS_GOOD
… audit normalizer

Copilot iter-4 (3):
- Create-token modal chips listed pre-schema scopes
  ('runs:write', 'findings:write', 'packs:install', 'admin'). Now
  mirrors @aqa/schemas ApiTokenScope (runs:read/create,
  findings:read/edit, audit:read, admin:everything) so a future
  POST /api/tokens can pass them straight through.
- Adapter defaulted `created_at` to `new Date().toISOString()` when
  the server omitted it — a misleading "just-created" date for
  whatever record lost the timestamp in transit. Now left null;
  downstream formatters can render a placeholder.
- Extracted normalizeAuditEventsForViewer to a top-level helper
  used by both PageAudit and PageAdminAudit. Same normalization
  (ts → at, actor: {type,id} → string, prev_hash null → zero
  sha256, …) in one place — no more divergence as the Event
  schema evolves.

Tests: admin-section + operations e2e green.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +12440 to +12444
// PR #40 Copilot iter 4: leave created_at null when the
// server omits it — defaulting to "now" was misleading
// (showed a fake "just created" date for any record where
// the timestamp was lost in transit).
created_at: t.created_at ?? null,
Copilot iter-5 (1):
- fmtDate/fmtDateTime didn't guard against null — a token with
  null created_at (from iter 4) would render as '1970-01-01'.
  Both formatters now return an em-dash for null/undefined/
  invalid dates, matching the existing fmtRelative null-safety
  pattern.

Tests: admin e2e green.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@lopadova lopadova merged commit 4c93bb7 into main May 20, 2026
16 checks passed
@lopadova lopadova deleted the task/v1.7-slice-4f-admin branch May 20, 2026 03:02
lopadova added a commit that referenced this pull request May 20, 2026
- docs/PROGRESS.md: added a 2026-05-20 entry summarizing every
  v1.7 slice 4 PR (#29..#40), the architecture lessons that
  carried across them, and the final tag step.
- README.md Roadmap table: added v1.4/v1.5/v1.6/v1.7 rows
  matching the actual shipped surface. Status header updated to
  "v1.7 current".

No code changes — closing-step docs only.
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.

2 participants