Example #1
0
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)
Example #2
0
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)
Example #3
0
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
Example #4
0
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
Example #5
0
    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,
            },
        )
Example #6
0
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}")
Example #7
0
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
Example #8
0
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
Example #9
0
    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()
Example #10
0
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
Example #11
0
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
Example #12
0
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()
Example #13
0
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
Example #14
0
def slack_id_to_fullname(value):
    profile = get_user_profile(value)
    if profile:
        return profile["fullname"]