Exemple #1
0
def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    bind = op.get_bind()
    session = sa.orm.Session(bind=bind)

    tasks = bind.execute(
        "select incident_id, creator, assignees, id from task")

    op.create_table(
        "task_assignees",
        sa.Column("participant_id", sa.Integer(), nullable=False),
        sa.Column("task_id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["participant_id"],
            ["participant.id"],
        ),
        sa.ForeignKeyConstraint(
            ["task_id"],
            ["task.id"],
        ),
        sa.PrimaryKeyConstraint("participant_id", "task_id"),
    )
    op.add_column("task", sa.Column("creator_id", sa.Integer(), nullable=True))
    op.create_foreign_key(None, "task", "participant", ["creator_id"], ["id"])

    op.drop_column("task", "creator")
    op.drop_column("task", "assignees")

    for incident_id, creator, assignees, id in tasks:
        print(f"Migrating task: {incident_id} {creator} {assignees}")
        emails = [e.strip() for e in assignees.split(",")]
        assignee_participants = []
        for e in emails:
            assignee_participants.append(
                add_participant(e, incident_id, db_session=session))

        # fetch creator email
        creator = session.query(IndividualContact).filter(
            IndividualContact.name == creator).first()

        creator_email = f"dispatch@{GOOGLE_DOMAIN}"
        if creator:
            creator_email = creator.email
        creator_participant = add_participant(creator_email,
                                              incident_id,
                                              db_session=session)

        task = task_service.get(db_session=session, task_id=id)
        task.creator = creator_participant
        task.assignees = assignee_participants
        session.add(task)
        session.commit()
Exemple #2
0
def assign_incident_role(
    db_session: SessionLocal,
    incident: Incident,
    reporter_email: str,
    role: ParticipantRoleType,
):
    """Assigns incident roles."""
    # We resolve the incident role email
    # default to reporter if we don't have an oncall plugin enabled
    assignee_email = reporter_email

    oncall_plugin = plugin_service.get_active(db_session=db_session,
                                              plugin_type="oncall")
    if not oncall_plugin:
        assignee_email = reporter_email

        # Add a new participant (duplicate participants with different roles will be updated)
        participant_flows.add_participant(
            assignee_email,
            incident.id,
            db_session,
            role,
        )
        return

    if role == ParticipantRoleType.incident_commander:
        # default to reporter
        if incident.incident_type.commander_service:
            service = incident.incident_type.commander_service
            assignee_email = oncall_plugin.instance.get(
                service_id=service.external_id)
            if incident.incident_priority.page_commander:
                oncall_plugin.instance.page(
                    service_id=service.external_id,
                    incident_name=incident.name,
                    incident_title=incident.title,
                    incident_description=incident.description,
                )
    else:
        if incident.incident_type.liaison_service:
            service = incident.incident_type.liaison_service
            assignee_email = oncall_plugin.instance.get(
                service_id=service.external_id)

    # Add a new participant (duplicate participants with different roles will be updated)
    participant_flows.add_participant(
        assignee_email,
        incident.id,
        db_session,
        role,
    )
Exemple #3
0
def incident_add_or_reactivate_participant_flow(
    user_email: str,
    incident_id: int,
    role: ParticipantRoleType = None,
    event: dict = None,
    db_session=None,
) -> Participant:
    """Runs the add or reactivate incident participant flow."""
    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email)

    if participant:
        if participant.is_active:
            log.debug(f"{user_email} is already an active participant.")
        else:
            # we reactivate the participant
            reactivated = participant_flows.reactivate_participant(
                user_email, incident_id, db_session)

            if reactivated:
                # we add the participant to the conversation
                add_participant_to_conversation(user_email, incident_id,
                                                db_session)

                # we announce the participant in the conversation
                send_incident_participant_announcement_message(
                    user_email, incident_id, db_session)

                # we send the welcome messages to the participant
                send_incident_welcome_participant_messages(
                    user_email, incident_id, db_session)
    else:
        # we add the participant to the incident
        participant = participant_flows.add_participant(user_email,
                                                        incident_id,
                                                        db_session,
                                                        role=role)

        # we add the participant to the tactical group
        add_participant_to_tactical_group(user_email, incident_id, db_session)

        # we add the participant to the conversation
        add_participant_to_conversation(user_email, incident_id, db_session)

        # we announce the participant in the conversation
        send_incident_participant_announcement_message(user_email, incident_id,
                                                       db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(user_email, incident_id,
                                                   db_session)

        # we send a suggested reading message to the participant
        suggested_document_items = get_suggested_document_items(
            incident_id, db_session)
        send_incident_suggested_reading_messages(incident_id,
                                                 suggested_document_items,
                                                 user_email, db_session)

    return participant
Exemple #4
0
def incident_add_or_reactivate_participant_flow(
    user_email: str, incident_id: int, role: ParticipantRoleType = None, db_session=None
):
    """Runs the add or reactivate incident participant flow."""
    # we load the incident instance
    incident = incident_service.get(db_session=db_session, incident_id=incident_id)

    # We get information about the individual
    contact_plugin = plugins.get(INCIDENT_PLUGIN_CONTACT_SLUG)
    individual_info = contact_plugin.get(user_email)

    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email
    )

    if participant:
        if participant.is_active:
            log.debug(f"{individual_info['fullname']} is already an active participant.")
            return
        else:
            # we reactivate the participant
            reactivated = participant_flows.reactivate_participant(
                user_email, incident_id, db_session
            )

            if reactivated:
                # we add the participant to the conversation
                add_participant_to_conversation(incident.conversation.channel_id, user_email)

                # we announce the participant in the conversation
                send_incident_participant_announcement_message(user_email, incident, db_session)

                # we send the welcome messages to the participant
                send_incident_welcome_participant_messages(user_email, incident, db_session)

            return
    else:
        # we add the participant to the incident
        added = participant_flows.add_participant(user_email, incident_id, db_session, role=role)

        if added:
            # we add the participant to the tactical group
            add_participant_to_tactical_group(user_email, incident_id)

            # we add the participant to the conversation
            add_participant_to_conversation(incident.conversation.channel_id, user_email)

            # we announce the participant in the conversation
            send_incident_participant_announcement_message(user_email, incident, db_session)

            # we send the welcome messages to the participant
            send_incident_welcome_participant_messages(user_email, incident, db_session)
