Skip to content

Commit

Permalink
Enable users to migrate a Case thread to a Case dedicated channel. (#…
Browse files Browse the repository at this point in the history
…5207)

* Enable users to migrate a Case thread to a Case dedicated channel.

* Handle migrating dedicated channels from flows.

* Adds Dispatch UI button to create dedicated channel.

* Remove unused import

* Update message.

* Update src/dispatch/plugins/dispatch_slack/case/interactive.py

Co-authored-by: Will Sheldon <[email protected]>

* Remove unnecessary service calls.

* Update src/dispatch/plugins/dispatch_slack/case/messages.py

Co-authored-by: David Whittaker <[email protected]>

* Raise exceptions if conversation creation fails.

* Hide button if conversation channel is created.

* Update conversation thread when migrating conversation.

* Dedicated channels should default to true.

* Update src/dispatch/plugins/dispatch_slack/case/interactive.py

Co-authored-by: Will Sheldon <[email protected]>

* Update src/dispatch/case/flows.py

Co-authored-by: Will Sheldon <[email protected]>

---------

Co-authored-by: Will Sheldon <[email protected]>
Co-authored-by: David Whittaker <[email protected]>
  • Loading branch information
3 people authored Sep 20, 2024
1 parent b0cd984 commit ced74f3
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 47 deletions.
60 changes: 39 additions & 21 deletions src/dispatch/case/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,41 @@ def case_assign_role_flow(
conversation_flows.set_conversation_topic(case, db_session)


def case_create_conversation_flow(
db_session: Session,
case: Case,
participant_emails: list[str],
conversation_target: str | None = None,
) -> None:
"""Runs the case conversation creation flow."""

conversation_flows.create_case_conversation(case, conversation_target, db_session)

event_service.log_case_event(
db_session=db_session,
source="Dispatch Core App",
description="Conversation added to case",
case_id=case.id,
)

for email in participant_emails:
# we don't rely on on this flow to add folks to the conversation because in this case
# we want to do it in bulk
case_add_or_reactivate_participant_flow(
db_session=db_session,
user_email=email,
case_id=case.id,
add_to_conversation=False,
)

# we add the participant to the conversation
conversation_flows.add_case_participants(
case=case,
participant_emails=participant_emails,
db_session=db_session,
)


def case_create_resources_flow(
db_session: Session,
case_id: int,
Expand Down Expand Up @@ -953,32 +988,15 @@ def case_create_resources_flow(
)

try:
# we create the conversation and add participants to the thread
conversation_flows.create_case_conversation(case, conversation_target, db_session)

event_service.log_case_event(
db_session=db_session,
source="Dispatch Core App",
description="Conversation added to case",
case_id=case.id,
)
# wait until all resources are created before adding suggested participants
individual_participants = [x.email for x, _ in individual_participants]

for email in individual_participants:
# we don't rely on on this flow to add folks to the conversation because in this case
# we want to do it in bulk
case_add_or_reactivate_participant_flow(
db_session=db_session,
user_email=email,
case_id=case.id,
add_to_conversation=False,
)
# # we add the participant to the conversation
conversation_flows.add_case_participants(
# we create the conversation and add participants to the thread
case_create_conversation_flow(
db_session=db_session,
case=case,
participant_emails=individual_participants,
db_session=db_session,
conversation_target=conversation_target,
)

for user_email in set(individual_participants):
Expand Down
4 changes: 4 additions & 0 deletions src/dispatch/case/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ def has_thread(self) -> bool:
return False
return True if self.conversation.thread_id else False

@property
def participant_emails(self) -> list:
return [participant.individual.email for participant in self.participants]

@hybrid_property
def total_cost(self):
total_cost = 0
Expand Down
25 changes: 25 additions & 0 deletions src/dispatch/case/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
case_new_create_flow,
case_triage_create_flow,
case_update_flow,
case_create_conversation_flow,
case_create_resources_flow,
get_case_participants_flow,
)
Expand Down Expand Up @@ -188,6 +189,30 @@ def create_case(
return case


@router.post(
"/{case_id}/resources/conversation",
response_model=CaseRead,
summary="Creates conversation channel for an existing case.",
)
def create_case_channel(
db_session: DbSession,
current_case: CurrentCase,
):
"""Creates conversation channel for an existing case."""

current_case.dedicated_channel = True

# Add all case participants to the case channel
case_create_conversation_flow(
db_session=db_session,
case=current_case,
participant_emails=current_case.participant_emails,
conversation_target=None,
)

return current_case


@router.post(
"/{case_id}/resources",
response_model=CaseRead,
Expand Down
120 changes: 104 additions & 16 deletions src/dispatch/conversation/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@
from dispatch.document.models import Document
from dispatch.event import service as event_service
from dispatch.incident.models import Incident
from dispatch.messaging.strings import MessageType
from dispatch.plugin import service as plugin_service
from dispatch.plugins.dispatch_slack.case import messages
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

from .models import Conversation, ConversationCreate
from .service import create
from .models import Conversation, ConversationCreate, ConversationUpdate
from .service import create, update

log = logging.getLogger(__name__)

Expand All @@ -44,6 +46,13 @@ def create_case_conversation(

conversation = None

# Do not overwrite a case conversation with one of the same type (thread, channel)
if case.conversation:
if case.has_channel:
raise RuntimeError("Case already has a dedicated channel conversation.")
if case.has_thread and not case.dedicated_channel:
raise RuntimeError("Case already has a thread conversation.")

# This case is a thread version, we send a new messaged (threaded) to the conversation target
# for the configured case type
if conversation_target and not case.dedicated_channel:
Expand Down Expand Up @@ -73,21 +82,100 @@ def create_case_conversation(

conversation.update({"resource_type": plugin.plugin.slug, "resource_id": conversation["id"]})

conversation_in = ConversationCreate(
resource_id=conversation["resource_id"],
resource_type=conversation["resource_type"],
weblink=conversation["weblink"],
thread_id=conversation.get("timestamp"),
channel_id=conversation["id"],
)
case.conversation = create(db_session=db_session, conversation_in=conversation_in)
if not case.conversation:
conversation_in = ConversationCreate(
resource_id=conversation["resource_id"],
resource_type=conversation["resource_type"],
weblink=conversation["weblink"],
thread_id=conversation.get("timestamp"),
channel_id=conversation["id"],
)
case.conversation = create(db_session=db_session, conversation_in=conversation_in)

event_service.log_case_event(
db_session=db_session,
source=plugin.plugin.title,
description="Case conversation created",
case_id=case.id,
)
event_service.log_case_event(
db_session=db_session,
source=plugin.plugin.title,
description="Case conversation created",
case_id=case.id,
)
elif case.conversation.thread_id and case.dedicated_channel:
thread_conversation_channel_id = case.conversation.channel_id
thread_conversation_thread_id = case.conversation.thread_id
thread_conversation_weblink = case.conversation.weblink

conversation_in = ConversationUpdate(
resource_id=conversation.get("resource_id"),
resource_type=conversation.get("resource_type"),
weblink=conversation.get("weblink"),
thread_id=conversation.get("timestamp"),
channel_id=conversation.get("id"),
)

update(
db_session=db_session, conversation=case.conversation, conversation_in=conversation_in
)

event_service.log_case_event(
db_session=db_session,
source=plugin.plugin.title,
description=f"Case conversation has migrated from thread [{thread_conversation_weblink}] to channel[{case.conversation.weblink}].",
case_id=case.id,
)
try:
plugin.instance.update_thread(
case=case,
conversation_id=thread_conversation_channel_id,
ts=thread_conversation_thread_id,
)
except Exception as e:
event_service.log_subject_event(
subject=case,
db_session=db_session,
source="Dispatch Core App",
description=f"Updating thread message failed. Reason: {e}",
)
log.exception(e)

# Inform users in the case thread that the conversation has migrated to a channel
try:
plugin.instance.send(
thread_conversation_channel_id,
"Notify Case conversation migration",
[],
MessageType.case_notification,
blocks=messages.create_case_thread_migration_message(
channel_weblink=conversation.get("weblink")
),
ts=thread_conversation_thread_id,
)
except Exception as e:
event_service.log_subject_event(
subject=case,
db_session=db_session,
source="Dispatch Core App",
description=f"Failed to send message to original Case thread. Reason: {e}",
)
log.exception(e)

# Provide users in the case channel which thread the conversation originated from.
try:
plugin.instance.send(
case.conversation.channel_id,
"Maintain Case conversation context",
[],
MessageType.case_notification,
blocks=messages.create_case_channel_migration_message(
thread_weblink=thread_conversation_weblink
),
)
except Exception as e:
event_service.log_subject_event(
subject=case,
db_session=db_session,
source="Dispatch Core App",
description=f"Failed to send message to dedicated Case channel. Reason: {e}",
)
log.exception(e)

db_session.add(case)
db_session.commit()
Expand Down
5 changes: 5 additions & 0 deletions src/dispatch/plugins/dispatch_slack/case/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

class CaseNotificationActions(DispatchEnum):
edit = "case-notification-edit"
migrate = "case-notification-migrate"
escalate = "case-notification-escalate"
join_incident = "case-notification-join-incident"
reopen = "case-notification-reopen"
Expand All @@ -30,6 +31,10 @@ class CaseEscalateActions(DispatchEnum):
project_select = "case-notification-escalate-project-select"


class CaseMigrateActions(DispatchEnum):
submit = "case-notification-migrate-submit"


class CaseReportActions(DispatchEnum):
submit = "case-report-submit"
project_select = "case-report-project-select"
Expand Down
Loading

0 comments on commit ced74f3

Please sign in to comment.