Skip to content

Commit

Permalink
Merge branch 'master' into adds-gzip-compression
Browse files Browse the repository at this point in the history
  • Loading branch information
Meandmybadself authored Jun 26, 2024
2 parents bcd00c6 + 50631c6 commit d020bb7
Show file tree
Hide file tree
Showing 27 changed files with 745 additions and 396 deletions.
2 changes: 1 addition & 1 deletion docs/docs/user-guide/incidents/participant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ After an incident is marked stable, Dispatch continues to help with incident man
In addition to Dispatch engaging individuals that will be directly responsible for managing the incident, it provides notifications for general awareness throughout the organization.

:::info
The new incident notification message includes a "Join" button; this allows individuals to add themselves to the incident \(and its resources\) without involvement from the incident commander.
The new incident notification message includes a "Join" button if "Self-Join" is enabled on the project; this allows individuals to add themselves to the incident \(and its resources\) without involvement from the incident commander.
:::

## Self-service engagement
Expand Down
12 changes: 6 additions & 6 deletions requirements-base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ duo-client==5.3.0
# via -r requirements-base.in
ecdsa==0.18.0
# via python-jose
email-validator==2.1.1
email-validator==2.2.0
# via -r requirements-base.in
emails==0.6
# via -r requirements-base.in
Expand All @@ -124,7 +124,7 @@ frozenlist==1.4.1
# aiosignal
google-api-core==2.15.0
# via google-api-python-client
google-api-python-client==2.133.0
google-api-python-client==2.134.0
# via -r requirements-base.in
google-auth==2.26.1
# via
Expand Down Expand Up @@ -242,7 +242,7 @@ oauthlib[signedtoken]==3.2.2
# atlassian-python-api
# jira
# requests-oauthlib
openai==1.34.0
openai==1.35.3
# via -r requirements-base.in
packaging==23.2
# via
Expand Down Expand Up @@ -291,7 +291,7 @@ pyasn1-modules==0.3.0
# oauth2client
pycparser==2.21
# via cffi
pydantic==1.10.15
pydantic==1.10.17
# via
# -r requirements-base.in
# blockkit
Expand Down Expand Up @@ -394,7 +394,7 @@ six==1.16.0
# python-dateutil
# sqlalchemy-filters
# validators
slack-bolt==1.18.1
slack-bolt==1.19.0
# via -r requirements-base.in
slack-sdk==3.29.0
# via
Expand Down Expand Up @@ -444,7 +444,7 @@ statsmodels==0.14.2
# via -r requirements-base.in
tabulate==0.9.0
# via -r requirements-base.in
tenacity==8.3.0
tenacity==8.4.1
# via -r requirements-base.in
text-unidecode==1.3
# via python-slugify
Expand Down
4 changes: 2 additions & 2 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ executing==2.0.1
# stack-data
factory-boy==3.3.0
# via -r requirements-dev.in
faker==25.8.0
faker==25.9.1
# via
# -r requirements-dev.in
# factory-boy
Expand Down Expand Up @@ -86,7 +86,7 @@ python-dateutil==2.9.0.post0
# via faker
pyyaml==6.0.1
# via pre-commit
ruff==0.4.8
ruff==0.4.9
# via -r requirements-dev.in
six==1.16.0
# via
Expand Down
79 changes: 56 additions & 23 deletions src/dispatch/case/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,30 +313,32 @@ def case_update_flow(
*,
case_id: int,
previous_case: CaseRead,
reporter_email: str,
assignee_email: str,
reporter_email: str | None,
assignee_email: str | None,
organization_slug: OrganizationSlug,
db_session=None,
):
"""Runs the case update flow."""
# we get the case
case = get(db_session=db_session, case_id=case_id)

# we run the case assign role flow for the reporter
case_assign_role_flow(
case_id=case.id,
participant_email=reporter_email,
participant_role=ParticipantRoleType.reporter,
db_session=db_session,
)
if reporter_email:
# we run the case assign role flow for the reporter
case_assign_role_flow(
case_id=case.id,
participant_email=reporter_email,
participant_role=ParticipantRoleType.reporter,
db_session=db_session,
)

# we run the case assign role flow for the assignee
case_assign_role_flow(
case_id=case.id,
participant_email=assignee_email,
participant_role=ParticipantRoleType.assignee,
db_session=db_session,
)
if assignee_email:
# we run the case assign role flow for the assignee
case_assign_role_flow(
case_id=case.id,
participant_email=assignee_email,
participant_role=ParticipantRoleType.assignee,
db_session=db_session,
)