Exemple #5
0
def create(
    *,
    db_session,
    incident_priority: str,
    incident_type: str,
    reporter_email: str,
    title: str,
    status: str,
    description: str,
    visibility: str = None,
) -> Incident:
    """Creates a new incident."""
    # We get the incident type by name
    incident_type = incident_type_service.get_by_name(
        db_session=db_session, name=incident_type["name"])

    # We get the incident priority by name
    incident_priority = incident_priority_service.get_by_name(
        db_session=db_session, name=incident_priority["name"])

    if not visibility:
        visibility = incident_type.visibility

    # We create the incident
    incident = Incident(
        title=title,
        description=description,
        status=status,
        incident_type=incident_type,
        incident_priority=incident_priority,
        visibility=visibility,
    )
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident created",
        incident_id=incident.id,
    )

    # We add the reporter to the incident
    reporter_participant = participant_flows.add_participant(
        reporter_email, incident.id, db_session, ParticipantRoleType.reporter)

    # We resolve the incident commander email
    incident_commander_email = resolve_incident_commander_email(
        db_session,
        reporter_email,
        incident_type.name,
        "",
        title,
        description,
        incident_priority.page_commander,
    )

    if reporter_email == incident_commander_email:
        # We add the role of incident commander the reporter
        participant_role_service.add_role(
            participant_id=reporter_participant.id,
            participant_role=ParticipantRoleType.incident_commander,
            db_session=db_session,
        )
    else:
        # We create a new participant for the incident commander and we add it to the incident
        participant_flows.add_participant(
            incident_commander_email,
            incident.id,
            db_session,
            ParticipantRoleType.incident_commander,
        )

    return incident
