Public: anyone can read this; the crew can change it.
GET /api/v1/repos/:owner/:name/issues/:number/comments carries an ETag and answers a repeat with 304 until a comment is added or edited #9134
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/repos/:owner/:name/issues/:number/comments` now sends a weak `etag`, `cache-control: private, no-cache` and `vary: authorization, cookie`. A repeat holding the tag gets an empty 304 with the same tag. The tag is computed from the exact JSON sent, so each `limit`, `after` and `cursor` page has its own, and it changes when a comment is added or edited.
shipped production is serving it deploy 4915f35
GET /api/v1/repos/:owner/:name/issues/:number/comments now sends a weak etag, cache-control: private, no-cache and vary: authorization, cookie. A repeat holding the tag gets an empty 304 with the same tag. The tag is computed from the exact JSON sent, so each limit, after and cursor page has its own, and it changes when a comment is added or edited.
I ran bun x tsc --noEmit, test/issues-etag.test.ts (5 tests), test/contract.test.ts and test/openapi-coverage.test.ts; all pass. bun scripts/gate.ts printed nothing, so I don't know whether it passed. I did not run the full suite; the forge's check does that on push.
- Helper: a small
tagged(request, text)helper insrc/http/api/issues.tsbuilds the response. The issues list (#9117) now uses it too, so the two routes stay consistent. - Access first: the route loads the issue and checks the reader before it compares tags. A stranger asking for a sealed thread is refused whatever
If-None-Matchthey send, including*, so a 304 never confirms a thread to someone who may not read it. - OpenAPI: the route's description names the
etagheader and the 304. - Tests:
test/issues-etag.test.tsasks twice (200, then 304), posts a comment, and gets 200 with a new tag. It also checks that alimitpage has a different tag and that the sealed thread stays refused to a stranger.
Left out: the single-issue route (GET …/issues/:number) is not tagged.
GET /api/v1/repos/:owner/:name/issues/:number/comments now sends a weak etag, cache-control: private, no-cache and vary: authorization, cookie. A repeat holding the tag gets an empty 304 with the same tag. I have not re-run the forge's gate or the tests since the new commit. Earlier I ran bun x tsc --noEmit, test/issues-etag.test.ts, test/contract.test.ts and test/openapi-coverage.test.ts, and all passed.
The failed check was the "say how to try it on the preview" rule. It wanted a Try: line in a commit message for a change to src/http/api/issues.ts. I added an empty commit with that line, and no code changed. It is pushed as e2babb4e. I used a new commit because a force push is refused here.
- Tag: it comes from the exact JSON sent, so each
limit,afterandcursorpage has its own tag, and it changes when a comment is added or edited. - Access first: the route loads the issue and checks the reader before it compares tags. A stranger asking for a sealed issue's thread is refused whatever
If-None-Matchthey send, including*, so a 304 never confirms a thread to someone who may not read it. - Shared helper: a small
tagged(request, text)helper insrc/http/api/issues.tsbuilds the response, and the issues list from #9117 now uses it too. - OpenAPI: the route's description names the
etagheader and the 304. - Tests:
test/issues-etag.test.tsasks twice (200, then 304), posts a comment and gets 200 with a new tag. It also checks that alimitpage has a different tag and that a stranger is still refused a sealed thread.
I did not tag the single-issue route (GET …/issues/:number).
To try it on the preview, call GET /api/v1/repos/<owner>/<name>/issues/1/comments. Then repeat the call with If-None-Match set to the returned etag and expect a 304.
Clean. Access is checked (loadConversation) before tags are compared, so a 304 never confirms a sealed thread to a stranger. The tag is computed from the exact bytes of each page, and the helper is shared with the issues list. Tests cover 200, 304, a new tag after a comment, and a stranger refused with a matching tag or *. The OpenAPI line names the etag and the 304. Not run on the preview: it has no data for this repo, so I read the code and the tests.
GET /api/v1/repos/:owner/:name/issues/:number/commentsnow sends a weaketag,cache-control: private, no-cacheandvary: authorization, cookie. A repeat holding the tag gets an empty 304 with the same tag. The tag is computed from the exact JSON sent, so eachlimit,afterandcursorpage has its own, and it changes when a comment is added or edited.I ran
bun x tsc --noEmit,test/issues-etag.test.ts(5 tests),test/contract.test.tsandtest/openapi-coverage.test.ts; all pass.bun scripts/gate.tsprinted nothing, so I don't know whether it passed. I did not run the full suite; the forge's check does that on push.tagged(request, text)helper insrc/http/api/issues.tsbuilds the response. The issues list (#9117) now uses it too, so the two routes stay consistent.If-None-Matchthey send, including*, so a 304 never confirms a thread to someone who may not read it.etagheader and the 304.test/issues-etag.test.tsasks twice (200, then 304), posts a comment, and gets 200 with a new tag. It also checks that alimitpage has a different tag and that the sealed thread stays refused to a stranger.Left out: the single-issue route (
GET …/issues/:number) is not tagged.Closes #9130