# we run the transition flow based on the current and previous status of the case
case_status_transition_flow_dispatcher(
Expand All @@ -358,20 +360,28 @@ def case_update_flow(

if case.tactical_group:
# we update the tactical group
for group_member in [reporter_email, assignee_email]:
if reporter_email:
group_flows.update_group(
subject=case,
group=case.tactical_group,
group_action=GroupAction.add_member,
group_member=reporter_email,
db_session=db_session,
)
if assignee_email:
group_flows.update_group(
subject=case,
group=case.tactical_group,
group_action=GroupAction.add_member,
group_member=group_member,
group_member=assignee_email,
db_session=db_session,
)

if case.conversation and case.has_thread:
# we send the case updated notification
update_conversation(case, db_session)

if case.has_channel and case.status != CaseStatus.closed:
if case.has_channel and not case.has_thread and case.status != CaseStatus.closed:
# determine if case channel topic needs to be updated
if case_details_changed(case, previous_case):
conversation_flows.set_conversation_topic(case, db_session)
Expand Down Expand Up @@ -646,12 +656,35 @@ def common_escalate_flow(

# we add the case participants to the incident
for participant in case.participants:
conversation_flows.add_incident_participants(
db_session=db_session,
incident=incident,
participant_emails=[participant.individual.email],
# check to see if already a participant in the incident
incident_participant = participant_service.get_by_incident_id_and_email(
db_session=db_session, incident_id=incident.id, email=participant.individual.email
)

if not incident_participant:
log.info(
f"Adding participant {participant.individual.email} from Case {case.id} to Incident {incident.id}"
)
# Get the roles for this participant
case_roles = participant.participant_roles

# Map the case role to an incident role
incident_role = ParticipantRoleType.map_case_role_to_incident_role(case_roles)

participant_flows.add_participant(
participant.individual.email,
incident,
db_session,
role=incident_role,
)

# We add the participants to the conversation
conversation_flows.add_incident_participants_to_conversation(
db_session=db_session,
incident=incident,
participant_emails=[participant.individual.email],
)

if case.has_channel:
# depends on `incident_create_flow()` (we need incident.name), so we invoke after we call it
send_escalation_messages_for_channel_case(
Expand Down
70 changes: 68 additions & 2 deletions src/dispatch/conversation/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from dispatch.plugin import service as plugin_service
from dispatch.storage.models import Storage
from dispatch.ticket.models import Ticket
from dispatch.service.models import Service
from dispatch.project.models import Project
from dispatch.utils import deslug_and_capitalize_resource_type
from dispatch.types import Subject

Expand Down Expand Up @@ -215,7 +217,7 @@ def get_topic_text(subject: Subject) -> str:
)


def set_conversation_topic(subject: Subject, db_session: SessionLocal):
def set_conversation_topic(subject: Subject, db_session: Session) -> None:
"""Sets the conversation topic."""
if not subject.conversation:
log.warning("Conversation topic not set. No conversation available for this incident/case.")
Expand All @@ -242,6 +244,66 @@ def set_conversation_topic(subject: Subject, db_session: SessionLocal):
log.exception(e)


def get_current_oncall_email(project: Project, service: Service, db_session: Session) -> str | None:
"""Notifies oncall about completed form"""
oncall_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=project.id, plugin_type="oncall"
)
if not oncall_plugin:
log.debug("Unable to send email since oncall plugin is not active.")
else:
return oncall_plugin.instance.get(service.external_id)


def get_description_text(subject: Subject, db_session: Session) -> str | None:
"""Returns the description details based on the subject"""
if not isinstance(subject, Incident):
return

incident_type = subject.incident_type
if not incident_type.channel_description:
return

description_service = incident_type.description_service
if description_service:
oncall_email = get_current_oncall_email(
project=subject.project, service=description_service, db_session=db_session
)
if oncall_email:
return incident_type.channel_description.replace("{oncall_email}", oncall_email)

return incident_type.channel_description


def set_conversation_description(subject: Subject, db_session: Session) -> None:
"""Sets the conversation description."""
if not subject.conversation:
log.warning("Conversation topic not set. No conversation available for this incident/case.")
return

plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=subject.project.id, plugin_type="conversation"
)
if not plugin:
log.warning("Conversation topic not set. No conversation plugin enabled.")
return

conversation_description = get_description_text(subject, db_session)
if not conversation_description:
return

try:
plugin.instance.set_description(subject.conversation.channel_id, conversation_description)
except Exception as e:
event_service.log_subject_event(
subject=subject,
db_session=db_session,
source="Dispatch Core App",
description=f"Setting the incident/case conversation description failed. Reason: {e}",
)
log.exception(e)


def add_conversation_bookmark(
db_session: Session,
subject: Subject,
Expand Down Expand Up @@ -327,7 +389,7 @@ def add_case_participants(
log.exception(e)


def add_incident_participants(
def add_incident_participants_to_conversation(
incident: Incident,
participant_emails: list[str],
db_session: Session,
Expand Down Expand Up @@ -358,6 +420,10 @@ def add_incident_participants(
incident_id=incident.id,
)
log.exception(e)
else:
log.info(
f"Add participants {str(participant_emails)} to Incident {incident.id} successfully"
)


def delete_conversation(conversation: Conversation, project_id: int, db_session: SessionLocal):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Create new channel description and description service id columns for incident type
Revision ID: 4286dcce0a2d
Revises: a836d4850a75
Create Date: 2024-06-12 17:45:25.556120
"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "4286dcce0a2d"
down_revision = "a836d4850a75"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("incident_type", sa.Column("channel_description", sa.String(), nullable=True))
op.add_column("incident_type", sa.Column("description_service_id", sa.Integer(), nullable=True))
op.create_foreign_key("description_service_id_fkey", "incident_type", "service", ["description_service_id"], ["id"])
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint("description_service_id_fkey", "incident_type", type_="foreignkey")
op.drop_column("incident_type", "description_service_id")
op.drop_column("incident_type", "channel_description")
# ### end Alembic commands ###
Loading

0 comments on commit d020bb7

Please sign in to comment.