Exemple #6
0
def incident_create_flow(*, incident_id: int, checkpoint: str = None, db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session, incident_id=incident_id)

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    if ticket:
        incident.ticket = ticket_service.create(
            db_session=db_session, ticket_in=TicketCreate(**ticket)
        )

        # we set the incident name
        incident.name = ticket["resource_id"]

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(
            user_email=individual.email, incident_id=incident.id, db_session=db_session
        )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    participant_emails = [x.individual.email for x in incident.participants]

    group_plugin = plugin_service.get_active(db_session=db_session, plugin_type="participant-group")
    tactical_group = None
    notification_group = None
    if group_plugin:
        try:
            tactical_group, notification_group = create_participant_groups(
                incident, individual_participants, team_participants, db_session
            )

            for g in [tactical_group, notification_group]:
                group_in = GroupCreate(
                    name=g["name"],
                    email=g["email"],
                    resource_type=g["resource_type"],
                    resource_id=g["resource_id"],
                    weblink=g["weblink"],
                )
                incident.groups.append(
                    group_service.create(db_session=db_session, group_in=group_in)
                )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Tactical and notification groups added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of tactical and notification groups failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    storage_plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage")
    if storage_plugin:
        # we create storage resource
        if group_plugin:
            storage = create_incident_storage(
                incident, [tactical_group["email"], notification_group["email"]], db_session
            )
        else:
            # we don't have a group so add participants directly
            storage = create_incident_storage(incident, participant_emails, db_session)

        incident.storage = storage_service.create(
            db_session=db_session,
            resource_id=storage["resource_id"],
            resource_type=storage["resource_type"],
            weblink=storage["weblink"],
        )

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Storage added to incident",
            incident_id=incident.id,
        )

        # we create collaboration documents, don't fail the whole flow if this fails
        try:
            collab_documents = create_collaboration_documents(incident, db_session)

            for d in collab_documents:
                document_in = DocumentCreate(
                    name=d["name"],
                    resource_id=d["resource_id"],
                    resource_type=d["resource_type"],
                    weblink=d["weblink"],
                )
                incident.documents.append(
                    document_service.create(db_session=db_session, document_in=document_in)
                )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Documents added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident documents failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    conference_plugin = plugin_service.get_active(db_session=db_session, plugin_type="conference")
    if conference_plugin:
        try:
            participants = participant_emails
            
            if group_plugin:
                # we use the tactical group email if the group plugin is enabled
                participants = [tactical_group["email"]]
                
            conference = create_conference(incident, participants, db_session)

            conference_in = ConferenceCreate(
                resource_id=conference["resource_id"],
                resource_type=conference["resource_type"],
                weblink=conference["weblink"],
                conference_id=conference["id"],
                conference_challenge=conference["challenge"],
            )
            incident.conference = conference_service.create(
                db_session=db_session, conference_in=conference_in
            )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Conference added to incident",
                incident_id=incident.id,
            )
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident conference failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    # we create the conversation for real-time communications

    conversation_plugin = plugin_service.get_active(
        db_session=db_session, plugin_type="conversation"
    )
    if conversation_plugin:
        try:
            conversation = create_conversation(incident, participant_emails, db_session)

            conversation_in = ConversationCreate(
                resource_id=conversation["resource_id"],
                resource_type=conversation["resource_type"],
                weblink=conversation["weblink"],
                channel_id=conversation["id"],
            )
            incident.conversation = conversation_service.create(
                db_session=db_session, conversation_in=conversation_in
            )

            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Conversation added to incident",
                incident_id=incident.id,
            )

            # we set the conversation topic
            set_conversation_topic(incident, db_session)
        except Exception as e:
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description=f"Creation of incident conversation failed. Reason: {e}",
                incident_id=incident.id,
            )
            log.exception(e)

    db_session.add(incident)
    db_session.commit()

    # we update the incident ticket
    update_external_incident_ticket(incident, db_session)

    # we update the investigation document
    document_plugin = plugin_service.get_active(db_session=db_session, plugin_type="document")
    if document_plugin:
        if incident.incident_document:
            try:
                document_plugin.instance.update(
                    incident.incident_document.resource_id,
                    name=incident.name,
                    priority=incident.incident_priority.name,
                    status=incident.status,
                    type=incident.incident_type.name,
                    title=incident.title,
                    description=incident.description,
                    commander_fullname=incident.commander.name,
                    conversation_weblink=resolve_attr(incident, "conversation.weblink"),
                    document_weblink=resolve_attr(incident, "incident_document.weblink"),
                    storage_weblink=resolve_attr(incident, "storage.weblink"),
                    ticket_weblink=resolve_attr(incident, "ticket.weblink"),
                    conference_weblink=resolve_attr(incident, "conference.weblink"),
                    conference_challenge=resolve_attr(incident, "conference.challendge"),
                )
            except Exception as e:
                event_service.log(
                    db_session=db_session,
                    source="Dispatch Core App",
                    description=f"Incident documents rendering failed. Reason: {e}",
                    incident_id=incident.id,
                )
                log.exception(e)

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )

    suggested_document_items = get_suggested_document_items(incident.id, db_session)

    for participant in incident.participants:
        # we announce the participant in the conversation
        # should protect ourselves from failures of any one participant
        try:
            send_incident_participant_announcement_message(
                participant.individual.email, incident.id, db_session
            )

            # we send the welcome messages to the participant
            send_incident_welcome_participant_messages(
                participant.individual.email, incident.id, db_session
            )

            send_incident_suggested_reading_messages(
                incident.id, suggested_document_items, participant.individual.email, db_session
            )

        except Exception as e:
            log.exception(e)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )
