Skip to content

Commit

Permalink
Add missing listener suggestion to the default unhandled error message (
Browse files Browse the repository at this point in the history
slackapi#323)

* Add missing listener suggestion to the default unhandled error message

* Adjust the warning log message
  • Loading branch information
seratch authored May 7, 2021
1 parent 1e18c0d commit c6ec722
Show file tree
Hide file tree
Showing 6 changed files with 1,048 additions and 9 deletions.
4 changes: 2 additions & 2 deletions scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ python_version=`python --version | awk '{print $2}'`
if [[ $test_target != "" ]]
then
black slack_bolt/ tests/ && \
pytest $1
pytest -vv $1
else
if [ ${python_version:0:3} == "3.8" ]
then
# pytype's behavior can be different in older Python versions
black slack_bolt/ tests/ \
&& pytest \
&& pytest -vv \
&& pip install -e ".[adapter]" \
&& pip install -U pip setuptools wheel \
&& pip install -U pytype \
Expand Down
2 changes: 0 additions & 2 deletions slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,6 @@ def middleware_next():
def _handle_unmatched_requests(
self, req: BoltRequest, resp: BoltResponse
) -> BoltResponse:
# TODO: provide more info like suggestion of listeners
# e.g., You can handle this type of message with @app.event("app_mention")
self._framework_logger.warning(warning_unhandled_request(req))
return resp

Expand Down
2 changes: 0 additions & 2 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,8 +586,6 @@ async def async_middleware_next():
def _handle_unmatched_requests(
self, req: AsyncBoltRequest, resp: BoltResponse
) -> BoltResponse:
# TODO: provide more info like suggestion of listeners
# e.g., You can handle this type of message with @app.event("app_mention")
self._framework_logger.warning(warning_unhandled_request(req))
return resp

Expand Down
191 changes: 188 additions & 3 deletions slack_bolt/logger/messages.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import time
from typing import Union
from typing import Union, Dict, Any, Optional

from slack_sdk.web import SlackResponse

from slack_bolt.request import BoltRequest

from slack_bolt.request.payload_utils import (
is_action,
is_event,
is_options,
is_shortcut,
is_slash_command,
is_view,
is_workflow_step_edit,
is_workflow_step_save,
is_workflow_step_execute,
)

# -------------------------------
# Error
Expand Down Expand Up @@ -94,10 +104,185 @@ def warning_unhandled_by_global_middleware( # type: ignore
)


_unhandled_request_suggestion_prefix = """
---
[Suggestion] You can handle this type of event with the following listener function:
"""


def _build_filtered_body(body: Optional[Dict[str, Any]]) -> dict:
if body is None:
return {}

payload_type = body.get("type")
filtered_body = {"type": payload_type}

if "view" in body:
view = body["view"]
# view_submission, view_closed, workflow_step_save
filtered_body["view"] = {
"type": view.get("type"),
"callback_id": view.get("callback_id"),
}

if payload_type == "block_actions":
# Block Kit Interactivity
actions = body.get("actions", [])
if len(actions) > 0 and actions[0] is not None:
filtered_body["block_id"] = actions[0].get("block_id")
filtered_body["action_id"] = actions[0].get("action_id")
if payload_type == "block_suggestion":
# Block Kit - external data source
filtered_body["block_id"] = body.get("block_id")
filtered_body["action_id"] = body.get("action_id")
filtered_body["value"] = body.get("value")

if payload_type == "event_callback" and "event" in body:
# Events API, workflow_step_execute
event_payload = body.get("event", {})
filtered_event = {"type": event_payload.get("type")}
if "subtype" in body["event"]:
filtered_event["subtype"] = event_payload.get("subtype")
filtered_body["event"] = filtered_event

if "command" in body:
# Slash Commands
filtered_body["command"] = body.get("command")

if payload_type in ["workflow_step_edit", "shortcut", "message_action"]:
# Workflow Steps, Global Shortcuts, Message Shortcuts
filtered_body["callback_id"] = body.get("callback_id")

if payload_type == "interactive_message":
# Actions in Attachments
filtered_body["callback_id"] = body.get("callback_id")
filtered_body["actions"] = body.get("actions")

if payload_type == "dialog_suggestion":
# Dialogs - external data source
filtered_body["callback_id"] = body.get("callback_id")
filtered_body["value"] = body.get("value")
if payload_type == "dialog_submission":
# Dialogs - clicking submit button
filtered_body["callback_id"] = body.get("callback_id")
filtered_body["submission"] = body.get("submission")
if payload_type == "dialog_cancellation":
# Dialogs - clicking cancel button
filtered_body["callback_id"] = body.get("callback_id")

return filtered_body


def _build_unhandled_request_suggestion(default_message: str, code_snippet: str):
return f"""{default_message}{_unhandled_request_suggestion_prefix}{code_snippet}"""


def warning_unhandled_request( # type: ignore
req: Union[BoltRequest, "AsyncBoltRequest"], # type: ignore
) -> str: # type: ignore
return f"Unhandled request ({req.body})"
filtered_body = _build_filtered_body(req.body)
default_message = f"Unhandled request ({filtered_body})"
if (
is_workflow_step_edit(req.body)
or is_workflow_step_save(req.body)
or is_workflow_step_execute(req.body)
):
# @app.step
callback_id = (
filtered_body.get("callback_id")
or filtered_body.get("view", {}).get("callback_id") # type: ignore
or "your-callback-id"
)
return _build_unhandled_request_suggestion(
default_message,
f"""
from slack_bolt.workflows.step import WorkflowStep
ws = WorkflowStep(
callback_id="{callback_id}",
edit=edit,
save=save,
execute=execute,
)
# Pass Step to set up listeners
app.step(ws)
""",
)
if is_action(req.body):
# @app.action
action_id_or_callback_id = req.body.get("callback_id")
if req.body.get("type") == "block_actions":
action_id_or_callback_id = req.body.get("actions")[0].get("action_id")
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.action("{action_id_or_callback_id}")
def handle_some_action(ack, body, logger):
ack()
logger.info(body)
""",
)
if is_options(req.body):
# @app.options
constraints = '"action-id"'
if req.body.get("action_id") is not None:
constraints = '"' + req.body.get("action_id") + '"'
elif req.body.get("type") == "dialog_suggestion":
constraints = f"""{{"type": "dialog_suggestion", "callback_id": "{req.body.get('callback_id')}"}}"""
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.options({constraints})
def handle_some_options(ack):
ack(options=[ ... ])
""",
)
if is_shortcut(req.body):
# @app.shortcut
id = req.body.get("action_id") or req.body.get("callback_id")
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.shortcut("{id}")
def handle_shortcuts(ack, body, logger):
ack()
logger.info(body)
""",
)
if is_view(req.body):
# @app.view
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.view("{req.body.get('view', {}).get('callback_id', 'modal-view-id')}")
def handle_view_events(ack, body, logger):
ack()
logger.info(body)
""",
)
if is_event(req.body):
# @app.event
event_type = req.body.get("event", {}).get("type")
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.event("{event_type}")
def handle_{event_type}_events(body, logger):
logger.info(body)
""",
)
if is_slash_command(req.body):
# @app.command
command = req.body.get("command", "/your-command")
return _build_unhandled_request_suggestion(
default_message,
f"""
@app.command("{command}")
def handle_some_command(ack, body, logger):
ack()
logger.info(body)
""",
)
return default_message


def warning_did_not_call_ack(listener_name: str) -> str:
Expand Down
Empty file.
Loading

0 comments on commit c6ec722

Please sign in to comment.