Skip to content

Add bucketId to card column color and on-hold paths#291

Merged
jeremy merged 3 commits intomainfrom
fix/card-column-bucket-scoped-paths
Apr 29, 2026
Merged

Add bucketId to card column color and on-hold paths#291
jeremy merged 3 commits intomainfrom
fix/card-column-bucket-scoped-paths

Conversation

@packagethief
Copy link
Copy Markdown
Member

Summary

Three card column operations had bucket-less URIs in spec/basecamp.smithy, so the generated clients in all six SDKs built paths like /{accountId}/card_tables/columns/{columnId}/color.json. Basecamp's API rejects those with 404 — the endpoints have always required /buckets/{bucketId} in the path. Every consumer of SetCardColumnColor, EnableCardColumnOnHold, and DisableCardColumnOnHold has been broken since these operations were added.

Reported via Basecamp card 9825845889 (Helpscout case from a Jack Henry customer). The basecamp-cli has a stopgap workaround at basecamp/basecamp-cli#455 — once this PR ships and CLI bumps to the new SDK, that workaround will be reverted.

Reproduction (verified live)

  • PUT /3642500/card_tables/columns/7103332776/color.json404 Not Found
  • PUT /3642500/buckets/36395297/card_tables/columns/7103332776/color.json200 OK

Spec change

Added @httpLabel bucketId: ProjectId to all three operation inputs and rewrote each @http URI to include /buckets/{bucketId}/, matching the pattern used by other bucket-scoped operations (ListWebhooks, etc.).

