# Webhooks for external integrations. import pprint from typing import Any, Dict, Iterable, Optional import ujson from django.http import HttpRequest, HttpResponse from zerver.decorator import api_key_only_webhook_view from zerver.lib.request import REQ, has_request_variables from zerver.lib.response import json_success from zerver.lib.webhooks.common import check_send_webhook_message, \ UnexpectedWebhookEventType from zerver.models import Client, UserProfile PAGER_DUTY_EVENT_NAMES = { 'incident.trigger': 'triggered', 'incident.acknowledge': 'acknowledged', 'incident.unacknowledge': 'unacknowledged', 'incident.resolve': 'resolved', 'incident.assign': 'assigned', 'incident.escalate': 'escalated', 'incident.delegate': 'delineated', } PAGER_DUTY_EVENT_NAMES_V2 = { 'incident.trigger': 'triggered', 'incident.acknowledge': 'acknowledged', 'incident.resolve': 'resolved', 'incident.assign': 'assigned', } ASSIGNEE_TEMPLATE = '[{username}]({url})' INCIDENT_WITH_SERVICE_AND_ASSIGNEE = ( 'Incident [{incident_num}]({incident_url}) {action} by [{service_name}]' '({service_url}) (assigned to {assignee_info})\n\n``` quote\n{trigger_message}\n```' ) INCIDENT_WITH_ASSIGNEE = """ Incident [{incident_num}]({incident_url}) {action} by {assignee_info} ``` quote {trigger_message} ``` """.strip() INCIDENT_ASSIGNED = """ Incident [{incident_num}]({incident_url}) {action} to {assignee_info} ``` quote {trigger_message} ``` """.strip() INCIDENT_RESOLVED_WITH_AGENT = """ Incident [{incident_num}]({incident_url}) resolved by {resolving_agent_info} ``` quote {trigger_message} ``` """.strip() INCIDENT_RESOLVED = """ Incident [{incident_num}]({incident_url}) resolved ``` quote {trigger_message} ``` """.strip() def build_pagerduty_formatdict(message: Dict[str, Any]) -> Dict[str, Any]: format_dict = {} # type: Dict[str, Any] format_dict['action'] = PAGER_DUTY_EVENT_NAMES[message['type']] format_dict['incident_id'] = message['data']['incident']['id'] format_dict['incident_num'] = message['data']['incident']['incident_number'] format_dict['incident_url'] = message['data']['incident']['html_url'] format_dict['service_name'] = message['data']['incident']['service']['name'] format_dict['service_url'] = message['data']['incident']['service']['html_url'] if message['data']['incident'].get('assigned_to_user', None): assigned_to_user = message['data']['incident']['assigned_to_user'] format_dict['assignee_info'] = ASSIGNEE_TEMPLATE.format( username=assigned_to_user['email'].split('@')[0], url=assigned_to_user['html_url'], ) else: format_dict['assignee_info'] = 'nobody' if message['data']['incident'].get('resolved_by_user', None): resolved_by_user = message['data']['incident']['resolved_by_user'] format_dict['resolving_agent_info'] = ASSIGNEE_TEMPLATE.format( username=resolved_by_user['email'].split('@')[0], url=resolved_by_user['html_url'], ) trigger_message = [] trigger_subject = message['data']['incident']['trigger_summary_data'].get('subject', '') if trigger_subject: trigger_message.append(trigger_subject) trigger_description = message['data']['incident']['trigger_summary_data'].get('description', '') if trigger_description: trigger_message.append(trigger_description) format_dict['trigger_message'] = u'\n'.join(trigger_message) return format_dict def build_pagerduty_formatdict_v2(message: Dict[str, Any]) -> Dict[str, Any]: format_dict = {} format_dict['action'] = PAGER_DUTY_EVENT_NAMES_V2[message['event']] format_dict['incident_id'] = message['incident']['id'] format_dict['incident_num'] = message['incident']['incident_number'] format_dict['incident_url'] = message['incident']['html_url'] format_dict['service_name'] = message['incident']['service']['name'] format_dict['service_url'] = message['incident']['service']['html_url'] assignments = message['incident']['assignments'] if assignments: assignee = assignments[0]['assignee'] format_dict['assignee_info'] = ASSIGNEE_TEMPLATE.format( username=assignee['summary'], url=assignee['html_url']) else: format_dict['assignee_info'] = 'nobody' last_status_change_by = message['incident'].get('last_status_change_by') if last_status_change_by is not None: format_dict['resolving_agent_info'] = ASSIGNEE_TEMPLATE.format( username=last_status_change_by['summary'], url=last_status_change_by['html_url'], ) trigger_description = message['incident'].get('description') if trigger_description is not None: format_dict['trigger_message'] = trigger_description return format_dict def send_raw_pagerduty_json(request: HttpRequest, user_profile: UserProfile, message: Dict[str, Any]) -> None: subject = 'pagerduty' body = ( u'Unknown pagerduty message\n' u'```\n' u'%s\n' u'```') % (ujson.dumps(message, indent=2),) check_send_webhook_message(request, user_profile, subject, body) def send_formated_pagerduty(request: HttpRequest, user_profile: UserProfile, message_type: str, format_dict: Dict[str, Any]) -> None: if message_type in ('incident.trigger', 'incident.unacknowledge'): template = INCIDENT_WITH_SERVICE_AND_ASSIGNEE elif message_type == 'incident.resolve' and format_dict.get('resolving_agent_info') is not None: template = INCIDENT_RESOLVED_WITH_AGENT elif message_type == 'incident.resolve' and format_dict.get('resolving_agent_info') is None: template = INCIDENT_RESOLVED elif message_type == 'incident.assign': template = INCIDENT_ASSIGNED else: template = INCIDENT_WITH_ASSIGNEE subject = u'Incident {incident_num}'.format(**format_dict) body = template.format(**format_dict) check_send_webhook_message(request, user_profile, subject, body) @api_key_only_webhook_view('PagerDuty') @has_request_variables def api_pagerduty_webhook( request: HttpRequest, user_profile: UserProfile, payload: Dict[str, Iterable[Dict[str, Any]]]=REQ(argument_type='body'), ) -> HttpResponse: for message in payload['messages']: message_type = message.get('type') # If the message has no "type" key, then this payload came from a # Pagerduty Webhook V2. if message_type is None: break if message_type not in PAGER_DUTY_EVENT_NAMES: send_raw_pagerduty_json(request, user_profile, message) try: format_dict = build_pagerduty_formatdict(message) except Exception: send_raw_pagerduty_json(request, user_profile, message) else: send_formated_pagerduty(request, user_profile, message_type, format_dict) for message in payload['messages']: event = message.get('event') # If the message has no "event" key, then this payload came from a # Pagerduty Webhook V1. if event is None: break if event not in PAGER_DUTY_EVENT_NAMES_V2: raise UnexpectedWebhookEventType('Pagerduty', event) format_dict = build_pagerduty_formatdict_v2(message) send_formated_pagerduty(request, user_profile, event, format_dict) return json_success()