feat(webhooks): registry + delivery (slice 09)#128
Conversation
One WebhookRegistry is now the single source of truth consumed by BOTH the REST CRUD routes and the orchestrator's fan-out emitter, closing the gap where registered webhooks never received any events. - New webhooks/registry.py: flock-serialised read-modify-write transactions + shared atomic_write_json (durable, crash-safe), built on WebhookConfig; corruption-tolerant lock-free reads - routes_webhooks.py: list/create/get/update/delete/test persist through the registry; mutations are atomic (concurrent POST no longer loses one, crash mid-save no longer truncates the file), typed conflict/not-found - WebhookEmitter: fans each event out to every registered webhook filtered by should_send_event, alongside the CLI --webhook-url client - Tests: registry unit suite + emitter fan-out routing Delivery semantics (retry, non-blocking) remain for the next task. Co-Authored-By: Claude <noreply@anthropic.com>
client.py: - Retry loop bound attempt <= max_retries (1 initial try + N retries, matching DEFAULT_MAX_RETRIES intent); back off only when another attempt actually remains. - Unify send/send_sync onto shared _success/_http_error/_exhausted result + _retryable_status_error/_log_retry/_backoff_delay helpers. events.py: - Base WebhookEvent.event_type is now required (field init=False, no default); __post_init__ raises TypeError if unset instead of silently mislabeling the abstract base as task.started. orchestrator.py: - Emit via a background single-worker queue so a slow/dead endpoint can never block the loop; add _drain_webhooks() (flush + stop worker), called in run()'s finally and the max-sessions early return so the terminal run.completed delivery isn't dropped under the daemon worker. tests: - Opt into synchronous delivery where delivery is asserted immediately; add TestWebhookEmitterAsyncDelivery covering off-thread dispatch, flush/close/drain, inline-after-close fallback, and run() drain. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bhooksConfig
- Add has_updates() validation to PUT /webhooks/{webhook_id}
- Returns 400 if empty body (no fields provided)
- Prevents silent no-op updates
- Delete WebhooksConfig class (unused, WebhookRegistry is the canonical implementation)
- Remove from webhooks/config.py, webhooks/__init__.py
- Remove from test_config.py (TestWebhooksConfig class)
- Remove unused Iterator import
- Add test_update_webhook_no_updates to verify 400 response
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…n-blocking - tests/webhooks/test_emitter.py (NEW): registered webhook receives run.started via synchronous fan-out; emit() returns < 0.5 s with a blocked delivery endpoint (background-worker timing assertion) - tests/webhooks/test_registry.py: TestTransaction.test_kill_mid_save_* patches atomic_write_json to raise OSError and verifies the original webhooks.json is intact after the failed rename - tests/api/test_routes_webhooks.py: TestCreateWebhook.test_concurrent_* sends two POST /webhooks requests from a ThreadPoolExecutor and asserts both 201 and both URLs in the registry; also fixes pre-existing mypy var-annotated error on update_data in test_update_webhook_no_updates Co-Authored-By: Claude <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 56 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (14)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Summary
WebhookRegistryconsumed by CRUD routes + orchestrator fan-out (registered webhooks now actually receive events)emit()is non-blocking via background workerapi/routes_webhooks.py: callhas_updates()on PUT empty body (400); wired/deleted unusedWebhooksConfigrun.starteddelivery),max_retries=0→1 attempt (already intest_client.py), concurrent creates survive flock serialisation, kill-mid-save atomic write guarantee, emit non-blocking timing assertionTest plan
tests/webhooks/test_emitter.py— 5 new tests covering fan-out and non-blocking emittests/webhooks/test_registry.py—test_kill_mid_save_leaves_original_intactadded toTestTransactiontests/api/test_routes_webhooks.py—test_concurrent_creates_both_survive+ pre-existing mypy fix🤖 Generated with Claude Code