Add slack button to show log report (#4641)
# What this PR does Add a button to show an alert group log report in Slack. After deployed we can remove it from the thread as requested in https://github.com/grafana/oncall/issues/3849 <img width="534" alt="Screenshot 2024-07-09 at 11 29 48 PM" src="https://github.com/grafana/oncall/assets/2262529/a17cd366-e97b-4e61-bf06-172fb4737d56"> <img width="673" alt="Screenshot 2024-07-09 at 11 29 57 PM" src="https://github.com/grafana/oncall/assets/2262529/a67dbe49-1972-45e6-a8b1-ce03ffe6951b"> ## Which issue(s) this PR closes Closes [issue link here] <!-- *Note*: if you have more than one GitHub issue that this PR closes, be sure to preface each issue link with a [closing keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue). This ensures that the issue(s) are auto-closed once the PR has been merged. --> ## Checklist - [ ] Unit, integration, and e2e (if applicable) tests updated - [ ] Documentation added (or `pr:no public docs` PR label added if not required) - [ ] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
This commit is contained in:
parent
324b0c4286
commit
deff52df07
4 changed files with 101 additions and 13 deletions
|
|
@ -250,6 +250,10 @@ class AlertGroupSlackRenderer(AlertGroupBaseRenderer):
|
|||
"action_id": ScenarioStep.get_step("declare_incident", "DeclareIncidentStep").routing_uid(),
|
||||
}
|
||||
|
||||
show_timeline_button = _make_button(
|
||||
":blue_book: Show Timeline", "OpenAlertGroupTimelineDialogStep", "alertgroup_timeline"
|
||||
)
|
||||
|
||||
buttons = []
|
||||
if not alert_group.is_maintenance_incident:
|
||||
if not alert_group.resolved:
|
||||
|
|
@ -282,6 +286,8 @@ class AlertGroupSlackRenderer(AlertGroupBaseRenderer):
|
|||
if not alert_group.resolved:
|
||||
buttons.append(resolve_button)
|
||||
|
||||
buttons.append(show_timeline_button)
|
||||
|
||||
return [{"type": "actions", "elements": buttons}]
|
||||
|
||||
def _get_invitation_attachment(self):
|
||||
|
|
|
|||
78
engine/apps/slack/scenarios/alertgroup_timeline.py
Normal file
78
engine/apps/slack/scenarios/alertgroup_timeline.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import typing
|
||||
|
||||
from apps.api.permissions import RBACPermission
|
||||
from apps.slack.chatops_proxy_routing import make_private_metadata
|
||||
from apps.slack.scenarios import scenario_step
|
||||
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
|
||||
from apps.slack.types import (
|
||||
Block,
|
||||
BlockActionType,
|
||||
EventPayload,
|
||||
InteractiveMessageActionType,
|
||||
ModalView,
|
||||
PayloadType,
|
||||
ScenarioRoute,
|
||||
)
|
||||
|
||||
from .step_mixins import AlertGroupActionsMixin
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
|
||||
|
||||
class OpenAlertGroupTimelineDialogStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
||||
REQUIRED_PERMISSIONS = [RBACPermission.Permissions.CHATOPS_WRITE]
|
||||
|
||||
def process_scenario(
|
||||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
self.open_unauthorized_warning(payload)
|
||||
return
|
||||
|
||||
private_metadata = {
|
||||
"organization_id": self.organization.pk,
|
||||
"alert_group_pk": alert_group.pk,
|
||||
"message_ts": payload.get("message_ts") or payload["container"]["message_ts"],
|
||||
}
|
||||
|
||||
alert_receive_channel = alert_group.channel
|
||||
past_log_report = AlertGroupLogSlackRenderer.render_alert_group_past_log_report_text(alert_group)
|
||||
future_log_report = AlertGroupLogSlackRenderer.render_alert_group_future_log_report_text(alert_group)
|
||||
blocks: typing.List[Block.Section] = []
|
||||
if past_log_report:
|
||||
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": past_log_report}})
|
||||
if future_log_report:
|
||||
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": future_log_report}})
|
||||
|
||||
view: ModalView = {
|
||||
"blocks": blocks,
|
||||
"type": "modal",
|
||||
"title": {
|
||||
"type": "plain_text",
|
||||
"text": "Alert group log",
|
||||
},
|
||||
"private_metadata": make_private_metadata(private_metadata, alert_receive_channel.organization),
|
||||
}
|
||||
|
||||
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
|
||||
|
||||
|
||||
STEPS_ROUTING: ScenarioRoute.RoutingSteps = [
|
||||
{
|
||||
"payload_type": PayloadType.INTERACTIVE_MESSAGE,
|
||||
"action_type": InteractiveMessageActionType.BUTTON,
|
||||
"action_name": OpenAlertGroupTimelineDialogStep.routing_uid(),
|
||||
"step": OpenAlertGroupTimelineDialogStep,
|
||||
},
|
||||
{
|
||||
"payload_type": PayloadType.BLOCK_ACTIONS,
|
||||
"block_action_type": BlockActionType.BUTTON,
|
||||
"block_action_id": OpenAlertGroupTimelineDialogStep.routing_uid(),
|
||||
"step": OpenAlertGroupTimelineDialogStep,
|
||||
},
|
||||
]
|
||||
|
|
@ -10,16 +10,13 @@ if typing.TYPE_CHECKING:
|
|||
|
||||
class AlertGroupLogSlackRenderer:
|
||||
@staticmethod
|
||||
def render_incident_log_report_for_slack(alert_group: "AlertGroup"):
|
||||
def render_alert_group_past_log_report_text(alert_group: "AlertGroup"):
|
||||
from apps.alerts.models import AlertGroupLogRecord
|
||||
from apps.base.models import UserNotificationPolicyLogRecord
|
||||
|
||||
log_builder = IncidentLogBuilder(alert_group)
|
||||
all_log_records = log_builder.get_log_records_list()
|
||||
|
||||
attachments = []
|
||||
|
||||
# get rendered logs
|
||||
result = ""
|
||||
for log_record in all_log_records: # list of AlertGroupLogRecord and UserNotificationPolicyLogRecord logs
|
||||
if type(log_record) is AlertGroupLogRecord:
|
||||
|
|
@ -27,14 +24,12 @@ class AlertGroupLogSlackRenderer:
|
|||
elif type(log_record) is UserNotificationPolicyLogRecord:
|
||||
result += f"{log_record.rendered_notification_log_line(for_slack=True)}\n"
|
||||
|
||||
attachments.append(
|
||||
{
|
||||
"text": result,
|
||||
}
|
||||
)
|
||||
result = ""
|
||||
return result
|
||||
|
||||
# check if escalation or invitation active
|
||||
@staticmethod
|
||||
def render_alert_group_future_log_report_text(alert_group: "AlertGroup"):
|
||||
log_builder = IncidentLogBuilder(alert_group)
|
||||
result = ""
|
||||
if not (alert_group.resolved or alert_group.wiped_at or alert_group.root_alert_group):
|
||||
escalation_policies_plan = log_builder.get_incident_escalation_plan(for_slack=True)
|
||||
if escalation_policies_plan:
|
||||
|
|
@ -43,11 +38,18 @@ class AlertGroupLogSlackRenderer:
|
|||
for time in sorted(escalation_policies_plan):
|
||||
for plan_line in escalation_policies_plan[time]:
|
||||
result += f"*{humanize.naturaldelta(time)}:* {plan_line}\n"
|
||||
return result
|
||||
|
||||
if len(result) > 0:
|
||||
@staticmethod
|
||||
def render_incident_log_report_for_slack(alert_group: "AlertGroup"):
|
||||
attachments = []
|
||||
past = AlertGroupLogSlackRenderer.render_alert_group_past_log_report_text(alert_group)
|
||||
future = AlertGroupLogSlackRenderer.render_alert_group_future_log_report_text(alert_group)
|
||||
text = past + future
|
||||
if len(text) > 0:
|
||||
attachments.append(
|
||||
{
|
||||
"text": result,
|
||||
"text": text,
|
||||
}
|
||||
)
|
||||
return attachments
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from apps.chatops_proxy.utils import uninstall_slack as uninstall_slack_from_cha
|
|||
from apps.slack.client import SlackClient
|
||||
from apps.slack.errors import SlackAPIError
|
||||
from apps.slack.scenarios.alertgroup_appearance import STEPS_ROUTING as ALERTGROUP_APPEARANCE_ROUTING
|
||||
from apps.slack.scenarios.alertgroup_timeline import STEPS_ROUTING as ALERTGROUP_TIMELINE_ROUTING
|
||||
|
||||
# Importing routes from scenarios
|
||||
from apps.slack.scenarios.declare_incident import STEPS_ROUTING as DECLARE_INCIDENT_ROUTING
|
||||
|
|
@ -52,6 +53,7 @@ SCENARIOS_ROUTES.extend(SCHEDULES_ROUTING)
|
|||
SCENARIOS_ROUTES.extend(SHIFT_SWAP_REQUESTS_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(SLACK_CHANNEL_INTEGRATION_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(ALERTGROUP_APPEARANCE_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(ALERTGROUP_TIMELINE_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(RESOLUTION_NOTE_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(SLACK_USERGROUP_UPDATE_ROUTING)
|
||||
SCENARIOS_ROUTES.extend(CHANNEL_ROUTING)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue