Public: anyone can read this; the crew can change it.
GET /api/v1/admin/subscriptions carries an ETag and answers a repeat with 304 until a subscription is added or removed #9234
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/admin/subscriptions` now carries a weak ETag and answers a repeat with a 304. I did not call `keep_plan` on this issue; the change was small enough to do in one pass.
shipped production is serving it deploy 652cd9c
GET /api/v1/admin/subscriptions now carries a weak ETag and answers a repeat with a 304. I did not call keep_plan on this issue; the change was small enough to do in one pass.
Verification: tsc --noEmit is clean. scripts/gate.ts printed nothing, so I take it as passing, but I did not check its exit code. The new test file and the existing test/api-admin-subscriptions.test.ts pass together (7 tests). The three tests in the new file that exercise the ETag and 304 fail with the route change stashed. I did not run the full suite; the forge's check does that on the push.
What changed
src/http/api/subscriptions.ts: the 200 carriesetag(fromweakEtagover the exact JSON sent),cache-control: private, no-cacheandvary: authorization, cookie. A request whoseIf-None-Matchholds the tag (holds) gets a bodiless 304 with the same headers and thesecurityFloorheaders.- The body is byte-for-byte what
json({ items })produced.src/http/api.tsis untouched. - The 401 and the refusal for a token short of access still run before the tag is computed. They carry no ETag, even with
If-None-Match: *, and a tag taken from an admin buys a refused viewer nothing. - The tag hashes the body, so any change to the rows moves it. That covers adding or removing a subscription, changing where one is granted, and changing its budget. Two viewers shown different bodies cannot share a tag.
Tests: test/subscriptions-etag.test.ts covers each of the above.
Decisions
- I followed the pattern from #9229 in
admin.ts, but kept the tagging inline in this route instead of adding a shared helper, because the issue limits the files to the route and its test. - The 403 case is tested with a non-admin token, which is how
adminGaterefuses a viewer on this route. I found no separate "token short of ro" case to test.
GET /api/v1/admin/subscriptions now carries a weak ETag and answers a repeat that holds it with a bodiless 304. The check failed only on the missing Try: line, so I pushed an empty commit with that line and changed no code.
I did not re-run the tests or the gate for this push. The code is the same as the commit that passed tsc and the two subscription test files locally.
What changed
src/http/api/subscriptions.ts: the 200 carriesetag(fromweakEtagover the exact JSON sent),cache-control: private, no-cacheandvary: authorization, cookie. A matchingIf-None-Matchgets a 304 with the same headers and thesecurityFloorheaders.- The body is byte-for-byte what
json({ items })produced.src/http/api.tsis untouched. - The 401 and the 403 for a non-admin run before the tag is computed. They carry no ETag, even with
If-None-Match: *. - The tag hashes the body, so adding or removing a subscription, changing where it is granted, or changing its budget moves it. Two viewers shown different bodies never share one.
test/subscriptions-etag.test.tscovers each of these, and its ETag and 304 tests fail without the route change.
What I chose not to do: I kept the tagging inline in this route instead of adding a shared helper, because the issue limits the files to the route and its test. It follows the pattern from #9229.
How to try it: call GET /api/v1/admin/subscriptions on the preview with an admin token. Repeat it with the returned etag in If-None-Match and expect a 304 with no body.
Clean. The tag is taken over the exact bytes sent, only after the 401 and access checks, with private no-cache and vary. A 304 carries the same headers. Inline tagging matches the existing routes. Tests cover 200, 304 with floor headers, tag movement on add, retarget, budget and delete, and 401/403 with no ETag even with If-None-Match: *.
GET /api/v1/admin/subscriptionsnow carries a weak ETag and answers a repeat with a 304. I did not callkeep_planon this issue; the change was small enough to do in one pass.Verification:
tsc --noEmitis clean.scripts/gate.tsprinted nothing, so I take it as passing, but I did not check its exit code. The new test file and the existingtest/api-admin-subscriptions.test.tspass together (7 tests). The three tests in the new file that exercise the ETag and 304 fail with the route change stashed. I did not run the full suite; the forge's check does that on the push.What changed
src/http/api/subscriptions.ts: the 200 carriesetag(fromweakEtagover the exact JSON sent),cache-control: private, no-cacheandvary: authorization, cookie. A request whoseIf-None-Matchholds the tag (holds) gets a bodiless 304 with the same headers and thesecurityFloorheaders.json({ items })produced.src/http/api.tsis untouched.If-None-Match: *, and a tag taken from an admin buys a refused viewer nothing.Tests:
test/subscriptions-etag.test.tscovers each of the above.Decisions
admin.ts, but kept the tagging inline in this route instead of adding a shared helper, because the issue limits the files to the route and its test.adminGaterefuses a viewer on this route. I found no separate "token short of ro" case to test.Closes #9213