diff --git a/slack_bolt/request/async_internals.py b/slack_bolt/request/async_internals.py index 3b0eca3bf..b32ccb20e 100644 --- a/slack_bolt/request/async_internals.py +++ b/slack_bolt/request/async_internals.py @@ -3,6 +3,7 @@ from slack_bolt.context.async_context import AsyncBoltContext from slack_bolt.request.internals import ( extract_enterprise_id, + extract_is_enterprise_install, extract_team_id, extract_user_id, extract_channel_id, @@ -14,6 +15,7 @@ def build_async_context( context: AsyncBoltContext, body: Dict[str, Any], ) -> AsyncBoltContext: + context["is_enterprise_install"] = extract_is_enterprise_install(body) enterprise_id = extract_enterprise_id(body) if enterprise_id: context["enterprise_id"] = enterprise_id diff --git a/slack_bolt/request/internals.py b/slack_bolt/request/internals.py index b520cb821..31c2bc0f0 100644 --- a/slack_bolt/request/internals.py +++ b/slack_bolt/request/internals.py @@ -47,6 +47,10 @@ def parse_body(body: str, content_type: Optional[str]) -> Dict[str, Any]: def extract_is_enterprise_install(payload: Dict[str, Any]) -> Optional[bool]: + if payload.get("authorizations") is not None and len(payload["authorizations"]) > 0: + # To make Events API handling functioning also for shared channels, + # we should use .authorizations[0].is_enterprise_install over .is_enterprise_install + return extract_is_enterprise_install(payload["authorizations"][0]) if "is_enterprise_install" in payload: is_enterprise_install = payload.get("is_enterprise_install") return is_enterprise_install is not None and ( diff --git a/tests/scenario_tests/test_events.py b/tests/scenario_tests/test_events.py index 49c1f409c..21dd48cd8 100644 --- a/tests/scenario_tests/test_events.py +++ b/tests/scenario_tests/test_events.py @@ -6,7 +6,7 @@ from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient -from slack_bolt import App, BoltRequest, Say +from slack_bolt import App, BoltRequest, Say, BoltContext from tests.mock_web_api_server import ( setup_mock_web_api_server, cleanup_mock_web_api_server, @@ -541,3 +541,50 @@ def handle(): app.event({"type": "message.channels"})(handle) with pytest.raises(ValueError): app.event({"type": re.compile("message\\..*")})(handle) + + def test_context_generation(self): + body = { + "token": "verification-token", + "enterprise_id": "E222", # intentionally inconsistent for testing + "team_id": "T222", # intentionally inconsistent for testing + "api_app_id": "A111", + "event": { + "type": "member_left_channel", + "user": "W111", + "channel": "C111", + "channel_type": "C", + "team": "T111", + }, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1610493715, + "authorizations": [ + { + "enterprise_id": "E333", + "user_id": "W222", + "is_bot": True, + "is_enterprise_install": True, + } + ], + "is_ext_shared_channel": False, + "event_context": "1-message-T111-G111", + } + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + process_before_response=True, + ) + + @app.event("member_left_channel") + def handle(context: BoltContext): + assert context.enterprise_id == "E333" + assert context.team_id is None + assert context.is_enterprise_install is True + assert context.user_id == "W111" + + timestamp, json_body = str(int(time())), json.dumps(body) + request: BoltRequest = BoltRequest( + body=json_body, headers=self.build_headers(timestamp, json_body) + ) + response = app.dispatch(request) + assert response.status == 200 diff --git a/tests/scenario_tests_async/test_events.py b/tests/scenario_tests_async/test_events.py index 5e5af42e2..d3e0f1f9d 100644 --- a/tests/scenario_tests_async/test_events.py +++ b/tests/scenario_tests_async/test_events.py @@ -9,6 +9,7 @@ from slack_sdk.web.async_client import AsyncWebClient from slack_bolt.app.async_app import AsyncApp +from slack_bolt.context.async_context import AsyncBoltContext from slack_bolt.context.say.async_say import AsyncSay from slack_bolt.request.async_request import AsyncBoltRequest from tests.mock_web_api_server import ( @@ -552,6 +553,54 @@ async def handle(): with pytest.raises(ValueError): app.event({"type": re.compile("message\\..*")})(handle) + @pytest.mark.asyncio + async def test_context_generation(self): + body = { + "token": "verification-token", + "enterprise_id": "E222", # intentionally inconsistent for testing + "team_id": "T222", # intentionally inconsistent for testing + "api_app_id": "A111", + "event": { + "type": "member_left_channel", + "user": "W111", + "channel": "C111", + "channel_type": "C", + "team": "T111", + }, + "type": "event_callback", + "event_id": "Ev111", + "event_time": 1610493715, + "authorizations": [ + { + "enterprise_id": "E333", + "user_id": "W222", + "is_bot": True, + "is_enterprise_install": True, + } + ], + "is_ext_shared_channel": False, + "event_context": "1-message-T111-G111", + } + app = AsyncApp( + client=self.web_client, + signing_secret=self.signing_secret, + process_before_response=True, + ) + + @app.event("member_left_channel") + async def handle(context: AsyncBoltContext): + assert context.enterprise_id == "E333" + assert context.team_id is None + assert context.is_enterprise_install is True + assert context.user_id == "W111" + + timestamp, json_body = str(int(time())), json.dumps(body) + request: AsyncBoltRequest = AsyncBoltRequest( + body=json_body, headers=self.build_headers(timestamp, json_body) + ) + response = await app.async_dispatch(request) + assert response.status == 200 + app_mention_body = { "token": "verification_token",