Add UpdateBoost

Merged
RO
4d ago
+1614-27

Stacked on #1015 (base is circles-service, rebased onto its head 124fe18f); retarget to main once it merges.

The operation

UpdateBoost models PUT /boosts/{boostId} (bc3 #13945), which replaces a boost's content and answers 200 with the boost (same shape as Get a boost).

  • Only the boost's creator may update it. Anyone else, admins included, gets 403, as does any caller when comments are closed on the boosted recording.
  • 404 for a boost on a deleted recording or in another account.
  • 422, bodiless, for missing, blank or invalid content. It is modeled as BareValidationError, the way CreatePing's is.
  • Flat route only. The legacy bucket-scoped boost routes get no update.
  • A naturally idempotent PUT: it retries on 429 and 503 and is flagged @basecampIdempotent(natural: true).
  • content is capped at 16 in the spec (@length(max: 16), maxLength: 16 in openapi.json), matching CreateRecordingBoostInput and CreateEventBoostInput and the web's limit. It is a separate commit. No SDK enforces maxLength on a request client side, create included, so no runtime behavior changes; the server owns the rule and answers a bodiless 422.

It sits on the existing Boosts service in all seven SDKs. Go: func (s *BoostsService) Update(ctx context.Context, boostID int64, content string) (*Boost, error). Go rejects empty content with a usage error before the request, as CreateRecording does.

The repin

spec/api-provenance.json moves from b1f18fd182 to 4958288166 (bc3 master after #13945, same day, so the API version is unchanged). make bc3-routes regenerates spec/bc3-routes.json, 416 to 417 routes. The only new route is PUT /boosts/:id, which this PR models, so no api-gap is registered and the allowlist needs no change. The range triage is in spec/api-gaps/README.md, including that the same docs reword CreateBoost (the 191-character limit and "too long" 422 are gone from the text).

Tests

  • Unit tests in every SDK for success (path, method, content-only body), 403, 404 and 422: Go, TypeScript, Ruby, Python, Rust (tests/boosts.rs), Kotlin (BoostsServiceTest), Swift (BoostsServiceTests).
  • A conformance/tests/boosts_write.json fixture (flat path, wire body, 403, 404, bodiless 422) with an UpdateBoost dispatch case in all seven runners.
  • Gates: operation counts (283), the idempotency parity script (98/234), Rust route-table counts, Go grouped-client inventory, and the SPEC fixture-category rows.

make check ends "All checks passed" locally on Linux. Swift's own build and the Swift conformance runner are macOS-only here and skipped, so the Swift test and runner dispatch rest on CI.


Summary by cubic

Adds UpdateBoost to all seven SDKs, modeling PUT /boosts/{boostId}, which replaces a boost's content and answers 200 with the updated boost.

Behavior

  • Only the boost's creator may update; others get 403 and a bodiless 422 covers missing, blank, or invalid content.
  • A naturally idempotent PUT retries on 429 and 503; Go rejects empty content before sending the request.
  • Flat route only; bucket-scoped boost routes get no update.

Spec and tests

  • Caps content at 16 characters in the spec (@length(max: 16), maxLength: 16 in openapi.json), matching CreateRecordingBoostInput, CreateEventBoostInput, and the web limit. No SDK enforces maxLength client side; the server owns the rule and answers a bodiless 422.
  • Repins the bc3 provenance to 4958288166 with no API version change; the only new route is PUT /boosts/:id.
  • Adds a boosts_write.json conformance fixture with dispatch cases in all seven runners, plus per-SDK unit tests and updated operation-count, idempotency-parity, and route-table gates.
  • TypeScript exports UpdateBoostRequest; the api-gaps note now names the same 16-character cap.