Exemple #7
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(user_email=individual.email,
                                          incident_id=incident.id,
                                          db_session=db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="External ticket added to incident",
        incident_id=incident.id,
    )

    # we set the incident name
    name = ticket["resource_id"]
    incident.name = name

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    tactical_group, notification_group = create_participant_groups(
        incident, individual_participants, team_participants, db_session)

    for g in [tactical_group, notification_group]:
        group_in = GroupCreate(
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(
            group_service.create(db_session=db_session, group_in=group_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Tactical and notification groups added to incident",
        incident_id=incident.id,
    )

    # we create storage resource
    storage = create_incident_storage(
        incident, [tactical_group["email"], notification_group["email"]],
        db_session)
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Storage added to incident",
        incident_id=incident.id,
    )

    # we create the incident documents
    incident_document, incident_sheet = create_collaboration_documents(
        incident, db_session)

    # TODO: we need to delineate between the investigation document and suggested documents
    # # get any additional documentation based on priority or terms
    # incident_documents = get_incident_documents(
    #     db_session, incident.incident_type, incident.incident_priority, incident.description
    # )
    #
    # incident.documents = incident_documents

    faq_document = {
        "name": "Incident FAQ",
        "resource_id": INCIDENT_FAQ_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}",
        "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT,
    }

    conversation_commands_reference_document = {
        "name":
        "Incident Conversation Commands Reference Document",
        "resource_id":
        INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID}",
        "resource_type":
        INCIDENT_RESOURCE_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT,
    }

    for d in [
            incident_document,
            incident_sheet,
            faq_document,
            conversation_commands_reference_document,
    ]:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Documents added to incident",
        incident_id=incident.id,
    )

    conference = create_conference(incident, [tactical_group["email"]],
                                   db_session)

    conference_in = ConferenceCreate(
        resource_id=conference["resource_id"],
        resource_type=conference["resource_type"],
        weblink=conference["weblink"],
        conference_id=conference["id"],
        conference_challenge=conference["challenge"],
    )
    incident.conference = conference_service.create(
        db_session=db_session, conference_in=conference_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conference added to incident",
        incident_id=incident.id,
    )

    # we create the conversation for real-time communications
    participant_emails = [x.individual.email for x in incident.participants]
    conversation = create_conversation(incident, participant_emails,
                                       db_session)

    conversation_in = ConversationCreate(
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    incident.conversation = conversation_service.create(
        db_session=db_session, conversation_in=conversation_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conversation added to incident",
        incident_id=incident.id,
    )

    db_session.add(incident)
    db_session.commit()

    # we set the conversation topic
    set_conversation_topic(incident)

    # we update the incident ticket
    update_incident_ticket(
        incident.ticket.resource_id,
        title=incident.title,
        description=incident.description,
        incident_type=incident.incident_type.name,
        priority=incident.incident_priority.name,
        status=incident.status,
        commander_email=incident.commander.email,
        reporter_email=incident.reporter.email,
        conversation_weblink=incident.conversation.weblink,
        document_weblink=incident_document["weblink"],
        storage_weblink=incident.storage.weblink,
        conference_weblink=incident.conference.weblink,
        visibility=incident.visibility,
    )

    # we update the investigation document
    update_document(
        incident_document["id"],
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.incident_type.name,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
        incident.ticket.weblink,
        incident.conference.weblink,
        incident.conference.conference_challenge,
    )

    for participant in incident.participants:
        # we announce the participant in the conversation
        send_incident_participant_announcement_message(
            participant.individual.email, incident.id, db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(
            participant.individual.email, incident.id, db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )
Exemple #8
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        db_session, incident.incident_type, incident.incident_priority,
        incident.description)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(db_session=db_session,
                                          user_email=individual.email,
                                          incident_id=incident.id)

    log.debug(f"Added {len(individual_participants)} to incident.")

    # create the incident ticket
    ticket = create_incident_ticket(
        incident.title,
        incident.incident_type.name,
        incident.incident_priority.name,
        incident.commander.email,
        incident.reporter.email,
    )

    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    log.debug("Added ticket to incident.")

    # we set the incident name
    name = ticket["resource_id"]

    incident.name = name

    log.debug("Added name to incident.")

    # we create the participant groups (tactical and notification)
    tactical_group, notification_group = create_participant_groups(
        name, team_participants, [x.individual for x in incident.participants])

    for g in [tactical_group, notification_group]:
        group = group_service.create(
            db_session=db_session,
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(group)

    log.debug("Added groups to incident.")

    # we create storage resource
    storage = create_incident_storage(
        name, [tactical_group["email"], notification_group["email"]])
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    # we create the incident documents
    incident_document, incident_sheet = create_collaboration_documents(
        incident.name,
        incident.incident_type.name,
        incident.storage.resource_id,
        incident.incident_type.template_document.resource_id,
    )

    # TODO: we need to delineate between the investigation document and suggested documents
    # # get any additional documentation based on priority or terms
    # incident_documents = get_incident_documents(
    #     db_session, incident.incident_type, incident.incident_priority, incident.description
    # )
    #
    # incident.documents = incident_documents

    faq_document = {
        "name": "Incident FAQ",
        "resource_id": INCIDENT_FAQ_DOCUMENT_ID,
        "weblink":
        f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}",
        "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT,
    }

    for d in [incident_document, incident_sheet, faq_document]:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    log.debug("Added documents to incident.")

    # we create the conversation for real-time communications
    conversation = create_conversation(
        incident, [x.individual.email for x in incident.participants])

    log.debug("Conversation created. Participants and bots added.")

    incident.conversation = conversation_service.create(
        db_session=db_session,
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    db_session.add(incident)
    db_session.commit()

    log.debug("Added conversation to incident.")

    # we set the conversation topic
    set_conversation_topic(incident)

    update_incident_ticket(
        incident.ticket.resource_id,
        incident.title,
        incident.description,
        incident.incident_type.name,
        incident.incident_priority.name,
        incident.status,
        incident.commander.email,
        incident.reporter.email,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
    )

    log.debug("Updated incident ticket.")

    update_document(
        incident_document["id"],
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident_document["weblink"],
        incident.storage.weblink,
        incident.ticket.weblink,
    )

    log.debug("Updated incident document.")

    for participant in incident.participants:
        # we announce the participant in the conversation
        send_incident_participant_announcement_message(
            participant.individual.email, incident, db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(
            participant.individual.email, incident, db_session)

    log.debug("Sent incident welcome and announcement notifications.")

    send_incident_notifications(incident, db_session)

    log.debug("Sent incident notifications.")
Exemple #9
0
def create(
    *,
    db_session,
    incident_priority: str,
    incident_type: str,
    reporter_email: str,
    title: str,
    status: str,
    description: str,
    tags: List[dict],
    visibility: str = None,
) -> Incident:
    """Creates a new incident."""
    # We get the incident type by name
    if not incident_type:
        incident_type = incident_type_service.get_default(
            db_session=db_session)
        if not incident_type:
            raise Exception(
                "No incident type specified and no default has been defined.")
    else:
        incident_type = incident_type_service.get_by_name(
            db_session=db_session, name=incident_type["name"])

    # We get the incident priority by name
    if not incident_priority:
        incident_priority = incident_priority_service.get_default(
            db_session=db_session)
        if not incident_priority:
            raise Exception(
                "No incident priority specified and no default has been defined."
            )
    else:
        incident_priority = incident_priority_service.get_by_name(
            db_session=db_session, name=incident_priority["name"])

    if not visibility:
        visibility = incident_type.visibility

    tag_objs = []
    for t in tags:
        tag_objs.append(
            tag_service.get_or_create(db_session=db_session,
                                      tag_in=TagCreate(**t)))

    # We create the incident
    incident = Incident(
        title=title,
        description=description,
        status=status,
        incident_type=incident_type,
        incident_priority=incident_priority,
        visibility=visibility,
        tags=tag_objs,
    )
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident created",
        incident_id=incident.id,
    )

    # We add the reporter to the incident
    reporter_participant = participant_flows.add_participant(
        reporter_email, incident.id, db_session, ParticipantRoleType.reporter)

    # We resolve the incident commander email
    incident_commander_email = resolve_incident_commander_email(
        db_session,
        reporter_email,
        incident_type.name,
        "",
        title,
        description,
        incident_priority.page_commander,
    )

    if reporter_email == incident_commander_email:
        # We add the role of incident commander the reporter
        participant_role_service.add_role(
            participant_id=reporter_participant.id,
            participant_role=ParticipantRoleType.incident_commander,
            db_session=db_session,
        )
    else:
        # We create a new participant for the incident commander and we add it to the incident
        participant_flows.add_participant(
            incident_commander_email,
            incident.id,
            db_session,
            ParticipantRoleType.incident_commander,
        )

    return incident
Exemple #10
0
def incident_create_flow(*,
                         incident_id: int,
                         checkpoint: str = None,
                         db_session=None):
    """Creates all resources required for new incidents."""
    incident = incident_service.get(db_session=db_session,
                                    incident_id=incident_id)

    # get the incident participants based on incident type and priority
    individual_participants, team_participants = get_incident_participants(
        incident, db_session)

    # add individuals to incident
    for individual in individual_participants:
        participant_flows.add_participant(user_email=individual.email,
                                          incident_id=incident.id,
                                          db_session=db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident participants added to incident",
        incident_id=incident.id,
    )

    # create the incident ticket
    ticket = create_incident_ticket(incident, db_session)
    incident.ticket = ticket_service.create(db_session=db_session,
                                            ticket_in=TicketCreate(**ticket))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="External ticket added to incident",
        incident_id=incident.id,
    )

    # we set the incident name
    name = ticket["resource_id"]
    incident.name = name

    # we create the participant groups (tactical and notification)
    individual_participants = [x.individual for x in incident.participants]
    tactical_group, notification_group = create_participant_groups(
        incident, individual_participants, team_participants, db_session)

    for g in [tactical_group, notification_group]:
        group_in = GroupCreate(
            name=g["name"],
            email=g["email"],
            resource_type=g["resource_type"],
            resource_id=g["resource_id"],
            weblink=g["weblink"],
        )
        incident.groups.append(
            group_service.create(db_session=db_session, group_in=group_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Tactical and notification groups added to incident",
        incident_id=incident.id,
    )

    # we create storage resource
    storage = create_incident_storage(
        incident, [tactical_group["email"], notification_group["email"]],
        db_session)
    incident.storage = storage_service.create(
        db_session=db_session,
        resource_id=storage["resource_id"],
        resource_type=storage["resource_type"],
        weblink=storage["weblink"],
    )

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Storage added to incident",
        incident_id=incident.id,
    )

    # we create the incident documents
    collab_documents = create_collaboration_documents(incident, db_session)

    for d in collab_documents:
        document_in = DocumentCreate(
            name=d["name"],
            resource_id=d["resource_id"],
            resource_type=d["resource_type"],
            weblink=d["weblink"],
        )
        incident.documents.append(
            document_service.create(db_session=db_session,
                                    document_in=document_in))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Documents added to incident",
        incident_id=incident.id,
    )

    conference = create_conference(incident, [tactical_group["email"]],
                                   db_session)

    conference_in = ConferenceCreate(
        resource_id=conference["resource_id"],
        resource_type=conference["resource_type"],
        weblink=conference["weblink"],
        conference_id=conference["id"],
        conference_challenge=conference["challenge"],
    )
    incident.conference = conference_service.create(
        db_session=db_session, conference_in=conference_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conference added to incident",
        incident_id=incident.id,
    )

    # we create the conversation for real-time communications
    participant_emails = [x.individual.email for x in incident.participants]
    conversation = create_conversation(incident, participant_emails,
                                       db_session)

    conversation_in = ConversationCreate(
        resource_id=conversation["resource_id"],
        resource_type=conversation["resource_type"],
        weblink=conversation["weblink"],
        channel_id=conversation["id"],
    )
    incident.conversation = conversation_service.create(
        db_session=db_session, conversation_in=conversation_in)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Conversation added to incident",
        incident_id=incident.id,
    )

    db_session.add(incident)
    db_session.commit()

    # we set the conversation topic
    set_conversation_topic(incident)

    # we update the incident ticket
    update_external_incident_ticket(incident, db_session)

    # we update the investigation document
    update_document(
        incident.incident_document.resource_id,
        incident.name,
        incident.incident_priority.name,
        incident.status,
        incident.incident_type.name,
        incident.title,
        incident.description,
        incident.commander.name,
        incident.conversation.weblink,
        incident.incident_document.weblink,
        incident.storage.weblink,
        incident.ticket.weblink,
        incident.conference.weblink,
        incident.conference.conference_challenge,
    )

    if incident.visibility == Visibility.open:
        send_incident_notifications(incident, db_session)
        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Incident notifications sent",
            incident_id=incident.id,
        )

    suggested_document_items = get_suggested_document_items(
        incident, db_session)

    for participant in incident.participants:
        # we announce the participant in the conversation
        # should protect ourselves from failures of any one participant
        try:
            send_incident_participant_announcement_message(
                participant.individual.email, incident.id, db_session)

            # we send the welcome messages to the participant
            send_incident_welcome_participant_messages(
                participant.individual.email, incident.id, db_session)

            send_incident_suggested_reading_messages(incident,
                                                     suggested_document_items,
                                                     participant.email)

        except Exception as e:
            log.exception(e)
            sentry_sdk.capture_exception(e)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Participants announced and welcome messages sent",
        incident_id=incident.id,
    )
