Commit 3923712
* feat(slack): expose web_client property on SlackAdapter (#98)
Port the Slack adapter's direct WebClient access from upstream
vercel/chat (commits 8366b8b / fdebde7 / 2f108bd, PRs #471/#476/#478).
- Add ``SlackAdapter.web_client``: a synchronous ``slack_sdk.WebClient``
bound to the current request-context token (multi-workspace) or the
configured default token (single-workspace). Token resolution uses the
existing 3-level resolver via ``_get_token()``: ContextVar token >
static ``bot_token`` config > ``AuthenticationError`` (no ``or``
fallbacks).
- Add ``_get_web_client_for_token`` mirroring upstream's
``getClientForToken`` — one cached ``WebClient`` per distinct token.
Kept separate from the async ``_client_cache`` (``AsyncWebClient``).
``slack_sdk`` import stays deferred (optional dependency, hazard #10).
- Add deprecated ``client`` property alias delegating to ``web_client``
(one-release deprecation; emits ``DeprecationWarning``).
Tests (tests/test_slack_web_client.py) mirror upstream's "webClient
getter" block: single-tenant binding + per-token caching identity,
multi-tenant ContextVar resolution under ``with_bot_token``, no-context
and unresolved-async-resolver -> ``AuthenticationError``, and the
deprecated alias returning the same object plus emitting the warning.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
* fix(slack): evict web_client cache on invalidation + clarify caching test (review)
Two review follow-ups on the web_client port:
- Gemini: `_invalidate_client` cleared only the async `_client_cache`, leaving
stale/revoked synchronous `WebClient` instances in `_web_client_cache` on
token revocation / auth-error eviction. Pop the token from both caches.
New `test_invalidate_client_clears_web_client_cache` is load-bearing.
- github-code-quality: `assert adapter.web_client is adapter.web_client`
tripped "comparison of identical values". The test is a genuine caching
check (the property is invoked twice), but binding each access to a name
makes that intent explicit and silences the false-positive.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
* fix(slack): invoke sync token resolvers in web_client / _get_token (codex review)
The sync ``_get_token`` path only handled the static-string and primed
cache cases — a sync ``bot_token`` callable (used e.g. for secret
rotation or lazy load from a sync source) raised
``AuthenticationError`` from ``web_client`` outside any webhook /
ContextVar scope until an async path had primed the cache. Proactive
sends from single-workspace apps using a sync resolver therefore
failed.
Detect the sync-resolver case via ``inspect.iscoroutinefunction``,
invoke the callable, validate the result, and prime
``_default_bot_token_cache`` with the same semantics the async
``_resolve_default_token`` path uses. Async resolvers still raise from
the sync property (cannot be awaited). Defensive check for sync
callables that *return* a coroutine (rare but real:
``lambda: some_async_fn()``) — refuse to cache the coroutine.
The two existing tests that asserted the previous deficient behavior
(sync resolver raising before resolution) are updated to assert the
new correct behavior; the cache-refresh regression test switches to an
async resolver so its sanity precondition still holds.
* fix(slack): close orphan coroutine before raising awaitable-resolver error (audit)
The defensive `inspect.isawaitable(resolved)` branch in `_get_token`'s
sync-callable handler raised AuthenticationError but never closed the
coroutine the resolver returned. Callers saw a noisy
`RuntimeWarning: coroutine was never awaited` on every triggering call.
Close the awaitable via its `close()` method (Coroutine protocol) before
raising. The existing regression test
`test_sync_callable_returning_coroutine_raises` is strengthened to capture
warnings and assert none of "never awaited" kind leaked — confirmed
load-bearing under `pytest -W error::RuntimeWarning`.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
* fix(slack): honor bot_token rotation contract in sync _get_token + invalidation
Addresses two P2 review findings on PR #127:
**Codex P2 — sync resolver rotation broken**
The previous sync-callable branch in ``_get_token`` cached the first
resolved value in ``_default_bot_token_cache`` and the cache-first
early-return prevented re-invocation, freezing rotating resolvers
(e.g., secret-manager-backed). The contract on
``SlackAdapterConfig.bot_token`` says callable resolvers are "called
on each use to support rotation."
Track ``_is_dynamic_bot_token`` at construction time. In ``_get_token``,
sync dynamic resolvers now invoke fresh on every call and never write
the process-wide cache. Static-string configs keep their cache fast
path (nothing to rotate). Async resolvers still require a webhook /
``current_token_async`` entry to be awaited.
The previously-added test
``test_sync_current_token_with_sync_resolver_invokes_resolver``
asserted the cache was primed — flipped to assert the inverse, with a
cross-reference to the dedicated rotation pin in
``test_sync_callable_invoked_fresh_each_access``.
**CodeRabbit P2 — _invalidate_client retained revoked tokens**
``_invalidate_client(token)`` evicted the WebClient and AsyncWebClient
caches but left ``_default_bot_token_cache`` / ``_resolved_default_token``
holding the revoked value, so the next ``_get_token`` returned the
same token and the adapter just rebuilt clients around it.
Now clears the resolved-token caches for dynamic-resolver configs so
the next access re-invokes the resolver. Guarded on
``_is_dynamic_bot_token`` so static-string configs retain their cache
(no refresh path — clearing would only make subsequent sync access
raise with no way to recover).
Tests: rewrote the caching test to assert rotation (resolver invoked
fresh on every access, cache stays None); added invalidation tests
covering dynamic-resolver clearing, ContextVar clearing, static-string
no-op, and token-mismatch no-op. Full suite green (4067 passed) under
``-W error::RuntimeWarning``.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
* chore(tests): address CodeRabbit duplicate-test + iter-StopIteration findings
CodeRabbit review on the latest #127 HEAD surfaced two test-quality
issues:
1. ``TestWebClientAsyncResolver.test_unresolved_async_resolver_raises``
duplicated ``TestWebClientSyncResolver.test_async_callable_in_sync_context_raises``
(same async-resolver-in-sync-context path, but the latter is stronger
— it also validates the error message wording). Removed the redundant
wrapper class to honor this repo's "no duplicate tests" CLAUDE.md
rule.
2. Rotation pin used ``tokens = iter(["xoxb-sync-1", ...])`` which would
``StopIteration`` if the test grew to a 4th access. Switched to
``f"xoxb-sync-{calls['n']}"`` so the resolver scales with call count;
existing assertions on the literal values still hold.
68 tests still pass under ``pytest -W error::RuntimeWarning``.
https://claude.ai/code/session_01FyMxQn2BEAzmwKS1GZczKj
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent ac6c6f4 commit 3923712
3 files changed
Lines changed: 625 additions & 28 deletions
File tree
- src/chat_sdk/adapters/slack
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
410 | 411 | | |
411 | 412 | | |
412 | 413 | | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
413 | 420 | | |
414 | 421 | | |
415 | 422 | | |
| |||
481 | 488 | | |
482 | 489 | | |
483 | 490 | | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
484 | 501 | | |
485 | 502 | | |
486 | 503 | | |
| |||
608 | 625 | | |
609 | 626 | | |
610 | 627 | | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
611 | 683 | | |
612 | 684 | | |
613 | 685 | | |
| |||
620 | 692 | | |
621 | 693 | | |
622 | 694 | | |
623 | | - | |
624 | | - | |
625 | | - | |
626 | | - | |
627 | | - | |
628 | | - | |
629 | | - | |
630 | | - | |
631 | | - | |
632 | | - | |
633 | | - | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
| 698 | + | |
| 699 | + | |
| 700 | + | |
| 701 | + | |
| 702 | + | |
| 703 | + | |
| 704 | + | |
| 705 | + | |
| 706 | + | |
| 707 | + | |
634 | 708 | | |
635 | 709 | | |
636 | 710 | | |
637 | 711 | | |
638 | 712 | | |
639 | 713 | | |
640 | 714 | | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
| 743 | + | |
641 | 744 | | |
642 | 745 | | |
643 | | - | |
644 | | - | |
645 | | - | |
646 | | - | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
647 | 749 | | |
648 | 750 | | |
649 | | - | |
650 | | - | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
651 | 754 | | |
652 | 755 | | |
653 | 756 | | |
| |||
758 | 861 | | |
759 | 862 | | |
760 | 863 | | |
761 | | - | |
| 864 | + | |
| 865 | + | |
| 866 | + | |
| 867 | + | |
| 868 | + | |
| 869 | + | |
| 870 | + | |
| 871 | + | |
762 | 872 | | |
| 873 | + | |
| 874 | + | |
| 875 | + | |
| 876 | + | |
| 877 | + | |
| 878 | + | |
| 879 | + | |
| 880 | + | |
| 881 | + | |
| 882 | + | |
| 883 | + | |
| 884 | + | |
| 885 | + | |
| 886 | + | |
| 887 | + | |
| 888 | + | |
| 889 | + | |
| 890 | + | |
| 891 | + | |
| 892 | + | |
| 893 | + | |
| 894 | + | |
| 895 | + | |
| 896 | + | |
| 897 | + | |
763 | 898 | | |
764 | 899 | | |
765 | 900 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
185 | | - | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
186 | 197 | | |
187 | 198 | | |
188 | 199 | | |
189 | 200 | | |
190 | 201 | | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
196 | 219 | | |
197 | 220 | | |
198 | 221 | | |
| |||
316 | 339 | | |
317 | 340 | | |
318 | 341 | | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
319 | 348 | | |
320 | 349 | | |
321 | 350 | | |
322 | 351 | | |
323 | 352 | | |
324 | 353 | | |
325 | 354 | | |
326 | | - | |
| 355 | + | |
327 | 356 | | |
328 | 357 | | |
329 | 358 | | |
330 | 359 | | |
331 | | - | |
332 | | - | |
| 360 | + | |
| 361 | + | |
333 | 362 | | |
334 | 363 | | |
335 | 364 | | |
| |||
0 commit comments