diff --git a/engine/apps/slack/scenarios/paging.py b/engine/apps/slack/scenarios/paging.py index ee38dc36..72219b23 100644 --- a/engine/apps/slack/scenarios/paging.py +++ b/engine/apps/slack/scenarios/paging.py @@ -15,6 +15,7 @@ from apps.slack.chatops_proxy_routing import make_private_metadata, make_value from apps.slack.constants import DIVIDER, PRIVATE_METADATA_MAX_LENGTH from apps.slack.errors import SlackAPIChannelNotFoundError from apps.slack.scenarios import scenario_step +from apps.slack.slash_command import SlashCommand from apps.slack.types import ( Block, BlockActionType, @@ -115,7 +116,13 @@ def get_current_items( class StartDirectPaging(scenario_step.ScenarioStep): """Handle slash command invocation and show initial dialog.""" - command_name = [settings.SLACK_DIRECT_PAGING_SLASH_COMMAND] + @staticmethod + def matcher(slash_command: SlashCommand) -> bool: + # Check if command is /escalate. It's a legacy command we keep for smooth transition. + is_legacy_command = slash_command.command == settings.SLACK_DIRECT_PAGING_SLASH_COMMAND + # Check if command is /grafana escalate. It's a new command from unified app. + is_unified_app_command = slash_command.is_grafana_command and slash_command.subcommand == "escalate" + return is_legacy_command or is_unified_app_command def process_scenario( self, @@ -995,8 +1002,8 @@ STEPS_ROUTING: ScenarioRoute.RoutingSteps = [ }, { "payload_type": PayloadType.SLASH_COMMAND, - "command_name": StartDirectPaging.command_name, "step": StartDirectPaging, + "matcher": StartDirectPaging.matcher, }, { "payload_type": PayloadType.VIEW_SUBMISSION, diff --git a/engine/apps/slack/slash_command.py b/engine/apps/slack/slash_command.py index 89131d5b..1bb349da 100644 --- a/engine/apps/slack/slash_command.py +++ b/engine/apps/slack/slash_command.py @@ -22,7 +22,8 @@ class SlashCommand: @property def subcommand(self): """ - Return first arg as subcommand + Return first arg as action subcommand: part of command which defines action + Example: /grafana escalate -> escalate """ return self.args[0] if len(self.args) > 0 else None @@ -34,3 +35,7 @@ class SlashCommand: command = payload["command"].lstrip("/") args = payload["text"].split() return SlashCommand(command, args) + + @property + def is_grafana_command(self): + return self.command in ["grafana", "grafana-dev", "grafana-ops", "grafana-prod"] diff --git a/engine/apps/slack/tests/test_slash_command.py b/engine/apps/slack/tests/test_slash_command.py index a821a8a4..ffe1bb69 100644 --- a/engine/apps/slack/tests/test_slash_command.py +++ b/engine/apps/slack/tests/test_slash_command.py @@ -14,6 +14,7 @@ def test_parse(): assert slash_command.command == "grafana" assert slash_command.args == ["escalate"] assert slash_command.subcommand == "escalate" + assert slash_command.is_grafana_command def test_parse_command_without_subcommand(): diff --git a/engine/apps/slack/types/scenario_routes.py b/engine/apps/slack/types/scenario_routes.py index 503a62ef..6458ac51 100644 --- a/engine/apps/slack/types/scenario_routes.py +++ b/engine/apps/slack/types/scenario_routes.py @@ -1,11 +1,15 @@ import typing +from apps.slack.slash_command import SlashCommand + from .common import EventType, PayloadType if typing.TYPE_CHECKING: from apps.slack.scenarios.scenario_step import ScenarioStep from apps.slack.types import BlockActionType, InteractiveMessageActionType +MatcherType = typing.Callable[[SlashCommand], bool] + class ScenarioRoute: class _Base(typing.TypedDict): @@ -32,7 +36,7 @@ class ScenarioRoute: class SlashCommandScenarioRoute(_Base): payload_type: typing.Literal[PayloadType.SLASH_COMMAND] - command_name: typing.List[str] + matcher: MatcherType class ViewSubmissionScenarioRoute(_Base): payload_type: typing.Literal[PayloadType.VIEW_SUBMISSION] diff --git a/engine/apps/slack/views.py b/engine/apps/slack/views.py index 37eb70da..1812927a 100644 --- a/engine/apps/slack/views.py +++ b/engine/apps/slack/views.py @@ -361,7 +361,7 @@ class SlackEventApiEndpointView(APIView): cmd = SlashCommand.parse(payload) # Check both command and subcommand for backward compatibility # So both /grafana escalate and /escalate will work. - if cmd.command in route["command_name"] or cmd.subcommand in route["command_name"]: + if route["matcher"](cmd): Step = route["step"] logger.info("Routing to {}".format(Step)) step = Step(slack_team_identity, organization, user)