Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion slack_bolt/adapter/socket_mode/async_internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
from slack_sdk.socket_mode.request import SocketModeRequest
from slack_sdk.socket_mode.response import SocketModeResponse

from slack_bolt.adapter.socket_mode.internals import build_retry_headers
from slack_bolt.app.async_app import AsyncApp
from slack_bolt.request.async_request import AsyncBoltRequest
from slack_bolt.response import BoltResponse


async def run_async_bolt_app(app: AsyncApp, req: SocketModeRequest):
bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload)
bolt_req: AsyncBoltRequest = AsyncBoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req))
bolt_resp: BoltResponse = await app.async_dispatch(bolt_req)
return bolt_resp

Expand Down
13 changes: 12 additions & 1 deletion slack_bolt/adapter/socket_mode/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import logging
from time import time
from typing import Dict, Optional, Sequence, Union

from slack_sdk.socket_mode.client import BaseSocketModeClient
from slack_sdk.socket_mode.request import SocketModeRequest
Expand All @@ -13,8 +14,18 @@
from slack_bolt.response import BoltResponse


def build_retry_headers(req: SocketModeRequest) -> Optional[Dict[str, Union[str, Sequence[str]]]]:
# Mirror the HTTP mode retry headers so middleware/listeners can detect Events API retries
headers: Dict[str, Union[str, Sequence[str]]] = {}
if req.retry_attempt is not None:
headers["x-slack-retry-num"] = str(req.retry_attempt)
if req.retry_reason is not None:
headers["x-slack-retry-reason"] = req.retry_reason
return headers or None


def run_bolt_app(app: App, req: SocketModeRequest):
bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload)
bolt_req: BoltRequest = BoltRequest(mode="socket_mode", body=req.payload, headers=build_retry_headers(req))
bolt_resp: BoltResponse = app.dispatch(bolt_req)
return bolt_resp

Expand Down
39 changes: 39 additions & 0 deletions tests/adapter_tests/socket_mode/test_internals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from slack_sdk.socket_mode.request import SocketModeRequest

from slack_bolt.adapter.socket_mode.internals import build_retry_headers, run_bolt_app


class TestSocketModeInternals:
def test_build_retry_headers_without_retry(self):
req = SocketModeRequest(type="events_api", envelope_id="e1", payload={"type": "event_callback"})
assert build_retry_headers(req) is None

def test_build_retry_headers_with_retry(self):
req = SocketModeRequest(
type="events_api",
envelope_id="e1",
payload={"type": "event_callback"},
retry_attempt=2,
retry_reason="http_timeout",
)
headers = build_retry_headers(req)
assert headers == {"x-slack-retry-num": "2", "x-slack-retry-reason": "http_timeout"}

def test_run_bolt_app_propagates_retry_headers(self):
captured = {}

class FakeApp:
def dispatch(self, bolt_req):
captured["headers"] = bolt_req.headers
return None

req = SocketModeRequest(
type="events_api",
envelope_id="e1",
payload={"type": "event_callback", "event": {"type": "app_mention"}},
retry_attempt=1,
retry_reason="http_timeout",
)
run_bolt_app(FakeApp(), req)
assert captured["headers"]["x-slack-retry-num"] == ["1"]
assert captured["headers"]["x-slack-retry-reason"] == ["http_timeout"]