def report_incident(user_id: str, channel_id: str, submission: Any, response_url: str, state: Any): report = submission["report"] summary = submission["summary"] impact = submission["impact"] lead_id = submission["lead"] severity = submission["severity"] if "incident_type" in submission: report_only = submission["incident_type"] == "report" else: report_only = False name = get_user_profile(user_id)["name"] reporter, _ = ExternalUser.objects.get_or_create_slack(external_id=user_id, display_name=name) lead = None if lead_id: lead_name = get_user_profile(lead_id)["name"] lead, _ = ExternalUser.objects.get_or_create_slack( external_id=lead_id, display_name=lead_name) new_incident = Incident.objects.create_incident( report=report, reporter=reporter, report_time=datetime.now(), report_only=report_only, summary=summary, impact=impact, lead=lead, severity=severity, ) timeline_event = TimelineEvent( incident=new_incident, timestamp=new_incident.report_time, text="Incident Reported by " + reporter.full_name, event_type="metadata", ) timeline_event.save() if report_only and hasattr(settings, "INCIDENT_REPORT_CHANNEL_ID"): incidents_channel_ref = channel_reference( settings.INCIDENT_REPORT_CHANNEL_ID) else: incidents_channel_ref = channel_reference(settings.INCIDENT_CHANNEL_ID) text = f"Thanks for raising the incident 🙏" settings.SLACK_CLIENT.send_ephemeral_message(channel_id, user_id, text)
def report_incident(user_id: str, channel_id: str, submission: Any, response_url: str, state: Any): report = submission["report"] summary = submission["summary"] environment = submission["environment"] impact = submission["impact"] lead_id = submission["lead"] severity = submission["severity"] incident_platform = submission["incident_platform"] if "incident_type" in submission: report_only = submission["incident_type"] == "report" else: report_only = False name = get_user_profile(user_id)["name"] reporter, _ = ExternalUser.objects.get_or_create_slack(external_id=user_id, display_name=name) lead = None if lead_id: lead_name = get_user_profile(lead_id)["name"] lead, _ = ExternalUser.objects.get_or_create_slack( external_id=lead_id, display_name=lead_name) Incident.objects.create_incident( report=report, reporter=reporter, report_time=datetime.now(), report_only=report_only, summary=summary, incident_platform=incident_platform, environment=environment, impact=impact, lead=lead, severity=severity, ) if report_only and hasattr(settings, "INCIDENT_REPORT_CHANNEL_ID"): incidents_channel_ref = channel_reference( settings.INCIDENT_REPORT_CHANNEL_ID) else: incidents_channel_ref = channel_reference(settings.INCIDENT_CHANNEL_ID) text = ( f"Thanks for raising the incident 🙏\n\nHead over to {incidents_channel_ref} " f"to complete the report and/or help deal with the issue") settings.SLACK_CLIENT.send_ephemeral_message(channel_id, user_id, text)
def test_get_user_profile_not_in_cache(mock_slack): mock_slack.get_user_profile.return_value = { "id": "U12345678", "name": "spengler", "fullname": "Egon Spengler", "email": "*****@*****.**", "deleted": True, } # check cache is empty at start assert len(ExternalUser.objects.all()) == 0 # request a user from cache user = get_user_profile("U12345678") # check we get back the user from slack mock_slack.get_user_profile.assert_called() assert user["id"] == "U12345678" # check user is now in cache assert len(ExternalUser.objects.all()) == 1 # and that it has the right details populated cache_user = ExternalUser.objects.get(external_id="U12345678") assert cache_user.display_name == "spengler" assert cache_user.full_name == "Egon Spengler" assert cache_user.email == "*****@*****.**" assert cache_user.deleted
def test_get_user_profile_in_cache(mock_slack): # create cache entry for user slack_user = ExternalUser( external_id="U12345678", display_name="spengler", full_name="Egon Spengler", email="*****@*****.**", deleted=True, ) slack_user.save() assert len(ExternalUser.objects.all()) == 1 # request a user from cache user = get_user_profile("U12345678") # check we get back the user from cache (i.e. not call to Slack API) mock_slack.get_user_profile.assert_not_called() assert user["id"] == "U12345678" assert user["name"] == "spengler" assert user["fullname"] == "Egon Spengler" assert user["email"] == "*****@*****.**" assert user["deleted"] # check cache is unchanged assert len(ExternalUser.objects.all()) == 1
def add_pin(self, incident, message_ts, author_id, text): name = get_user_profile(author_id)["name"] author, _ = ExternalUser.objects.get_or_create_slack( external_id=author_id, display_name=name) timestamp = datetime.fromtimestamp(float(message_ts)) user_data = ExternalUserSerializer(author).data timeline_event = TimelineEvent( incident=incident, timestamp=timestamp, text=text, event_type="slack_pin", metadata={ "author": user_data, "message_ts": message_ts, "channel_id": incident.comms_channel().channel_id, }, ) timeline_event.save() PinnedMessage.objects.get_or_create( incident=incident, message_ts=message_ts, defaults={ "author": author, "text": text, "timestamp": timestamp, "timeline_event": timeline_event, }, )
def edit_incident(user_id: str, channel_id: str, submission: Any, response_url: str, state: Any): report = submission["report"] summary = submission["summary"] impact = submission["impact"] lead_id = submission["lead"] severity = submission["severity"] lead = None if lead_id: lead_name = get_user_profile(lead_id)["name"] lead, _ = ExternalUser.objects.get_or_create_slack( external_id=lead_id, display_name=lead_name) try: incident = Incident.objects.get(pk=state) if not severity and incident.severity: raise Exception("Cannot unset severity") # deliberately update in this way the post_save signal gets sent # (required for the headline post to auto update) incident.report = report incident.summary = summary incident.impact = impact incident.lead = lead incident.severity = severity incident.save() except Incident.DoesNotExist: logger.error(f"No incident found for pk {state}")
def set_action(incident: Incident, user_id: str, message: str): name = get_user_profile(user_id)["name"] action_reporter, _ = ExternalUser.objects.get_or_create_slack( external_id=user_id, display_name=name ) Action(incident=incident, details=message, user=action_reporter).save() return True, None
def set_incident_lead(incident: Incident, user_id: str, message: str): assignee = reference_to_id(message) or user_id name = get_user_profile(assignee)["name"] user, _ = ExternalUser.objects.get_or_create_slack(external_id=assignee, display_name=name) incident.lead = user incident.save() return True, None
def increment_message_count(incident, user_id): name = get_user_profile(user_id)["name"] user, _ = ExternalUser.objects.get_or_create_slack(external_id=user_id, display_name=name) user_stats, created = UserStats.objects.get_or_create( incident=incident, user=user) if created: user_stats.join_time = datetime.now() user_stats.message_count += 1 user_stats.save()
def add_status_update(incident: Incident, user_id: str, message: str): name = get_user_profile(user_id)["name"] action_reporter, _ = ExternalUser.objects.get_or_create_slack( external_id=user_id, display_name=name) StatusUpdate(incident=incident, text=message, user=action_reporter).save() msg = Message() msg.add_block( Section( block_id="update", text=Text(f":warning: *Update:*\n{message} "), )) comms_channel = CommsChannel.objects.get(incident=incident) ts = HeadlinePost.objects.get(incident=incident).message_ts msg.send(comms_channel.channel_id, None) settings.SLACK_CLIENT.send_message(settings.INCIDENT_CHANNEL_ID, ":warning: *Update*: " + message, thread_ts=ts) # comms_channel.post_in_channel(msg) return True, None
def user_ref_to_username(value): """takes a <@U123ABCD> style ref and returns an @username""" # strip the '<@' and '>' user_id = reference_to_id(value.group()) user_profile = cache.get_user_profile(user_id) return "@" + user_profile["name"] or user_id
def slash_command(request): """ Handles slash commands from slack More details here: https://api.slack.com/slash-commands Note: The order the elements are specified is the order they appear in the slack dialog @param request the request from slack containing the slash command @return: return a HTTP response to indicate the request was handled """ user_id = request.POST.get("user_id") trigger_id = request.POST.get("trigger_id") channel_id = request.POST.get("channel_id") text = request.POST.get("text").split(" ") command_name = text.pop(0) message = " ".join(text) if command_name == INCIDENT_CREATE_SLUG: name = get_user_profile(user_id)["name"] dialog = Dialog( title="Report an Incident", submit_label="Report", elements=[ Text( label="Title", name="report", placeholder="What's happened, in a sentence?", value=message, ) ], ) if hasattr(settings, "INCIDENT_REPORT_CHANNEL_ID"): dialog.add_element( SelectWithOptions( [ ("Yes - this is a live incident happening right now", "live"), ( "No - this is just a report of something that happened", "report", ), ], label="Is this a live incident?", name="incident_type", optional=False, ) ) dialog.add_element( TextArea( label="Summary", name="summary", optional=True, placeholder="Can you share any more details?", ) ) dialog.add_element( TextArea( label="Impact", name="impact", optional=True, placeholder="Who or what might be affected?", hint="Think about affected people, systems, and processes", ) ) dialog.add_element(SelectFromUsers(label="Lead", name="lead", optional=True)) dialog.add_element( SelectWithOptions( [(s.capitalize(), i) for i, s in Incident.SEVERITIES], label="Severity", name="severity", optional=True, ) ) dialog.send_open_dialog(INCIDENT_REPORT_DIALOG, trigger_id) logger.info( f"Handling Slack slash command for user {user_id}, command {command_name} - opening dialog" ) elif command_name not in get_commands(): settings.SLACK_CLIENT.send_ephemeral_message( channel_id, user_id, get_create_help(), ) else: handle_incident_command( command_name=command_name, message=message, user_id=user_id, channel_id=channel_id, thread_ts=None, ) return HttpResponse()
def report_incident(user_id: str, channel_id: str, submission: Any, response_url: str, state: Any): report = submission["report"] summary = submission["summary"] impact = submission["impact"] # lead_id = submission["lead"] severity = submission["severity"] pdschedule = submission["pdschedule"] if "incident_type" in submission: report_only = submission["incident_type"] == "report" else: report_only = False name = get_user_profile(user_id)["name"] reporter, _ = ExternalUser.objects.get_or_create_slack(external_id=user_id, display_name=name) lead = None # if lead_id: # lead_name = get_user_profile(lead_id)["name"] # lead, _ = ExternalUser.objects.get_or_create_slack( # external_id=lead_id, display_name=lead_name # ) try: pdincident = None if pdschedule == "yes": incident = settings.PDSESSION.rpost( "/incidents", json={ "incident": { "type": "incident", "title": report, "service": { "id": "P703RRY", "type": "service_reference" }, "body": { "type": "incident_body", "details": summary if summary else "", }, } }, headers={"From": "*****@*****.**"}, ) pdincident = incident["id"] Incident.objects.create_incident( report=report, reporter=reporter, report_time=datetime.now(), report_only=report_only, summary=summary, impact=impact, lead=lead, severity=severity, pdschedule=pdincident, ) if report_only and hasattr(settings, "INCIDENT_REPORT_CHANNEL_ID"): incidents_channel_ref = channel_reference( settings.INCIDENT_REPORT_CHANNEL_ID) else: incidents_channel_ref = channel_reference( settings.INCIDENT_CHANNEL_ID) text = ( f"Thanks for raising the incident 🙏\n\nHead over to {incidents_channel_ref} " f"to complete the report and/or help deal with the issue") settings.SLACK_CLIENT.send_ephemeral_message(channel_id, user_id, text) except PDClientError as pce: logger.error(pce.response.json()) # Raise here so incident doesn't get created raise pce
def slack_id_to_fullname(value): profile = get_user_profile(value) if profile: return profile["fullname"]