diff --git a/slack_bolt/adapter/aws_lambda/chalice_handler.py b/slack_bolt/adapter/aws_lambda/chalice_handler.py index 8d5fc9b21..f4c7220ba 100644 --- a/slack_bolt/adapter/aws_lambda/chalice_handler.py +++ b/slack_bolt/adapter/aws_lambda/chalice_handler.py @@ -1,6 +1,10 @@ import logging +import json +from os import getenv from chalice.app import Request, Response, Chalice +from chalice.config import Config +from chalice.test import BaseClient, LambdaContext, InvokeResponse from slack_bolt.adapter.aws_lambda.chalice_lazy_listener_runner import ( ChaliceLazyListenerRunner, @@ -13,14 +17,43 @@ from slack_bolt.response import BoltResponse +class LocalLambdaClient(BaseClient): + """Lambda client implementing `invoke` for use when running with Chalice CLI""" + + def __init__(self, app: Chalice, config: Config) -> None: + self._app = app + self._config = config + + def invoke( + self, + FunctionName: str = None, + InvocationType: str = "Event", + Payload: str = "{}", + ) -> InvokeResponse: + scoped = self._config.scope(self._config.chalice_stage, FunctionName) + lambda_context = LambdaContext( + FunctionName, memory_size=scoped.lambda_memory_size + ) + + with self._patched_env_vars(scoped.environment_variables): + response = self._app(json.loads(Payload), lambda_context) + return InvokeResponse(payload=response) + + class ChaliceSlackRequestHandler: def __init__(self, app: App, chalice: Chalice): # type: ignore self.app = app self.chalice = chalice self.logger = get_bolt_app_logger(app.name, ChaliceSlackRequestHandler) + + lambda_client = None + if getenv("AWS_CHALICE_CLI_MODE") == "true": + lambda_client = LocalLambdaClient(self.chalice, Config()) + self.app.listener_runner.lazy_listener_runner = ChaliceLazyListenerRunner( - logger=self.logger + logger=self.logger, lambda_client=lambda_client ) + if self.app.oauth_flow is not None: self.app.oauth_flow.settings.redirect_uri_page_renderer.install_path = "?" diff --git a/tests/adapter_tests/aws/test_aws_chalice.py b/tests/adapter_tests/aws/test_aws_chalice.py index ad9263550..7b9daad7b 100644 --- a/tests/adapter_tests/aws/test_aws_chalice.py +++ b/tests/adapter_tests/aws/test_aws_chalice.py @@ -1,12 +1,16 @@ import json +import os from time import time from typing import Dict, Any from urllib.parse import quote +from unittest import mock +import logging from chalice import Chalice, Response from chalice.app import Request from chalice.config import Config from chalice.local import LocalGateway +from chalice.test import Client from slack_sdk.signature import SignatureVerifier from slack_sdk.web import WebClient @@ -265,6 +269,59 @@ def say_it(say): assert_auth_test_count(self, 1) assert self.mock_received_requests["/chat.postMessage"] == 1 + def test_lazy_listeners_cli(self): + with mock.patch.dict(os.environ, {"AWS_CHALICE_CLI_MODE": "true"}): + assert os.environ.get("AWS_CHALICE_CLI_MODE") == "true" + app = App( + client=self.web_client, + signing_secret=self.signing_secret, + process_before_response=True + ) + + def command_handler(ack): + ack() + + def say_it(say): + say("Done!") + + app.command("/hello-world")(ack=command_handler, lazy=[say_it]) + + input = ( + "token=verification_token" + "&team_id=T111" + "&team_domain=test-domain" + "&channel_id=C111" + "&channel_name=random" + "&user_id=W111" + "&user_name=primary-owner" + "&command=%2Fhello-world" + "&text=Hi" + "&enterprise_id=E111" + "&enterprise_name=Org+Name" + "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx" + "&trigger_id=111.111.xxx" + ) + timestamp, body = str(int(time())), input + + chalice_app = Chalice(app_name="bolt-python-chalice") + slack_handler = ChaliceSlackRequestHandler(app=app, chalice=chalice_app) + + @chalice_app.route( + "/slack/events", + methods=["POST"], + content_types=["application/x-www-form-urlencoded", "application/json"], + ) + def events() -> Response: + return slack_handler.handle(chalice_app.current_request) + + headers = self.build_headers(timestamp, body) + client = Client(chalice_app, Config()) + response = client.http.post("/slack/events", headers=headers, body=body) + + assert response.status_code == 200, f"Failed request: {response.body}" + assert_auth_test_count(self, 1) + assert self.mock_received_requests["/chat.postMessage"] == 1 + def test_oauth(self): app = App( client=self.web_client,