Exemple #11
0
def incident_add_or_reactivate_participant_flow(
    user_email: str,
    incident_id: int,
    service: Service = None,
    role: ParticipantRoleType = None,
    event: dict = None,
    db_session=None,
) -> Participant:
    """Runs the add or reactivate incident participant flow."""
    if service:
        # we need to ensure that we don't add another member of a service if one
        # already exists (e.g. overlapping oncalls, we assume they will hand-off if necessary)
        participant = participant_service.get_by_incident_id_and_service(
            incident_id=incident_id, service_id=service.id, db_session=db_session
        )
        if participant:
            log.debug("Skipping resolved participant, service member already engaged.")
            return

    participant = participant_service.get_by_incident_id_and_email(
        db_session=db_session, incident_id=incident_id, email=user_email
    )

    if participant:
        if participant.is_active:
            log.debug(f"{user_email} is already an active participant.")
        else:
            # we reactivate the participant
            reactivated = participant_flows.reactivate_participant(
                user_email, incident_id, db_session
            )

            if reactivated:
                # we add the participant to the conversation
                add_participants_to_conversation([user_email], incident_id, db_session)

                # we announce the participant in the conversation
                send_incident_participant_announcement_message(user_email, incident_id, db_session)

                # we send the welcome messages to the participant
                send_incident_welcome_participant_messages(user_email, incident_id, db_session)
    else:
        # we add the participant to the incident
        participant = participant_flows.add_participant(
            user_email, incident_id, db_session, service=service, role=role
        )

        # we add the participant to the tactical group
        add_participant_to_tactical_group(user_email, incident_id, db_session)

        # we add the participant to the conversation
        add_participants_to_conversation([user_email], incident_id, db_session)

        # we announce the participant in the conversation
        send_incident_participant_announcement_message(user_email, incident_id, db_session)

        # we send the welcome messages to the participant
        send_incident_welcome_participant_messages(user_email, incident_id, db_session)

        # we send a suggested reading message to the participant
        suggested_document_items = get_suggested_document_items(incident_id, db_session)
        send_incident_suggested_reading_messages(
            incident_id, suggested_document_items, user_email, db_session
        )

    return participant
