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()
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, )
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
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)
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
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, )
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, )
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.")
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
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, )
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
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
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)