Repository navigation
Live outbound E2E test + partial updates for full-replace PUTs (fixes buckets.update) - #4
Merged
Merged
Conversation
…d E2E test - buckets.update: GET + merge editable fields + PUT (backend PUT is a full replace and now 400s without name); workspace_id only when passed - outputs.update, functions.update (payload), serviceConnections.update (name) and updateInput/updateOutput: same merge so omitted fields are kept - outbound.access() / requestAccess() - RequestStatus enum now uses the string values the API returns - test/integration/outbound.integration.test.ts: live end-to-end outbound flow against Webhook Bin with Standard Webhooks signature verification
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
Checks the public SDK end to end against the live API (https://my.webhookrelay.com) now that outbound webhooks are GA, and fixes what the checks turned up.
Fix:
buckets.updateis now a partial updateSince 2026-10-04,
PUT /v1/buckets/{id}replaces the whole bucket and returns400 invalid bucket: bucket name cannot be emptywhennameis missing. That brokerelay.buckets.update(id, { description })and the live testmanages buckets, ....buckets.updatenow:GETs the bucket.name,description,stream,ephemeral,static_ip,large_webhooks, andauth(type/username/password/token, merged field by field).PUTs the result.workspace_idis sent only when you pass it, because the backend moves the bucket whenever the key is present, even asnull. Read-only fields (id,inputs,outputs,cron_id, ...) are never echoed back. Also added: camelCase aliasesstaticIp,largeWebhooksandworkspaceId, plusworkspace_idonBucket. The JSDoc and README explain the behaviour.Other full-replace update methods (checked against the backend handlers)
inputs.updateoutputs.updatedestinationgives a 400, other omitted fields get clearedfunctions.updatepayloadwould empty the codepayloadfrom the current functionserviceConnections.updatenameand rejects an empty oneserviceConnections.updateInput/updateOutputoutbound.endpoints.updateurlrequired)outbound.consumers.upsert/eventTypes.upsertOther SDK changes
relay.outbound.access()(GET /v1/outbound/access) andrelay.outbound.requestAccess(), plus theOutboundAccesstype withlimits.RequestStatuswas a numeric enum (Sent = 1), but the API (and the swagger) send lowercase strings ("sent"), so comparing against the enum never matched. It now uses the string values, andWebhookLog.statusis typedRequestStatus | "sent" | ....New live test:
test/integration/outbound.integration.test.tsIt uses the same credential gating as
api.integration.test.ts, andvitest.integration.config.tspicks it up. Steps:outbound.access()must be enabled. If not, it fails withoutbound not enabled for the CI account (needs Basic+ plan).whsec_+ base64 (≥ 24 bytes), andrevealSecretreturns the same secret.endpoint_ids); same key + same body gives the same message id; same key + different body gives 409. A sleep then confirms the bin got exactly one delivery.webhook-id/webhook-timestamp/webhook-signature, with the Standard Webhooks signature verified locally (HMAC-SHA256 overid.timestamp.body); body equals the payload; thewebhook-event-type,webhook-consumerand custom headers are present.messages.getis polled until the delivery issent.endpoints.deliveriesis checked with the status/event_type/message_id filters, including filters that should match nothing.messages.listis filtered by consumer and event type.whsec_+ base64(32 random bytes). Checks: create and reveal both return it; the fan-out reaches both endpoints; the delivery verifies with the imported secret and not with endpoint 1's.whsec_shortis rejected with 400, and neither the error body nor the message contains it.outbound.health(),endpoints.listAll({ state: "failing" })andlistAll({ consumer })(stats present).endpoints.retrycreates a recovery task, which is polled untilcompleted; the bin then has a second signed delivery of the same message.rotateSecret: reveal returns the new secret, andprevious_secret_expires_atis in the future.afterAllcleans up endpoints, then the consumer, then the event type, even when a test fails.Secret assertions compare booleans, so a failure can't print a secret. Nothing logs secrets or the API key.
api.integration.test.tsnow also checks that partial bucket, output and function updates keep the fields you didn't pass.Test plan
npm run typecheck && npm test && npm run buildpass locally (84 unit tests)integrationjob (live API,RELAY_API_KEY) is green🤖 Generated with Claude Code
Note
Medium Risk
Update methods now perform extra GETs and merge-then-PUT, which can overwrite concurrent edits and changes default RequestStatus enum values (breaking for numeric comparisons). Bucket workspace moves only when
workspace_idis passed, reducing accidental moves.Overview
Fixes partial
updatebehavior for resources whose API uses full-replacePUTs: the SDK now GETs current state, merges only the fields you pass (via sharedmergeForReplace/ bucket-specific merge logic), and sends a complete body so omitted fields are not cleared.buckets.updateis the main fix (partial description updates no longer fail withoutname); the same pattern applies tooutputs.update,functions.update(including preservingpayloadon rename), and service connectionupdate/updateInput/updateOutput, withworkspace_idsent only when explicitly set and new bucket camelCase aliases.Adds
relay.outbound.access()andrelay.outbound.requestAccess()withOutboundAccesstyping; README now points ataccess()instead of a pilot feature flag.RequestStatusenum values change from numbers to lowercase API strings ("sent", etc.) so comparisons against webhook log status work.Adds a live outbound E2E integration test (Webhook Bins, Standard Webhooks verification, idempotency, secret import/rotation, recovery) plus focused unit tests in
test/updates.test.tsand stronger live checks for partial bucket/output/function updates.Reviewed by Cursor Bugbot for commit c02e1d5. Configure here.