Exemple #12
0
def create(*, db_session, incident_in: IncidentCreate) -> Incident:
    """Creates a new incident."""
    project = project_service.get_by_name_or_default(
        db_session=db_session, project_in=incident_in.project
    )

    incident_type = incident_type_service.get_by_name_or_default(
        db_session=db_session, project_id=project.id, incident_type_in=incident_in.incident_type
    )

    incident_priority = incident_priority_service.get_by_name_or_default(
        db_session=db_session,
        project_id=project.id,
        incident_priority_in=incident_in.incident_priority,
    )

    if not incident_in.visibility:
        visibility = incident_type.visibility
    else:
        visibility = incident_in.visibility

    tag_objs = []
    for t in incident_in.tags:
        tag_objs.append(tag_service.get_or_create(db_session=db_session, tag_in=t))

    # We create the incident
    incident = Incident(
        title=incident_in.title,
        description=incident_in.description,
        status=incident_in.status,
        incident_type=incident_type,
        incident_priority=incident_priority,
        visibility=visibility,
        tags=tag_objs,
        project=project,
    )
    db_session.add(incident)
    db_session.commit()

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Incident created",
        incident_id=incident.id,
    )

    # add reporter
    reporter_email = incident_in.reporter.individual.email
    participant_flows.add_participant(
        reporter_email,
        incident,
        db_session,
        role=ParticipantRoleType.reporter,
    )

    # add commander
    commander_email = commander_service_id = None
    if incident_in.commander:
        commander_email = incident_in.commander.individual.email
    else:
        commander_email, commander_service_id = resolve_and_associate_role(
            db_session=db_session, incident=incident, role=ParticipantRoleType.incident_commander
        )

    if not commander_email:
        # we make the reporter the commander if an email for the commander
        # was not provided or resolved via incident role policies
        commander_email = reporter_email

    participant_flows.add_participant(
        commander_email,
        incident,
        db_session,
        service_id=commander_service_id,
        role=ParticipantRoleType.incident_commander,
    )

    # add liaison
    liaison_email, liaison_service_id = resolve_and_associate_role(
        db_session=db_session, incident=incident, role=ParticipantRoleType.liaison
    )

    if liaison_email:
        # we only add the liaison if we are able to resolve its email
        # via incident role policies
        participant_flows.add_participant(
            liaison_email,
            incident,
            db_session,
            service_id=liaison_service_id,
            role=ParticipantRoleType.liaison,
        )

    # add scribe
    scribe_email, scribe_service_id = resolve_and_associate_role(
        db_session=db_session, incident=incident, role=ParticipantRoleType.scribe
    )

    if scribe_email:
        # we only add the scribe if we are able to resolve its email
        # via incident role policies
        participant_flows.add_participant(
            scribe_email,
            incident,
            db_session,
            service_id=scribe_service_id,
            role=ParticipantRoleType.scribe,
        )

    return incident
