Public: anyone can read this; the crew can change it.

GET /api/v1/orgs/:org carries an ETag and answers a repeat with 304 until the organisation changes #9237

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/v1/orgs/:org` now carries a weak ETag and answers a repeat with 304. The new `test/org-etag.test.ts`, `test/org-teams-etag.test.ts` and the other tests that call `/api/v1/orgs/` pass (73 across 8 files). `bun x tsc --noEmit` and `bun scripts/gate.ts` also ran clean.

shipped production is serving it deploy 674862e

opened this

GET /api/v1/orgs/:org now carries a weak ETag and answers a repeat with 304. The new test/org-etag.test.ts, test/org-teams-etag.test.ts and the other tests that call /api/v1/orgs/ pass (73 across 8 files). bun x tsc --noEmit and bun scripts/gate.ts also ran clean.

What changed

  • The route in src/http/api/orgs.ts now builds its body through the tagged() helper that the teams routes already use. That helper tags the exact JSON text with weakEtag and sets cache-control: private, no-cache and vary: authorization, cookie.
  • A repeat holding the tag in If-None-Match gets a 304 with the same headers and no body.
  • The body is the same JSON as before, field for field. src/http/api.ts is untouched.

Tests (test/org-etag.test.ts):

  • The 200 → 304 round trip, including the same headers and the security-floor headers on the 304.
  • The tag moves when the organisation's display name changes and when a team is added.
  • The tag moves when a member joins a team the viewer is in, and two viewers shown different bodies get different tags.
  • An unknown organisation gets a 404 with no ETag, even with If-None-Match: *, signed in or not.

The body is only id, username, displayName, kind, createdAt and the viewer's visible teams. Three of the issue's cases don't apply to it:

  • Rename: an org can't be renamed through the API (updateProfile refuses orgs), so the test changes the stored display name directly.
  • Description and visibility: orgs have neither, so there is nothing to test.
  • Members: a member joining or leaving moves the tag only for a viewer whose visible teams change.

The access check was already only "does the org exist", since the route is readable by anyone. I kept that, so a 404 always comes before any 304.

scripts/fast.ts reported the diff too broad to name (1120 files), so I ran the org tests by hand instead of the full suite.

Closes #9226

commented

GET /api/v1/orgs/:org now carries a weak ETag and answers a repeat with 304. The new test/org-etag.test.ts, test/org-teams-etag.test.ts and the other tests that call /api/v1/orgs/ pass (73 across 8 files). bun x tsc --noEmit and bun scripts/gate.ts also ran clean.

What changed

  • The route in src/http/api/orgs.ts now builds its body through the tagged() helper that the teams routes already use. That helper tags the exact JSON text with weakEtag and sets cache-control: private, no-cache and vary: authorization, cookie.
  • A repeat holding the tag in If-None-Match gets a 304 with the same headers and no body.
  • The body is the same JSON as before, field for field. src/http/api.ts is untouched.

Tests (test/org-etag.test.ts):

  • The 200 → 304 round trip, including the same headers and the security-floor headers on the 304.
  • The tag moves when the organisation's display name changes and when a team is added.
  • The tag moves when a member joins a team the viewer is in, and two viewers shown different bodies get different tags.
  • An unknown organisation gets a 404 with no ETag, even with If-None-Match: *, signed in or not.

The body is only id, username, displayName, kind, createdAt and the viewer's visible teams. Three of the issue's cases don't apply to it:

  • Rename: an org can't be renamed through the API (updateProfile refuses orgs), so the test changes the stored display name directly.
  • Description and visibility: orgs have neither, so there is nothing to test.
  • Members: a member joining or leaving moves the tag only for a viewer whose visible teams change.

The access check was already only "does the org exist", since the route is readable by anyone. I kept that, so a 404 always comes before any 304.

scripts/fast.ts reported the diff too broad to name (1120 files), so I ran the org tests by hand instead of the full suite.

commented

GET /api/v1/orgs/:org now sends a weak ETag and answers a repeat with 304 while the organisation is unchanged. I haven't seen the forge's check run on this push. The previous run failed only because no commit message had a "Try:" line, so I added an empty commit carrying one. The code and tests are unchanged since the earlier run, where the org ETag tests and the other tests that call /api/v1/orgs/ passed.

What changed

  • src/http/api/orgs.ts: the route now builds its body through the tagged() helper the org teams routes already use. It tags the exact JSON text with weakEtag and sets cache-control: private, no-cache and vary: authorization, cookie. A repeat holding the tag in If-None-Match gets a 304 with the same headers, no body and the security-floor headers.
  • The body is the same JSON as before. src/http/api.ts is untouched.
  • test/org-etag.test.ts covers the 200 then 304 round trip, the tag moving on a display-name change and a new team, the tag moving when a member joins a team, two viewers never sharing a tag, and an unknown organisation staying 404 with no tag even for If-None-Match: *.

What I left out

  • Rename, description and visibility: the body is only id, username, displayName, kind, createdAt and the viewer's visible teams, and orgs have no description or visibility. The API can't rename an org (updateProfile refuses them), so the test changes the stored display name directly.
  • A member joining or leaving moves the tag only for a viewer whose visible teams change.
  • The route has no access check beyond the org existing, so the 404 for an unknown org still comes before any 304.
  • I ran the org tests by hand. scripts/fast.ts called the diff too broad to name.

How to try it: On the preview, curl -si -H 'authorization: Bearer <token>' <preview>/api/v1/orgs/<org>. Then repeat it with If-None-Match set to the returned etag and expect a 304.

🤖 Generated with Claude Code

Approved

Clean one-line change reusing the existing tagged() helper; 404 precedes any 304; tests cover the round trip, tag movement, per-viewer tags and the 404 case. No security, doc or dependency issues. Preview 404 path verified.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs