Skip to content

Commit

Permalink
Fix #768 The client arg in a listener does not respect the singleton …
Browse files Browse the repository at this point in the history
…WebClient's retry_handlers (#769)
  • Loading branch information
seratch authored Nov 17, 2022
1 parent 48a793d commit e396b8b
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,7 @@ def _init_context(self, req: BoltRequest):
proxy=self._client.proxy,
headers=self._client.headers,
team_id=req.context.team_id,
retry_handlers=self._client.retry_handlers.copy() if self._client.retry_handlers is not None else None,
)
req.context["client"] = client_per_request

Expand Down
3 changes: 3 additions & 0 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,9 @@ def _init_context(self, req: AsyncBoltRequest):
trust_env_in_session=self._async_client.trust_env_in_session,
headers=self._async_client.headers,
team_id=req.context.team_id,
retry_handlers=self._async_client.retry_handlers.copy()
if self._async_client.retry_handlers is not None
else None,
)
req.context["client"] = client_per_request

Expand Down
110 changes: 110 additions & 0 deletions tests/scenario_tests/test_web_client_customization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import json
from time import time
from urllib.parse import quote

from slack_sdk import WebClient
from slack_sdk.http_retry import all_builtin_retry_handlers
from slack_sdk.signature import SignatureVerifier

from slack_bolt import BoltRequest
from slack_bolt.app import App
from tests.mock_web_api_server import (
setup_mock_web_api_server,
cleanup_mock_web_api_server,
assert_auth_test_count,
)
from tests.utils import remove_os_env_temporarily, restore_os_env


class TestWebClientCustomization:
valid_token = "xoxb-valid"
signing_secret = "secret"
mock_api_server_base_url = "http://localhost:8888"
signature_verifier = SignatureVerifier(signing_secret)
web_client = WebClient(
token=valid_token,
base_url=mock_api_server_base_url,
)

def setup_method(self):
self.old_os_env = remove_os_env_temporarily()
setup_mock_web_api_server(self)

def teardown_method(self):
cleanup_mock_web_api_server(self)
restore_os_env(self.old_os_env)

def generate_signature(self, body: str, timestamp: str):
return self.signature_verifier.generate_signature(
body=body,
timestamp=timestamp,
)

def build_headers(self, timestamp: str, body: str):
return {
"content-type": ["application/x-www-form-urlencoded"],
"x-slack-signature": [self.generate_signature(body, timestamp)],
"x-slack-request-timestamp": [timestamp],
}

def build_valid_request(self) -> BoltRequest:
timestamp = str(int(time()))
return BoltRequest(body=raw_body, headers=self.build_headers(timestamp, raw_body))

def test_web_client_customization(self):
self.web_client.retry_handlers = all_builtin_retry_handlers()
app = App(
client=self.web_client,
signing_secret=self.signing_secret,
)

@app.action("a")
def listener(ack, client):
assert len(client.retry_handlers) == 2
ack()

request = self.build_valid_request()
response = app.dispatch(request)
assert response.status == 200
assert response.body == ""
assert_auth_test_count(self, 1)


block_actions_body = {
"type": "block_actions",
"user": {
"id": "W99999",
"username": "primary-owner",
"name": "primary-owner",
"team_id": "T111",
},
"api_app_id": "A111",
"token": "verification_token",
"container": {
"type": "message",
"message_ts": "111.222",
"channel_id": "C111",
"is_ephemeral": True,
},
"trigger_id": "111.222.valid",
"team": {
"id": "T111",
"domain": "workspace-domain",
"enterprise_id": "E111",
"enterprise_name": "Sandbox Org",
},
"channel": {"id": "C111", "name": "test-channel"},
"response_url": "https://hooks.slack.com/actions/T111/111/random-value",
"actions": [
{
"action_id": "a",
"block_id": "b",
"text": {"type": "plain_text", "text": "Button", "emoji": True},
"value": "click_me_123",
"type": "button",
"action_ts": "1596530385.194939",
}
],
}

raw_body = f"payload={quote(json.dumps(block_actions_body))}"
120 changes: 120 additions & 0 deletions tests/scenario_tests_async/test_web_client_customization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import asyncio
import json
from time import time
from urllib.parse import quote

import pytest
from slack_sdk.web.async_client import AsyncWebClient
from slack_sdk.http_retry.builtin_async_handlers import AsyncConnectionErrorRetryHandler, AsyncRateLimitErrorRetryHandler
from slack_sdk.signature import SignatureVerifier

from slack_bolt.request.async_request import AsyncBoltRequest
from slack_bolt.async_app import AsyncApp
from tests.mock_web_api_server import (
setup_mock_web_api_server,
cleanup_mock_web_api_server,
assert_auth_test_count_async,
)
from tests.utils import remove_os_env_temporarily, restore_os_env


class TestWebClientCustomization:
valid_token = "xoxb-valid"
signing_secret = "secret"
mock_api_server_base_url = "http://localhost:8888"
signature_verifier = SignatureVerifier(signing_secret)
web_client = AsyncWebClient(
token=valid_token,
base_url=mock_api_server_base_url,
)

@pytest.fixture
def event_loop(self):
old_os_env = remove_os_env_temporarily()
try:
setup_mock_web_api_server(self)
loop = asyncio.get_event_loop()
yield loop
loop.close()
cleanup_mock_web_api_server(self)
finally:
restore_os_env(old_os_env)

def generate_signature(self, body: str, timestamp: str):
return self.signature_verifier.generate_signature(
body=body,
timestamp=timestamp,
)

def build_headers(self, timestamp: str, body: str):
return {
"content-type": ["application/x-www-form-urlencoded"],
"x-slack-signature": [self.generate_signature(body, timestamp)],
"x-slack-request-timestamp": [timestamp],
}

def build_valid_request(self) -> AsyncBoltRequest:
timestamp = str(int(time()))
return AsyncBoltRequest(body=raw_body, headers=self.build_headers(timestamp, raw_body))

@pytest.mark.asyncio
async def test_web_client_customization(self):
self.web_client.retry_handlers = [
AsyncConnectionErrorRetryHandler,
AsyncRateLimitErrorRetryHandler,
]
app = AsyncApp(
client=self.web_client,
signing_secret=self.signing_secret,
)

@app.action("a")
async def listener(ack, client):
assert len(client.retry_handlers) == 2
await ack()

request = self.build_valid_request()
response = await app.async_dispatch(request)
assert response.status == 200
assert response.body == ""
await assert_auth_test_count_async(self, 1)


block_actions_body = {
"type": "block_actions",
"user": {
"id": "W99999",
"username": "primary-owner",
"name": "primary-owner",
"team_id": "T111",
},
"api_app_id": "A111",
"token": "verification_token",
"container": {
"type": "message",
"message_ts": "111.222",
"channel_id": "C111",
"is_ephemeral": True,
},
"trigger_id": "111.222.valid",
"team": {
"id": "T111",
"domain": "workspace-domain",
"enterprise_id": "E111",
"enterprise_name": "Sandbox Org",
},
"channel": {"id": "C111", "name": "test-channel"},
"response_url": "https://hooks.slack.com/actions/T111/111/random-value",
"actions": [
{
"action_id": "a",
"block_id": "b",
"text": {"type": "plain_text", "text": "Button", "emoji": True},
"value": "click_me_123",
"type": "button",
"action_ts": "1596530385.194939",
}
],
}

raw_body = f"payload={quote(json.dumps(block_actions_body))}"

0 comments on commit e396b8b

Please sign in to comment.