Exemple #13
0
def job_create_flow(*, job_id: int, checkpoint: str = None, db_session=None):
    """Creates all resources required for new jobs."""
    job = job_service.get(db_session=db_session, job_id=job_id)

    # get the job participants based on job type and priority
    worker_participants, team_participants = get_job_participants(
        job, db_session)

    # add workers to job
    for worker in worker_participants:
        participant_flows.add_participant(user_code=worker.code,
                                          job_id=job.id,
                                          db_session=db_session)

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="Job participants added to job",
        job_id=job.id,
    )

    # create the job ticket
    ticket = create_job_ticket(job, db_session)
    job.ticket = ticket_service.create(db_session=db_session,
                                       ticket_in=TicketCreate(**ticket))

    event_service.log(
        db_session=db_session,
        source="Dispatch Core App",
        description="External ticket added to job",
        job_id=job.id,
    )

    # we set the job name
    name = ticket["resource_id"]
    job.name = name

    # we create the participant groups (tactical and notification)
    try:
        worker_participants = [x.worker for x in job.participants]
        tactical_group, notification_group = create_participant_groups(
            job, worker_participants, team_participants, db_session)

        for g in [tactical_group, notification_group]:
            group_in = GroupCreate(
                name=g["name"],
                code=g["email"],
                resource_type=g["resource_type"],
                resource_id=g["resource_id"],
                weblink=g["weblink"],
            )
            job.groups.append(
                group_service.create(db_session=db_session, group_in=group_in))

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Tactical and notification groups added to job",
            job_id=job.id,
        )

        # we create storage resource
        storage = create_job_storage(
            job, [tactical_group["email"], notification_group["email"]],
            db_session)
        job.storage = storage_service.create(
            db_session=db_session,
            resource_id=storage["resource_id"],
            resource_type=storage["resource_type"],
            weblink=storage["weblink"],
        )

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Storage added to job",
            job_id=job.id,
        )

        # we create the job documents
        job_document, job_sheet = create_collaboration_documents(
            job, db_session)

        faq_document = {
            "name": "Job FAQ",
            "resource_id": INCIDENT_FAQ_DOCUMENT_ID,
            "weblink":
            f"https://docs.google.com/document/d/{INCIDENT_FAQ_DOCUMENT_ID}",
            "resource_type": INCIDENT_RESOURCE_FAQ_DOCUMENT,
        }

        conversation_commands_reference_document = {
            "name":
            "Job Conversation Commands Reference Document",
            "resource_id":
            INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID,
            "weblink":
            f"https://docs.google.com/document/d/{INCIDENT_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT_ID}",
            "resource_type":
            INCIDENT_RESOURCE_CONVERSATION_COMMANDS_REFERENCE_DOCUMENT,
        }

        for d in [
                job_document,
                job_sheet,
                faq_document,
                conversation_commands_reference_document,
        ]:
            document_in = DocumentCreate(
                name=d["name"],
                resource_id=d["resource_id"],
                resource_type=d["resource_type"],
                weblink=d["weblink"],
            )
            job.documents.append(
                document_service.create(db_session=db_session,
                                        document_in=document_in))

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Documents added to job",
            job_id=job.id,
        )

        conference = create_conference(job, [tactical_group["email"]],
                                       db_session)

        conference_in = ConferenceCreate(
            resource_id=conference["resource_id"],
            resource_type=conference["resource_type"],
            weblink=conference["weblink"],
            conference_id=conference["id"],
            conference_challenge=conference["challenge"],
        )
        job.conference = conference_service.create(db_session=db_session,
                                                   conference_in=conference_in)

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Conference added to job",
            job_id=job.id,
        )

        # we create the conversation for real-time communications
        participant_emails = [x.worker.code for x in job.participants]
        conversation = create_conversation(job, participant_emails, db_session)

        conversation_in = ConversationCreate(
            resource_id=conversation["resource_id"],
            resource_type=conversation["resource_type"],
            weblink=conversation["weblink"],
            channel_id=conversation["id"],
        )
        job.conversation = conversation_service.create(
            db_session=db_session, conversation_in=conversation_in)

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Conversation added to job",
            job_id=job.id,
        )

    except Exception as e:
        log.warn("failed to create ... , job_id ={}".format(job_id), e)

    db_session.add(job)
    db_session.commit()
    try:

        # we set the conversation topic
        set_conversation_topic(job)

        # we update the job ticket
        update_external_job_ticket(job, db_session)

        # we update the investigation document
        update_document(
            job_document["id"],
            job.name,
            job.job_priority.name,
            job.planning_status,
            job.job_type.name,
            job.job_code,
            job.description,
            job.commander.name,
            job.conversation.weblink,
            job_document["weblink"],
            job.storage.weblink,
            job.ticket.weblink,
            job.conference.weblink,
            job.conference.conference_challenge,
        )

        for participant in job.participants:
            # we announce the participant in the conversation
            send_job_participant_announcement_message(participant.worker.code,
                                                      job.id, db_session)

            # we send the welcome messages to the participant
            send_job_welcome_participant_messages(participant.worker.code,
                                                  job.id, db_session)

            send_job_suggested_reading_messages(participant.worker.code,
                                                job.id, db_session)

        event_service.log(
            db_session=db_session,
            source="Dispatch Core App",
            description="Participants announced and welcome messages sent",
            job_id=job.id,
        )

        if job.visibility == Visibility.open:
            send_job_notifications(job, db_session)
            event_service.log(
                db_session=db_session,
                source="Dispatch Core App",
                description="Job notifications sent",
                job_id=job.id,
            )

    except Exception as e:
        log.warn("failed to create more ... , job_id ={}".format(job_id), e)