def create_instance(*, db_session, instance_in: WorkflowInstanceCreate) -> WorkflowInstance: """Creates a new workflow instance.""" instance = WorkflowInstance(**instance_in.dict( exclude={"incident", "workflow", "creator", "artifacts"})) incident = incident_service.get(db_session=db_session, incident_id=instance_in.incident.id) instance.incident = incident workflow = get(db_session=db_session, workflow_id=instance_in.workflow.id) instance.workflow = workflow creator = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident.id, email=instance_in.creator.individual.email) instance.creator = creator for a in instance_in.artifacts: artifact_document = document_service.create(db_session=db_session, document_in=a) instance.artifacts.append(artifact_document) db_session.add(instance) db_session.commit() return instance
def test_create(session, project): from dispatch.document.service import create from dispatch.document.models import DocumentCreate name = "XXX" resource_id = "XXX" resource_type = "XXX" weblink = "https://example.com/" document_in = DocumentCreate( name=name, resource_id=resource_id, resource_type=resource_type, weblink=weblink, project={"id": project.id, "name": project.name}, ) document = create(db_session=session, document_in=document_in) assert document
def create_executive_report( user_email: str, incident_id: int, executive_report_in: ExecutiveReportCreate, organization_slug: str = None, db_session=None, ): """Creates an executive report.""" current_date = date.today().strftime("%B %d, %Y") current_status = executive_report_in.current_status overview = executive_report_in.overview next_steps = executive_report_in.next_steps # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) if not incident.incident_type.executive_template_document: raise ValidationError( [ ErrorWrapper( InvalidConfigurationError( msg="No executive report template defined."), loc="executive_template_document", ) ], model=ExecutiveReportCreate, ) # we fetch all previous executive reports executive_reports = get_all_by_incident_id_and_type( db_session=db_session, incident_id=incident_id, report_type=ReportTypes.executive_report) previous_executive_reports = [] for executive_report in executive_reports: previous_executive_reports.append( f"{executive_report.document.name} - {executive_report.document.weblink}\n" ) # we create a new executive report details = { "current_status": current_status, "overview": overview, "next_steps": next_steps } executive_report_in = ReportCreate( details=details, type=ReportTypes.executive_report, ) executive_report = create(db_session=db_session, report_in=executive_report_in) # we load the participant participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=user_email) # we save the executive report participant.reports.append(executive_report) incident.reports.append(executive_report) db_session.add(participant) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Incident Participant", description= f"{participant.individual.name} created a new executive report", details={ "current_status": current_status, "overview": overview, "next_steps": next_steps }, incident_id=incident_id, individual_id=participant.individual.id, ) # we create a new document for the executive report storage_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="storage") executive_report_document_name = f"{incident.name} - Executive Report - {current_date}" executive_report_document = storage_plugin.instance.copy_file( folder_id=incident.storage.resource_id, file_id=incident.incident_type.executive_template_document.resource_id, name=executive_report_document_name, ) executive_report_document.update({ "name": executive_report_document_name, "resource_type": DocumentResourceTypes.executive, }) storage_plugin.instance.move_file( new_folder_id=incident.storage.resource_id, file_id=executive_report_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.plugin.title, description="Executive report document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=executive_report_document["name"], resource_id=executive_report_document["id"], resource_type=executive_report_document["resource_type"], project=incident.project, weblink=executive_report_document["weblink"], ) executive_report.document = document_service.create( db_session=db_session, document_in=document_in) incident.documents.append(executive_report.document) db_session.add(executive_report) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Executive report document added to incident", incident_id=incident.id, ) # we update the incident update document document_plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="document") document_plugin.instance.update( executive_report_document["id"], name=incident.name, title=incident.title, current_date=current_date, current_status=current_status, overview=overview, next_steps=next_steps, previous_reports="\n".join(previous_executive_reports), commander_fullname=incident.commander.individual.name, commander_team=incident.commander.team, commander_weblink=incident.commander.individual.weblink, ) # we send the executive report to the notifications group send_executive_report_to_notifications_group(incident.id, executive_report, db_session) return executive_report
def incident_stable_status_flow(incident: Incident, db_session=None): """Runs the incident stable flow.""" # we set the stable time incident.stable_at = datetime.utcnow() # set time immediately db_session.add(incident) db_session.commit() if incident.incident_review_document: log.debug("Incident review document already created... skipping creation.") return storage_plugin = plugin_service.get_active(db_session=db_session, plugin_type="storage") if not storage_plugin: log.warning("Incident review document not created, no storage plugin enabled.") return # we create a copy of the incident review document template and we move it to the incident storage incident_review_document_name = f"{incident.name} - Post Incident Review Document" template = document_service.get_incident_review_template(db_session=db_session) # incident review document is optional if not template: log.warning("No incident review template specificed.") return incident_review_document = storage_plugin.instance.copy_file( folder_id=incident.storage.resource_id, file_id=template.resource_id, name=incident_review_document_name, ) incident_review_document.update( { "name": incident_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, } ) storage_plugin.instance.move_file( new_folder_id=incident.storage.resource_id, file_id=incident_review_document["id"], ) event_service.log( db_session=db_session, source=storage_plugin.title, description="Incident review document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=incident_review_document["name"], resource_id=incident_review_document["id"], resource_type=incident_review_document["resource_type"], weblink=incident_review_document["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="Incident review document added to incident", incident_id=incident.id, ) # we update the incident review document document_plugin = plugin_service.get_active(db_session=db_session, plugin_type="document") if document_plugin: document_plugin.instance.update( incident.incident_review_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"), ) else: log.warning("No document plugin enabled, could not update template.") # we send a notification about the incident review document to the conversation send_incident_review_document_notification( incident.conversation.channel_id, incident.incident_review_document.weblink, db_session, ) db_session.add(incident) db_session.commit()
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_stable_flow(incident_id: int, command: Optional[dict] = None, db_session=None): """Runs the incident stable flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) # we set the stable time incident.stable_at = datetime.utcnow() # we remind the incident commander to write a status report send_incident_status_report_reminder(incident) # we update the incident cost incident_cost = incident_service.calculate_cost(incident_id, db_session) # we update the external ticket update_incident_ticket(incident.ticket.resource_id, status=IncidentStatus.stable.lower(), cost=incident_cost) incident_review_document = get_document( db_session=db_session, incident_id=incident.id, resource_type=INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, ) if not incident_review_document: storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) # we create a copy of the incident review document template and we move it to the incident storage incident_review_document_name = f"{incident.name} - Post Incident Review Document" incident_review_document = storage_plugin.copy_file( team_drive_id=incident.storage.resource_id, file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID, name=incident_review_document_name, ) incident_review_document.update({ "name": incident_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, }) storage_plugin.move_file( new_team_drive_id=incident.storage.resource_id, file_id=incident_review_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.title, description="Incident review document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=incident_review_document["name"], resource_id=incident_review_document["id"], resource_type=incident_review_document["resource_type"], weblink=incident_review_document["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="Incident review document added to incident", incident_id=incident.id, ) # we get the incident investigation and faq documents incident_document = get_document( db_session=db_session, incident_id=incident_id, resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT, ) # we update the incident review document update_document( incident_review_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, ) # we send a notification about the incident review document to the conversation send_incident_review_document_notification( incident.conversation.channel_id, incident_review_document["weblink"]) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"Incident marked as {incident.status}", 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_stable_flow(incident_id: int, command: Optional[dict] = None, db_session=None): """Runs the incident stable flow.""" # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) if incident.status == IncidentStatus.stable: if command: convo_plugin = plugins.get(INCIDENT_PLUGIN_CONVERSATION_SLUG) convo_plugin.send_ephemeral( command["channel_id"], command["user_id"], "Incident Already Stable Notification", blocks=[{ "type": "section", "text": { "type": "plain_text", "text": "The incident is already stable. Aborting command...", }, }], ) return # we update the status of the incident update_incident_status(db_session=db_session, incident=incident, status=IncidentStatus.stable) log.debug( f"We have updated the status of the incident to {IncidentStatus.stable}." ) # we update the incident cost incident_cost = incident_service.calculate_cost(incident_id, db_session) log.debug(f"We have updated the cost of the incident.") # we update the external ticket update_incident_ticket( incident.ticket.resource_id, incident_type=incident.incident_type.name, status=IncidentStatus.stable.lower(), cost=incident_cost, ) log.debug( f"We have updated the status of the external ticket to {IncidentStatus.stable}." ) # we update the conversation topic set_conversation_topic(incident) incident_review_document = get_document( db_session=db_session, incident_id=incident.id, resource_type=INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, ) if not incident_review_document: storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) # we create a copy of the incident review document template and we move it to the incident storage incident_review_document_name = f"{incident.name} - Post Incident Review Document" incident_review_document = storage_plugin.copy_file( team_drive_id=incident.storage.resource_id, file_id=INCIDENT_STORAGE_INCIDENT_REVIEW_FILE_ID, name=incident_review_document_name, ) incident_review_document.update({ "name": incident_review_document_name, "resource_type": INCIDENT_RESOURCE_INCIDENT_REVIEW_DOCUMENT, }) storage_plugin.move_file( new_team_drive_id=incident.storage.resource_id, file_id=incident_review_document["id"]) log.debug( "We have added the incident review document in the incident storage." ) document_in = DocumentCreate( name=incident_review_document["name"], resource_id=incident_review_document["id"], resource_type=incident_review_document["resource_type"], weblink=incident_review_document["weblink"], ) incident.documents.append( document_service.create(db_session=db_session, document_in=document_in)) db_session.add(incident) db_session.commit() log.debug( "We have added the incident review document to the incident.") # we get the incident investigation and faq documents incident_document = get_document( db_session=db_session, incident_id=incident_id, resource_type=INCIDENT_RESOURCE_INVESTIGATION_DOCUMENT, ) # we update the incident review document update_document( incident_review_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("We have updated the incident review document.") # we send a notification about the incident review document to the conversation send_incident_review_document_notification( incident.conversation.channel_id, incident_review_document["weblink"]) log.debug( "We have sent a notification about the incident review document to the conversation." ) # we send the stable notifications send_incident_status_notifications(incident, db_session) log.debug("We have sent the incident stable notifications.")
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 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 create_executive_report(user_id: str, user_email: str, incident_id: int, action: dict, db_session=None): """Creates an executive report.""" report_template = document_service.get_executive_report_template( db_session=db_session) current_date = date.today().strftime("%B %d, %Y") current_status = action["submission"]["current_status"] overview = action["submission"]["overview"] next_steps = action["submission"]["next_steps"] # we load the incident instance incident = incident_service.get(db_session=db_session, incident_id=incident_id) if not report_template: send_feedack_to_user(incident.conversation.channel_id, user_id, "No executive report template defined.") return # we fetch all previous executive reports executive_reports = get_all_by_incident_id_and_type( db_session=db_session, incident_id=incident_id, report_type=ReportTypes.executive_report) previous_executive_reports = [] for executive_report in executive_reports: previous_executive_reports.append( f"{executive_report.document.name} - {executive_report.document.weblink}\n" ) # we create a new executive report details = { "current_status": current_status, "overview": overview, "next_steps": next_steps } executive_report_in = ReportCreate( details=details, type=ReportTypes.executive_report, ) executive_report = create(db_session=db_session, report_in=executive_report_in) # we load the participant participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident_id, email=user_email) # we save the executive report participant.reports.append(executive_report) incident.reports.append(executive_report) db_session.add(participant) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Incident Participant", description= f"{participant.individual.name} created a new executive report", details={ "current_status": current_status, "overview": overview, "next_steps": next_steps }, incident_id=incident_id, individual_id=participant.individual.id, ) # we create a new document for the executive report storage_plugin = plugins.get(INCIDENT_PLUGIN_STORAGE_SLUG) executive_report_document_name = f"{incident.name} - Executive Report - {current_date}" executive_report_document = storage_plugin.copy_file( team_drive_id=incident.storage.resource_id, file_id=report_template.resource_id, name=executive_report_document_name, ) executive_report_document.update({ "name": executive_report_document_name, "resource_type": INCIDENT_RESOURCE_EXECUTIVE_REPORT_DOCUMENT, }) storage_plugin.move_file(new_team_drive_id=incident.storage.resource_id, file_id=executive_report_document["id"]) event_service.log( db_session=db_session, source=storage_plugin.title, description="Executive report document added to storage", incident_id=incident.id, ) document_in = DocumentCreate( name=executive_report_document["name"], resource_id=executive_report_document["id"], resource_type=executive_report_document["resource_type"], weblink=executive_report_document["weblink"], ) executive_report.document = document_service.create( db_session=db_session, document_in=document_in) incident.documents.append(executive_report.document) db_session.add(executive_report) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description="Executive report document added to incident", incident_id=incident.id, ) # we update the incident update document document_plugin = plugins.get(INCIDENT_PLUGIN_DOCUMENT_SLUG) document_plugin.update( executive_report_document["id"], name=incident.name, title=incident.title, current_date=current_date, current_status=current_status, overview=overview, next_steps=next_steps, previous_reports="\n".join(previous_executive_reports), commander_fullname=incident.commander.name, commander_weblink=incident.commander.weblink, ) # we let the user know that the report has been created send_feedack_to_user( incident.conversation.channel_id, user_id, f"The executive report document has been created and can be found in the incident storage here: {executive_report_document['weblink']}", ) # we send the executive report to the notifications group send_executive_report_to_notifications_group(incident_id, executive_report, db_session) # we let the user know that the report has been sent to the notifications group send_feedack_to_user( incident.conversation.channel_id, user_id, f"The executive report has been emailed to the notifications distribution list ({incident.notifications_group.email}).", ) return executive_report