Close the If-Match gap on delete/restore/associations/permissions; make total:null explicit - #67
Merged
Merged
Conversation
…issions; make total:null explicit (#35, #57) Issue #35's spec-cited endpoint list for If-Match/ifVersion is longer than what the routes actually wired up: PATCH, undelete, and migrate honored the header, but DELETE /records/:id, POST /records/:id/restore/:version, POST /records/:id/associations[/delete], and PUT /records/:id/permissions did not, even though ScopedStack has accepted ifVersion on all of them since the 0.10.0 dependency bump. Thread the header through the remaining five handlers and pin each with a match/mismatch test. Also hardcode `total: null` at the query-response serialization boundary (#57) rather than forwarding result.total — ScopedStack.query() already guarantees null, but the issue's point is that the wire guarantee shouldn't depend on which query path produced the result. Document both the full If-Match endpoint list and the total/empty-page envelope rules in docs/api.md. #32, #34, and #36 turned out to already be fully implemented, tested, and documented by earlier work (#51, #65) — no code changes needed for those.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This started as a sweep of five open "records" issues (#32, #34, #35, #36, #57). Reading each one against the current code (post-#51/#65) showed most of the batch had already landed:
POST /recordsaccepts and validatesbody.id(charset/length/reserved-prefix → 400, duplicate → 409, timestamp-skew check), with tests (records.test.ts:748-785) and docs (docs/api.md) already in place.That left one real gap and one piece of defensive hardening, both fixed here:
PATCH,undelete, andmigratealready honoredIf-Match, but the spec's endpoint list is longer:DELETE /records/:id,POST /records/:id/restore/:version,POST /records/:id/associations,POST /records/:id/associations/delete, andPUT /records/:id/permissionsdidn't read the header at all, even thoughScopedStack(@haverstack/core0.10.0) has accepted anifVersionoption on every one of those methods since the dependency bump. The conformance-fixtures package (v0.2.0) only ships an If-Match fixture forPATCHso far, so nothing in CI was catching this. WiredIf-Match→ifVersionthrough all five remaining handlers.totalmust be null over the wire, and cursor is the only end-of-results signal #57 (totalenvelope) —ScopedStack.query()already guaranteestotal: null, and it was already forwarded correctly, but the route trustedresult.totalrather than asserting the wire guarantee explicitly. Hardcodedtotal: nullin both query handlers (GET /records,POST /records/query) so the guarantee holds independent of which query path produced the result. The versions/types bare-array envelope question this issue also raised was already answered correctly — no change needed there.Changes
src/routes/records.ts: threadIf-MatchthroughDELETE,restore/:version,associations(POST + delete), andPUT /permissions; hardcodetotal: nullin both query handlers.tests/routes/records.test.ts,tests/routes/associations.test.ts,tests/routes/versions.test.ts: match/mismatch (412 version_conflict) tests for each newly-wired endpoint.docs/api.md: document the fullIf-Matchendpoint list and thetotal/empty-page envelope rules.No code changes were needed for #34 or #36 — they're already done.
Test plan
npm run typechecknpm run lintnpm run format:checknpm test— 225/225 passing (10 new tests, no regressions)Generated by Claude Code