GetCardColumn and UpdateCardColumn paths are not changed in this PR. Those happen to work without the bucket today (Basecamp's API tolerates bucket-less reads on these specific endpoints), and changing them would be a larger breaking change with no functional benefit. Worth a follow-up to re-align them with the spec for consistency.

Breaking change

Go SDK: SetColor, EnableOnHold, DisableOnHold now take a bucketID int64 parameter before columnID. TypeScript / Ruby / Python / Swift / Kotlin generated method signatures gain a bucketId parameter. Since the previous signatures couldn't ever produce a successful API call, no currently-working caller is affected.

Regeneration

Ran the full pipeline:

  • make smithy-build (Smithy → openapi.json)
  • make -C go generate && make url-routes
  • make ts-generate ts-generate-services
  • make rb-generate rb-generate-services
  • make swift-generate
  • make kt-generate-services
  • make py-generate

Hand-written updates:

  • go/pkg/basecamp/cards.goSetColor / EnableOnHold / DisableOnHold wrappers take bucketID and pass it through.
  • typescript/tests/services/card-columns.test.ts — call sites and MSW stubs updated.
  • ruby/test/basecamp/services/card_columns_service_test.rb — call sites and WebMock stubs updated.
  • No Python / Swift / Kotlin tests reference these methods today.

Test plan

  • make smithy-check — spec round-trips cleanly.
  • make go-check-drift / kt-check-drift — no service-layer drift.
  • make ts-check — 616 tests passed.
  • make rb-check — 599 runs / 905 assertions passed; rubocop clean.
  • make py-check — 209 tests passed; mypy + ruff clean.
  • make kt-check / swift-check — pass.
  • make conformance — 64 passed, 0 failed.
  • cd go && make test — pass. (make go-check skipped: golangci-lint not installed locally; CI will exercise it.)
  • CI green.

Follow-ups (not blocking this PR)

  1. Revert the basecamp-cli workaround in Fix cards column color 404 from missing bucket in URL basecamp-cli#455 after this ships and the CLI bumps the SDK.
  2. Consider aligning GetCardColumn and UpdateCardColumn paths to be bucket-scoped for spec consistency.

🤖 Generated with Claude Code

The Smithy spec for SetCardColumnColor, EnableCardColumnOnHold, and
DisableCardColumnOnHold was missing the {bucketId} URI label and input
field, so the generated clients in all six SDKs built bucket-less paths
like /{accountId}/card_tables/columns/{columnId}/color.json. Basecamp's
API rejects those forms with 404 — the endpoints have always required
the /buckets/{bucketId} segment. Every consumer of these three SDK
methods has been broken since the operations were added.

Reproduced with the Go SDK / basecamp-cli:

  - PUT /{account}/card_tables/columns/{column}/color.json    -> 404
  - PUT /{account}/buckets/{bucket}/card_tables/columns/{column}/color.json -> 200

Spec change: added @httpLabel bucketId: ProjectId to all three operation
inputs and rewrote each @http URI to /{accountId}/buckets/{bucketId}/...
to match the rest of the bucket-scoped operations. Get/Update card
column paths are NOT changed in this PR — they happen to work without
the bucket today, and changing them would be a larger breaking change.
We can revisit those for spec parity in a follow-up.

Regen pipeline: make smithy-build, make -C go generate, make url-routes,
make ts-generate(+services), make rb-generate(+services), make
swift-generate, make kt-generate-services, make py-generate.

Hand-written changes:
  - go/pkg/basecamp/cards.go: SetColor / EnableOnHold / DisableOnHold
    wrappers now take bucketID alongside columnID and pass it through
    to the generated client (breaking change to public Go API).
  - typescript/tests/services/card-columns.test.ts: stubs and call
    sites updated to include bucketId.
  - ruby/test/basecamp/services/card_columns_service_test.rb: stubs and
    call sites updated to include bucket_id:.

Verification: make smithy-check, go-check-drift, kt-check-drift,
ts-check, rb-check, py-check, kt-check, swift-check, conformance all
green. (go-check skipped — golangci-lint not installed locally; the
underlying go test passes.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 28, 2026 21:47
@github-actions github-actions Bot added typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK go kotlin swift spec Changes to the Smithy spec or OpenAPI labels Apr 28, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 28, 2026

Spec Change Impact

  • Modified operations/types:

    • Added bucketId to card.column.color and card.on-hold.paths.
  • SDKs needing regeneration:

    • All SDKs need regeneration to incorporate the changes.
  • Breaking API change:

    • No, this is not a breaking change since no operations or fields were removed.
  • SDKs needing updates:

    • Go
    • TypeScript
    • Ruby
    • Kotlin
    • Swift

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 21 files

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the Smithy spec and regenerated SDKs for three Card Column operations whose URIs were missing the required /buckets/{bucketId} segment, which previously caused generated clients to call non-existent endpoints (404s).

Changes:

  • Updated SetCardColumnColor, EnableCardColumnOnHold, and DisableCardColumnOnHold in spec/basecamp.smithy to add bucketId as an @httpLabel and include it in the @http URI.
  • Regenerated OpenAPI artifacts and all SDK service clients to use the new bucket-scoped paths and updated method signatures.
  • Updated TypeScript and Ruby tests, plus Go hand-written wrappers, to pass bucketId.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Reviewed changes

Copilot reviewed 7 out of 21 changed files in this pull request and generated no comments.

Show a summary per file
File Description
spec/basecamp.smithy Adds bucketId labels and bucket-scoped URIs for the affected operations.
openapi.json Regenerated OpenAPI containing updated bucket-scoped paths.
typescript/src/generated/openapi-stripped.json Regenerated TS OpenAPI input with the new bucket-scoped paths.
typescript/src/generated/schema.d.ts Regenerated TS type definitions reflecting new paths/params.
typescript/src/generated/path-mapping.ts Updates operation mapping keys to include bucket-scoped routes.
typescript/src/generated/services/card-columns.ts Updates generated TS service methods to accept bucketId and call bucket-scoped endpoints.
typescript/src/generated/services/index.ts Export ordering updated due to regeneration.
typescript/src/generated/metadata.json Regenerated TS operation metadata.
typescript/tests/services/card-columns.test.ts Updates test calls and MSW stubs to include /buckets/{bucketId} and pass bucketId.
ruby/lib/basecamp/generated/services/card_columns_service.rb Updates generated Ruby service methods to require bucket_id and use bucket-scoped endpoints.
ruby/lib/basecamp/generated/types.rb Regenerated Ruby types header timestamp.
ruby/lib/basecamp/generated/metadata.json Regenerated Ruby operation metadata.
ruby/test/basecamp/services/card_columns_service_test.rb Updates Ruby test stubs and call sites to pass bucket_id.
python/src/basecamp/generated/services/card_columns.py Updates generated Python service signatures/paths to include bucket_id for the affected operations.
swift/Sources/Basecamp/Generated/Services/CardColumnsService.swift Updates generated Swift methods to accept bucketId and use bucket-scoped endpoints.
kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/card-columns.kt Updates generated Kotlin methods to accept bucketId and use bucket-scoped endpoints.
kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt Moves/retains generated Kotlin request body types due to regeneration.
go/pkg/generated/client.gen.go Updates generated Go OpenAPI client interface + request builders for new bucket-scoped endpoints.
go/pkg/basecamp/cards.go Updates Go hand-written CardColumnsService wrappers to accept/pass bucketID.
go/pkg/basecamp/url-routes.json Regenerated route table to include bucket-scoped card column routes.
go.work.sum Updates Go workspace sums due to regeneration/tooling changes.
Comments suppressed due to low confidence (2)

spec/basecamp.smithy:4285

  • SetCardColumnColor is configured with @basecampRetry(... retryOn: [429, 503]), but its errors: list omits RateLimitError. That causes generated OpenAPI/SDK types to omit the 429 response shape even though clients may retry on 429. Add RateLimitError to the operation’s errors: list to keep the modeled error responses consistent with the retry contract.
operation SetCardColumnColor {
  input: SetCardColumnColorInput
  output: SetCardColumnColorOutput
  errors: [NotFoundError, ValidationError, UnauthorizedError, ForbiddenError, InternalServerError]
}

spec/basecamp.smithy:4347

  • DisableCardColumnOnHold has @basecampRetry(... retryOn: [429, 503]) but its errors: list does not include RateLimitError, so generated OpenAPI/SDKs won’t model a 429 response for this operation. Include RateLimitError in the errors: list (and regenerate) so rate-limit failures are represented consistently across clients.
@http(method: "DELETE", uri: "/{accountId}/buckets/{bucketId}/card_tables/columns/{columnId}/on_hold.json")
operation DisableCardColumnOnHold {
  input: DisableCardColumnOnHoldInput
  output: DisableCardColumnOnHoldOutput
  errors: [NotFoundError, UnauthorizedError, ForbiddenError, InternalServerError]
}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

jeremy added 2 commits April 29, 2026 11:02
Asserts method and full URL path for SetColor, EnableOnHold, and
DisableOnHold. The path assertion uses distinct bucketID and columnID
constants so a future swap of the wrapper's (bucketID, columnID)
argument order builds a path with the IDs in the wrong slots and
fails the test, rather than silently producing a 404 against the live
API like the bug this PR fixes.
EnableCardColumnOnHold already declared RateLimitError in its errors
list, but its two siblings — SetCardColumnColor and DisableCardColumnOnHold —
did not, despite all three sharing @basecampRetry(retryOn: [429, 503]).
Align the three operations.

Inserts RateLimitError between ForbiddenError and InternalServerError
to match the existing ordering convention. Regenerates the affected
artifacts: openapi.json, the Go generated client (JSON429 fields and
parse cases), and the TS schema/openapi-stripped.json. Ruby/TS metadata
and Ruby types.rb also pick up regen timestamps.

The hand-written wrappers in go/pkg/basecamp/cards.go are unchanged —
they route errors through generic checkResponse/checkError, so the new
JSON429 fields surface automatically without wrapper updates.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes broken Card Column color/on-hold operations by making them bucket-scoped (adding bucketId as an HTTP label and inserting /buckets/{bucketId} into the URI), aligning generated SDK paths with the Basecamp API’s required routing.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

Changes:

  • Update Smithy spec for SetCardColumnColor, EnableCardColumnOnHold, and DisableCardColumnOnHold to require bucketId in the path.
  • Regenerate OpenAPI + generated SDK surfaces (TS/Ruby/Python/Swift/Kotlin/Go) so client methods include bucketId and build correct URIs.
  • Update/extend tests (TypeScript, Ruby, Go) to assert the new bucket-scoped request paths.

Reviewed changes

Copilot reviewed 8 out of 22 changed files in this pull request and generated no comments.

Show a summary per file
File Description
typescript/tests/services/card-columns.test.ts Updates MSW stubs and call sites to include bucketId in paths and method signatures.
typescript/src/generated/services/index.ts Reorders exports (no functional change).
typescript/src/generated/services/card-columns.ts Updates generated service methods to use bucket-scoped routes and signatures.
typescript/src/generated/schema.d.ts Regenerates OpenAPI TypeScript types reflecting new bucket-scoped paths/params.
typescript/src/generated/path-mapping.ts Updates operationId mapping for new bucket-scoped Card Column endpoints.
typescript/src/generated/openapi-stripped.json Regenerates stripped OpenAPI with new paths for these operations.
typescript/src/generated/metadata.json Regenerates per-operation metadata (retry/idempotency/etc.) after spec change.
swift/Sources/Basecamp/Generated/Services/CardColumnsService.swift Updates generated Swift service signatures and paths to include bucketId.
spec/basecamp.smithy Adds bucketId to inputs and fixes the three @http URIs to include /buckets/{bucketId}.
ruby/test/basecamp/services/card_columns_service_test.rb Updates stubs + call sites to pass bucket_id and assert new URIs.
ruby/lib/basecamp/generated/types.rb Updates generation timestamp (regen artifact).
ruby/lib/basecamp/generated/services/card_columns_service.rb Updates generated Ruby service paths/signatures to include bucket_id.
ruby/lib/basecamp/generated/metadata.json Updates generation timestamp and operation metadata (regen artifact).
python/src/basecamp/generated/services/card_columns.py Updates generated Python service method signatures/paths to include bucket_id.
kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/card-columns.kt Updates generated Kotlin service signatures/paths to include bucketId.
kotlin/sdk/src/commonMain/kotlin/com/basecamp/sdk/generated/services/Types.kt Moves/updates generated request body type placement (regen artifact).
go/pkg/generated/client.gen.go Updates generated Go client interfaces/builders to include bucketId path label.
go/pkg/basecamp/url-routes.json Updates generated URL route table to include bucket-scoped Card Column endpoints.
go/pkg/basecamp/cards_test.go Adds Go tests asserting correct bucket-scoped paths for the wrapper methods.
go/pkg/basecamp/cards.go Updates Go wrapper method signatures to take bucketID and pass through to generated client.
openapi.json Regenerates OpenAPI with corrected bucket-scoped paths for these operations.
go.work.sum Updates Go workspace sums (regen/tidy artifact).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@jeremy jeremy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. Spec fix is correct: cross-checked against the BC3 Rails app — those controllers are BucketScoped and the routes live under resources :buckets, so /buckets/{bucketId}/... is the right target. Some published API doc examples show the bucket-less form, but those are stale; the Rails routes are the source of truth and reject the bucket-less form with 404 (reproduced).

Hand-written cards.go wrapper signature changes (SetColor, EnableOnHold, DisableOnHold) and the TS/Ruby test stub updates are correct. Regen is consistent across all six SDKs. The go.work.sum diff isn't unrelated churn — running go build ./... from the parent reproducibly re-adds the same 5 lines, so this PR is correcting pre-existing workspace-sum drift on main. @cubic found no issues on the original commit.

Added two fixup commits onto the branch:

  1. 9c15b67 Add CardColumns httptest coverage for bucket-scoped paths. TS and Ruby already assert URL paths in their test stub diffs; Go didn't. Adds three httptest-backed tests pinning method and full path for SetColor / EnableOnHold / DisableOnHold using distinct bucketID and columnID constants — a future swap of the wrapper's (bucketID, columnID) argument order builds the path with the IDs in the wrong slots and fails the test, rather than silently producing a 404 against the live API like the bug this PR fixes.

  2. 58c00e3 Model RateLimitError on retry-marked card column ops. Picks up @copilot's two flags. EnableCardColumnOnHold already declared RateLimitError; aligning SetCardColumnColor and DisableCardColumnOnHold so all three siblings (which share @basecampRetry(retryOn: [429, 503])) model the 429 response consistently. Spec edit + clean regen across the affected artifacts. The wider asymmetry (203 retry-marked ops vs 97 modeling RateLimitError) is a separate cleanup PR.

CI is green on head 58c00e3.

One follow-up worth filing separately: grep for these three operations in conformance/tests/*.json returns nothing, which is exactly how a wrong-path bug reaches the live API undetected. Adding conformance fixtures (templated on the recent uploads-download work in #280/#281) would close the gap for this class of regression.

@jeremy jeremy merged commit 5bef7a1 into main Apr 29, 2026
52 checks passed
@jeremy jeremy deleted the fix/card-column-bucket-scoped-paths branch April 29, 2026 19:28
@jeremy jeremy added this to the v0.8.0 milestone Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go kotlin ruby Pull requests that update the Ruby SDK spec Changes to the Smithy spec or OpenAPI swift typescript Pull requests that update TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants