From 5a3fcbdb817cd536b5491e63ca74a0628ea877d0 Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Wed, 16 Oct 2024 13:03:58 -0700
Subject: [PATCH 01/19] Adding docker type to github utils (#5348)
---
utils/github_utils.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/utils/github_utils.py b/utils/github_utils.py
index a9534cb42c7b..837ab78fbe12 100755
--- a/utils/github_utils.py
+++ b/utils/github_utils.py
@@ -99,6 +99,7 @@ def release_notes(pull_request_number: int) -> NoReturn:
"techdebt": "",
"tests": "",
"improvement": "",
+ "docker": "",
}
click.echo(f"Fetching list of merged PRs since #{pull_request_number}...")
From 1a523665b46cd2f760da16a4db7317d02df65744 Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Wed, 16 Oct 2024 13:07:30 -0700
Subject: [PATCH 02/19] Allow list of strings in plugin metadata (#5350)
---
src/dispatch/plugin/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dispatch/plugin/models.py b/src/dispatch/plugin/models.py
index 1f043fff5d19..e7a6b3760ff1 100644
--- a/src/dispatch/plugin/models.py
+++ b/src/dispatch/plugin/models.py
@@ -219,7 +219,7 @@ class PluginInstanceUpdate(PluginBase):
class KeyValue(DispatchBase):
key: str
- value: str
+ value: str | List[str]
class PluginMetadata(DispatchBase):
From 581235c1dcfd652e3e49016000ddf1f8351d4ead Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Wed, 16 Oct 2024 13:38:24 -0700
Subject: [PATCH 03/19] Only collect participant emails if individual exists
(#5351)
---
src/dispatch/search/views.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/dispatch/search/views.py b/src/dispatch/search/views.py
index 271f39b07bc0..bd2dc101acdf 100644
--- a/src/dispatch/search/views.py
+++ b/src/dispatch/search/views.py
@@ -40,7 +40,9 @@ def search(
current_user_email = common["current_user"].email
for incident in results["Incident"]:
participant_emails: list[str] = [
- participant.individual.email for participant in incident.participants
+ participant.individual.email
+ for participant in incident.participants
+ if participant.individual
]
if (
incident.project in admin_projects
From 8efea5c48f93500a1c8b6af2f375735737fa57b0 Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Wed, 16 Oct 2024 15:35:23 -0700
Subject: [PATCH 04/19] Add Dispatch link to Jira ticket (#5352)
---
src/dispatch/plugins/dispatch_core/plugin.py | 1 +
src/dispatch/plugins/dispatch_jira/plugin.py | 8 +++
.../plugins/dispatch_jira/templates.py | 2 +
src/dispatch/ticket/flows.py | 53 ++++++++++---------
4 files changed, 39 insertions(+), 25 deletions(-)
diff --git a/src/dispatch/plugins/dispatch_core/plugin.py b/src/dispatch/plugins/dispatch_core/plugin.py
index 84593ce87b65..2dbeb354a615 100644
--- a/src/dispatch/plugins/dispatch_core/plugin.py
+++ b/src/dispatch/plugins/dispatch_core/plugin.py
@@ -258,6 +258,7 @@ def update_case_ticket(
# reporter_email: str,
document_weblink: str,
storage_weblink: str,
+ dispatch_weblink: str,
case_type_plugin_metadata: dict = None,
):
"""Updates a Dispatch case ticket."""
diff --git a/src/dispatch/plugins/dispatch_jira/plugin.py b/src/dispatch/plugins/dispatch_jira/plugin.py
index 42cde3f13a0b..8e427d20cec4 100644
--- a/src/dispatch/plugins/dispatch_jira/plugin.py
+++ b/src/dispatch/plugins/dispatch_jira/plugin.py
@@ -155,6 +155,7 @@ def create_incident_issue_fields(
document_weblink: str,
storage_weblink: str,
conference_weblink: str,
+ dispatch_weblink: str,
cost: float,
):
"""Creates Jira issue fields."""
@@ -192,6 +193,7 @@ def create_incident_issue_fields(
conference_weblink=conference_weblink,
conversation_weblink=conversation_weblink,
storage_weblink=storage_weblink,
+ dispatch_weblink=dispatch_weblink,
)
issue_fields.update({"description": description})
@@ -210,6 +212,7 @@ def create_case_issue_fields(
assignee_username: str,
document_weblink: str,
storage_weblink: str,
+ dispatch_weblink: str,
):
"""Creates Jira issue fields."""
issue_fields = {}
@@ -226,6 +229,7 @@ def create_case_issue_fields(
document_weblink=document_weblink,
resolution=resolution,
storage_weblink=storage_weblink,
+ dispatch_weblink=dispatch_weblink,
)
issue_fields.update({"description": description})
@@ -329,6 +333,7 @@ def update(
document_weblink: str,
storage_weblink: str,
conference_weblink: str,
+ dispatch_weblink: str,
cost: float,
incident_type_plugin_metadata: dict = None,
):
@@ -357,6 +362,7 @@ def update(
document_weblink=document_weblink,
storage_weblink=storage_weblink,
conference_weblink=conference_weblink,
+ dispatch_weblink=dispatch_weblink,
cost=cost,
)
@@ -481,6 +487,7 @@ def update_case_ticket(
# reporter_email: str,
document_weblink: str,
storage_weblink: str,
+ dispatch_weblink: str,
case_type_plugin_metadata: dict = None,
):
"""Updates a case Jira issue."""
@@ -506,6 +513,7 @@ def update_case_ticket(
assignee_username=assignee_username,
document_weblink=document_weblink,
storage_weblink=storage_weblink,
+ dispatch_weblink=dispatch_weblink,
)
return update(self.configuration, client, issue, issue_fields, status)
diff --git a/src/dispatch/plugins/dispatch_jira/templates.py b/src/dispatch/plugins/dispatch_jira/templates.py
index b652ccee6cdb..1437478766fa 100644
--- a/src/dispatch/plugins/dispatch_jira/templates.py
+++ b/src/dispatch/plugins/dispatch_jira/templates.py
@@ -9,6 +9,7 @@
Cost: {{cost}}
*Incident Resources*
+[Dispatch Link|{{dispatch_weblink}}]
[Conversation|{{conversation_weblink}}]
[Investigation Document|{{document_weblink}}]
[Storage|{{storage_weblink}}]
@@ -41,6 +42,7 @@
Priority: {{case_priority}}
*Case Resources*
+[Dispatch Link|{{dispatch_weblink}}]
[Investigation Document|{{document_weblink}}]
[Storage|{{storage_weblink}}]
diff --git a/src/dispatch/ticket/flows.py b/src/dispatch/ticket/flows.py
index 9b6c045e8594..a506e478bcb2 100644
--- a/src/dispatch/ticket/flows.py
+++ b/src/dispatch/ticket/flows.py
@@ -13,6 +13,7 @@
from dispatch.participant import service as participant_service
from dispatch.plugin import service as plugin_service
from dispatch.task.models import Task
+from dispatch.config import DISPATCH_UI_URL
from .models import Ticket, TicketCreate
from .service import create
@@ -111,20 +112,21 @@ def update_incident_ticket(
# we update the external incident ticket
try:
plugin.instance.update(
- incident.ticket.resource_id,
- title,
- description,
- incident.incident_type.name,
- incident.incident_severity.name,
- incident.incident_priority.name,
- incident.status.lower(),
- incident.commander.individual.email,
- incident.reporter.individual.email,
- resolve_attr(incident, "conversation.weblink"),
- resolve_attr(incident, "incident_document.weblink"),
- resolve_attr(incident, "storage.weblink"),
- resolve_attr(incident, "conference.weblink"),
- total_cost,
+ ticket_id=incident.ticket.resource_id,
+ title=title,
+ description=description,
+ incident_type=incident.incident_type.name,
+ incident_severity=incident.incident_severity.name,
+ incident_priority=incident.incident_priority.name,
+ status=incident.status.lower(),
+ commander_email=incident.commander.individual.email,
+ reporter_email=incident.reporter.individual.email,
+ conversation_weblink=resolve_attr(incident, "conversation.weblink"),
+ document_weblink=resolve_attr(incident, "incident_document.weblink"),
+ storage_weblink=resolve_attr(incident, "storage.weblink"),
+ conference_weblink=resolve_attr(incident, "conference.weblink"),
+ dispatch_weblink=f"{DISPATCH_UI_URL}/{incident.project.organization.slug}/incidents/{incident.name}",
+ cost=total_cost,
incident_type_plugin_metadata=incident_type_plugin_metadata,
)
except Exception as e:
@@ -230,17 +232,18 @@ def update_case_ticket(
# we update the external case ticket
try:
plugin.instance.update_case_ticket(
- case.ticket.resource_id,
- title,
- description,
- case.resolution,
- case.case_type.name,
- case.case_severity.name,
- case.case_priority.name,
- case.status.lower(),
- case.assignee.individual.email,
- case_document_weblink,
- case_storage_weblink,
+ ticket_id=case.ticket.resource_id,
+ title=title,
+ description=description,
+ resolution=case.resolution,
+ case_type=case.case_type.name,
+ case_severity=case.case_severity.name,
+ case_priority=case.case_priority.name,
+ status=case.status.lower(),
+ assignee_email=case.assignee.individual.email,
+ document_weblink=case_document_weblink,
+ storage_weblink=case_storage_weblink,
+ dispatch_weblink=f"{DISPATCH_UI_URL}/{case.project.organization.slug}/cases/{case.name}",
case_type_plugin_metadata=case_type_plugin_metadata,
)
except Exception as e:
From e08bb05899e9d97b8ebaa4536827bd7b7ee7efe6 Mon Sep 17 00:00:00 2001
From: Marc Vilanova <39573146+mvilanova@users.noreply.github.com>
Date: Thu, 17 Oct 2024 14:16:20 -0700
Subject: [PATCH 05/19] Checks if the message is a dict before saving it to the
genai analysis field in the case model (#5357)
---
src/dispatch/plugins/dispatch_slack/plugin.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/dispatch/plugins/dispatch_slack/plugin.py b/src/dispatch/plugins/dispatch_slack/plugin.py
index dd93e2c22c7a..e2968d94cd48 100644
--- a/src/dispatch/plugins/dispatch_slack/plugin.py
+++ b/src/dispatch/plugins/dispatch_slack/plugin.py
@@ -107,7 +107,9 @@ def create_threaded(self, case: Case, conversation_id: str, db_session: Session)
client=client,
config=self.configuration,
)
- if message:
+ if message and isinstance(message, dict):
+ # we update the genai_analysis field in the case model with the message if it's a dict
+ # if the message is a string, it means there was an error generating the analysis
case.genai_analysis = message
if message_blocks:
From a9a382b97c025a680e22d2d88792e0c187e685cf Mon Sep 17 00:00:00 2001
From: Marc Vilanova <39573146+mvilanova@users.noreply.github.com>
Date: Fri, 18 Oct 2024 11:01:23 -0700
Subject: [PATCH 06/19] Handles JSONDecodeError exception when parsing GenAI
JSON string (#5359)
---
.../plugins/dispatch_slack/case/messages.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/src/dispatch/plugins/dispatch_slack/case/messages.py b/src/dispatch/plugins/dispatch_slack/case/messages.py
index 8d3bb8dc6e70..88c85a0756db 100644
--- a/src/dispatch/plugins/dispatch_slack/case/messages.py
+++ b/src/dispatch/plugins/dispatch_slack/case/messages.py
@@ -462,12 +462,18 @@ def create_genai_signal_analysis_message(
"""
)
- message = json.loads(
- response["choices"][0]["message"]["content"]
- .replace("```json", "")
- .replace("```", "")
- .strip()
- )
+
+ try:
+ message = json.loads(
+ response["choices"][0]["message"]["content"]
+ .replace("```json", "")
+ .replace("```", "")
+ .strip()
+ )
+ except json.JSONDecodeError as e:
+ message = "Unable to generate GenAI signal analysis. Error decoding response from the artificial-intelligence plugin."
+ log.warning(f"{message} Error: {e}")
+ return message, create_genai_signal_message_metadata_blocks(signal_metadata_blocks, message)
# we check if the response is empty
if not message:
From d648a960c9ad21bfa6172dfb95bafb338438570e Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Fri, 18 Oct 2024 11:54:20 -0700
Subject: [PATCH 07/19] Fixing linting errors (#5360)
---
.../dispatch/src/components/GenaiAnalysisDisplay.vue | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue b/src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue
index 5e34c54d1474..34bf59cc0f72 100644
--- a/src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue
+++ b/src/dispatch/static/dispatch/src/components/GenaiAnalysisDisplay.vue
@@ -8,13 +8,9 @@
-
+
-
+
From 1f34f83be56fff45db1afd84f290f6b22a887d0e Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:14:28 -0700
Subject: [PATCH 08/19] feat(case): adding case feedback (#5356)
---
src/dispatch/case/flows.py | 6 +
src/dispatch/case/messaging.py | 42 +++++++
src/dispatch/case/models.py | 2 +
src/dispatch/conversation/enums.py | 1 +
.../versions/2024-10-16_3c49f62d7914.py | 63 ++++++++++
src/dispatch/database/service.py | 7 +-
src/dispatch/feedback/incident/models.py | 19 +--
src/dispatch/feedback/incident/service.py | 23 +++-
src/dispatch/messaging/strings.py | 19 +++
.../plugins/dispatch_slack/feedback/enums.py | 17 +++
.../dispatch_slack/feedback/interactive.py | 114 +++++++++++++++++-
.../dispatch/src/feedback/incident/Table.vue | 21 +++-
.../feedback/incident/TableFilterDialog.vue | 10 +-
.../dispatch/src/feedback/incident/store.js | 2 +
.../static/dispatch/src/router/config.js | 2 +-
15 files changed, 325 insertions(+), 23 deletions(-)
create mode 100644 src/dispatch/database/revisions/tenant/versions/2024-10-16_3c49f62d7914.py
diff --git a/src/dispatch/case/flows.py b/src/dispatch/case/flows.py
index b802e841632d..21aa8d517b4f 100644
--- a/src/dispatch/case/flows.py
+++ b/src/dispatch/case/flows.py
@@ -37,6 +37,7 @@
from .messaging import (
send_case_created_notifications,
send_case_update_notifications,
+ send_case_rating_feedback_message,
)
from .models import Case, CaseStatus
@@ -498,6 +499,11 @@ def case_closed_status_flow(case: Case, db_session=None):
for document in case.documents:
document_flows.mark_document_as_readonly(document=document, db_session=db_session)
+ if case.dedicated_channel:
+ # we send a direct message to all participants asking them
+ # to rate and provide feedback about the case
+ send_case_rating_feedback_message(case, db_session)
+
def reactivate_case_participants(case: Case, db_session: Session):
"""Reactivates all case participants."""
diff --git a/src/dispatch/case/messaging.py b/src/dispatch/case/messaging.py
index a030b6903eae..d223a03d37b7 100644
--- a/src/dispatch/case/messaging.py
+++ b/src/dispatch/case/messaging.py
@@ -24,6 +24,7 @@
CASE_TYPE_CHANGE,
CASE_SEVERITY_CHANGE,
CASE_PRIORITY_CHANGE,
+ CASE_CLOSED_RATING_FEEDBACK_NOTIFICATION,
MessageType,
)
from dispatch.config import DISPATCH_UI_URL
@@ -330,3 +331,44 @@ def send_case_welcome_participant_message(
)
log.debug(f"Welcome ephemeral message sent to {participant_email}.")
+
+
+def send_case_rating_feedback_message(case: Case, db_session: Session):
+ """
+ Sends a direct message to all case participants asking
+ them to rate and provide feedback about the case.
+ """
+ notification_text = "Case Rating and Feedback"
+ notification_template = CASE_CLOSED_RATING_FEEDBACK_NOTIFICATION
+
+ plugin = plugin_service.get_active_instance(
+ db_session=db_session, project_id=case.project.id, plugin_type="conversation"
+ )
+ if not plugin:
+ log.warning("Case rating and feedback message not sent, no conversation plugin enabled.")
+ return
+
+ items = [
+ {
+ "case_id": case.id,
+ "organization_slug": case.project.organization.slug,
+ "name": case.name,
+ "title": case.title,
+ "ticket_weblink": case.ticket.weblink,
+ }
+ ]
+
+ for participant in case.participants:
+ try:
+ plugin.instance.send_direct(
+ participant.individual.email,
+ notification_text,
+ notification_template,
+ MessageType.case_rating_feedback,
+ items=items,
+ )
+ except Exception as e:
+ # if one fails we don't want all to fail
+ log.exception(e)
+
+ log.debug("Case rating and feedback message sent to all participants.")
diff --git a/src/dispatch/case/models.py b/src/dispatch/case/models.py
index 0559664e6312..9065db786413 100644
--- a/src/dispatch/case/models.py
+++ b/src/dispatch/case/models.py
@@ -128,6 +128,8 @@ class Case(Base, TimeStampMixin, ProjectMixin):
events = relationship("Event", backref="case", cascade="all, delete-orphan")
+ feedback = relationship("Feedback", backref="case", cascade="all, delete-orphan")
+
groups = relationship(
"Group", backref="case", cascade="all, delete-orphan", foreign_keys=[Group.case_id]
)
diff --git a/src/dispatch/conversation/enums.py b/src/dispatch/conversation/enums.py
index 5467a17c30be..643d4767bd7a 100644
--- a/src/dispatch/conversation/enums.py
+++ b/src/dispatch/conversation/enums.py
@@ -14,6 +14,7 @@ class ConversationCommands(DispatchEnum):
class ConversationButtonActions(DispatchEnum):
feedback_notification_provide = "feedback-notification-provide"
+ case_feedback_notification_provide = "case-feedback-notification-provide"
invite_user = "invite-user"
invite_user_case = "invite-user-case"
monitor_link = "monitor-link"
diff --git a/src/dispatch/database/revisions/tenant/versions/2024-10-16_3c49f62d7914.py b/src/dispatch/database/revisions/tenant/versions/2024-10-16_3c49f62d7914.py
new file mode 100644
index 000000000000..caad8de0efa5
--- /dev/null
+++ b/src/dispatch/database/revisions/tenant/versions/2024-10-16_3c49f62d7914.py
@@ -0,0 +1,63 @@
+"""Adds case_id and project_id to feedback
+
+Revision ID: 3c49f62d7914
+Revises: b8c1a8a4d957
+Create Date: 2024-10-16 15:21:17.120891
+
+"""
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.orm import Session
+from dispatch.feedback.incident.models import Feedback
+
+# revision identifiers, used by Alembic.
+revision = "3c49f62d7914"
+down_revision = "b8c1a8a4d957"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column("feedback", sa.Column("case_id", sa.Integer(), nullable=True))
+ op.create_foreign_key(
+ "assoc_feedback_case_id_fkey",
+ "feedback",
+ "case",
+ ["case_id"],
+ ["id"],
+ ondelete="CASCADE",
+ )
+ op.add_column("feedback", sa.Column("project_id", sa.Integer(), nullable=True))
+ op.create_foreign_key(
+ "assoc_feedback_project_id_fkey",
+ "feedback",
+ "project",
+ ["project_id"],
+ ["id"],
+ ondelete="CASCADE",
+ )
+
+ bind = op.get_bind()
+ session = Session(bind=bind)
+
+ instances = session.query(Feedback).all()
+
+ for instance in instances:
+ if instance.incident:
+ instance.project_id = instance.incident.project_id
+ elif instance.case:
+ instance.project_id = instance.case.project_id
+
+ session.commit()
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_constraint("assoc_feedback_case_id_fkey", "feedback", type_="foreignkey")
+ op.drop_column("feedback", "case_id")
+ op.drop_constraint("assoc_feedback_project_id_fkey", "feedback", type_="foreignkey")
+ op.drop_column("feedback", "project_id")
+ # ### end Alembic commands ###
diff --git a/src/dispatch/database/service.py b/src/dispatch/database/service.py
index 1d7728358df8..209a3735b86f 100644
--- a/src/dispatch/database/service.py
+++ b/src/dispatch/database/service.py
@@ -185,9 +185,8 @@ def build_filters(filter_spec):
if not _is_iterable_filter(fn_args):
raise BadFilterFormat(
- "`{}` value must be an iterable across the function " "arguments".format(
- boolean_function.key
- )
+ "`{}` value must be an iterable across the function "
+ "arguments".format(boolean_function.key)
)
if boolean_function.only_one_arg and len(fn_args) != 1:
raise BadFilterFormat(
@@ -347,8 +346,8 @@ def apply_filter_specific_joins(model: Base, filter_spec: dict, query: orm.query
# this is required because by default sqlalchemy-filter's auto-join
# knows nothing about how to join many-many relationships.
model_map = {
- (Feedback, "Project"): (Incident, False),
(Feedback, "Incident"): (Incident, False),
+ (Feedback, "Case"): (Case, False),
(Task, "Project"): (Incident, False),
(Task, "Incident"): (Incident, False),
(Task, "IncidentPriority"): (Incident, False),
diff --git a/src/dispatch/feedback/incident/models.py b/src/dispatch/feedback/incident/models.py
index ea320894e424..a6389093c2f7 100644
--- a/src/dispatch/feedback/incident/models.py
+++ b/src/dispatch/feedback/incident/models.py
@@ -3,24 +3,32 @@
from typing import Optional, List
from sqlalchemy import Column, Integer, ForeignKey
-from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy_utils import TSVectorType
from dispatch.database.core import Base
from dispatch.incident.models import IncidentReadMinimal
-from dispatch.models import DispatchBase, TimeStampMixin, FeedbackMixin, PrimaryKey, Pagination
+from dispatch.models import (
+ DispatchBase,
+ TimeStampMixin,
+ FeedbackMixin,
+ PrimaryKey,
+ Pagination,
+ ProjectMixin,
+)
from dispatch.participant.models import ParticipantRead
from dispatch.project.models import ProjectRead
+from dispatch.case.models import CaseReadMinimal
from .enums import FeedbackRating
-class Feedback(TimeStampMixin, FeedbackMixin, Base):
+class Feedback(TimeStampMixin, FeedbackMixin, ProjectMixin, Base):
# Columns
id = Column(Integer, primary_key=True)
# Relationships
incident_id = Column(Integer, ForeignKey("incident.id", ondelete="CASCADE"))
+ case_id = Column(Integer, ForeignKey("case.id", ondelete="CASCADE"))
participant_id = Column(Integer, ForeignKey("participant.id"))
search_vector = Column(
@@ -31,10 +39,6 @@ class Feedback(TimeStampMixin, FeedbackMixin, Base):
)
)
- @hybrid_property
- def project(self):
- return self.incident.project
-
# Pydantic models
class FeedbackBase(DispatchBase):
@@ -42,6 +46,7 @@ class FeedbackBase(DispatchBase):
rating: FeedbackRating = FeedbackRating.very_satisfied
feedback: Optional[str] = Field(None, nullable=True)
incident: Optional[IncidentReadMinimal]
+ case: Optional[CaseReadMinimal]
participant: Optional[ParticipantRead]
diff --git a/src/dispatch/feedback/incident/service.py b/src/dispatch/feedback/incident/service.py
index 48594f002251..fd80be90909a 100644
--- a/src/dispatch/feedback/incident/service.py
+++ b/src/dispatch/feedback/incident/service.py
@@ -2,6 +2,7 @@
from datetime import datetime, timedelta
from dispatch.incident import service as incident_service
+from dispatch.case import service as case_service
from dispatch.incident.models import Incident
from dispatch.project.models import Project
@@ -34,13 +35,25 @@ def get_all_last_x_hours_by_project_id(
def create(*, db_session, feedback_in: FeedbackCreate) -> Feedback:
"""Creates a new piece of feedback."""
- incident = incident_service.get(
- db_session=db_session,
- incident_id=feedback_in.incident.id,
- )
+ if feedback_in.incident:
+ incident = incident_service.get(
+ db_session=db_session,
+ incident_id=feedback_in.incident.id,
+ )
+ project = incident.project
+ case = None
+ else:
+ case = case_service.get(
+ db_session=db_session,
+ case_id=feedback_in.case.id,
+ )
+ project = case.project
+ incident = None
feedback = Feedback(
- **feedback_in.dict(exclude={"incident"}),
+ **feedback_in.dict(exclude={"incident", "case", "project"}),
incident=incident,
+ case=case,
+ project=project,
)
db_session.add(feedback)
db_session.commit()
diff --git a/src/dispatch/messaging/strings.py b/src/dispatch/messaging/strings.py
index 8908b4ad2563..70e0353d979d 100644
--- a/src/dispatch/messaging/strings.py
+++ b/src/dispatch/messaging/strings.py
@@ -42,6 +42,7 @@ class MessageType(DispatchEnum):
case_status_reminder = "case-status-reminder"
service_feedback = "service-feedback"
task_add_to_incident = "task-add-to-incident"
+ case_rating_feedback = "case-rating-feedback"
INCIDENT_STATUS_DESCRIPTIONS = {
@@ -373,6 +374,9 @@ class MessageType(DispatchEnum):
INCIDENT_CLOSED_RATING_FEEDBACK_DESCRIPTION = """
Thanks for participating in the {{name}} ("{{title}}") incident. We would appreciate if you could rate your experience and provide feedback."""
+CASE_CLOSED_RATING_FEEDBACK_DESCRIPTION = """
+Thanks for participating in the {{name}} ("{{title}}") case. We would appreciate if you could rate your experience and provide feedback."""
+
INCIDENT_MANAGEMENT_HELP_TIPS_MESSAGE_DESCRIPTION = """
Hey, I see you're the Incident Commander for <{{conversation_weblink}}|{{name}}> ("{{title}}"). Here are a few things to consider when managing the incident:
\n • Keep the incident and its status up to date using the Slack `{{update_command}}` command.
@@ -971,6 +975,21 @@ class MessageType(DispatchEnum):
}
]
+CASE_CLOSED_RATING_FEEDBACK_NOTIFICATION = [
+ {
+ "title": "{{name}} Case - Rating and Feedback",
+ "title_link": "{{ticket_weblink}}",
+ "text": CASE_CLOSED_RATING_FEEDBACK_DESCRIPTION,
+ "buttons": [
+ {
+ "button_text": "Provide Feedback",
+ "button_value": "{{organization_slug}}-{{case_id}}",
+ "button_action": ConversationButtonActions.case_feedback_notification_provide,
+ }
+ ],
+ }
+]
+
INCIDENT_FEEDBACK_DAILY_REPORT = [
{"title": "Incident", "text": "{{ name }}"},
{"title": "Incident Title", "text": "{{ title }}"},
diff --git a/src/dispatch/plugins/dispatch_slack/feedback/enums.py b/src/dispatch/plugins/dispatch_slack/feedback/enums.py
index 5fc94ba386d4..162e8f839480 100644
--- a/src/dispatch/plugins/dispatch_slack/feedback/enums.py
+++ b/src/dispatch/plugins/dispatch_slack/feedback/enums.py
@@ -8,17 +8,34 @@ class IncidentFeedbackNotificationBlockIds(DispatchEnum):
anonymous_checkbox = "incident-feedback-notification-anonymous-checkbox"
+class CaseFeedbackNotificationBlockIds(DispatchEnum):
+ feedback_input = "case-feedback-notification-feedback-input"
+ rating_select = "case-feedback-notification-rating-select"
+ anonymous_checkbox = "case-feedback-notification-anonymous-checkbox"
+
+
class IncidentFeedbackNotificationActionIds(DispatchEnum):
feedback_input = "incident-feedback-notification-feedback-input"
rating_select = "incident-feedback-notification-rating-select"
anonymous_checkbox = "incident-feedback-notification-anonymous-checkbox"
+class CaseFeedbackNotificationActionIds(DispatchEnum):
+ feedback_input = "case-feedback-notification-feedback-input"
+ rating_select = "case-feedback-notification-rating-select"
+ anonymous_checkbox = "case-feedback-notification-anonymous-checkbox"
+
+
class IncidentFeedbackNotificationActions(DispatchEnum):
submit = "incident-feedback-notification-submit"
provide = ConversationButtonActions.feedback_notification_provide
+class CaseFeedbackNotificationActions(DispatchEnum):
+ submit = "case-feedback-notification-submit"
+ provide = ConversationButtonActions.case_feedback_notification_provide
+
+
class ServiceFeedbackNotificationBlockIds(DispatchEnum):
anonymous_checkbox = "service-feedback-notification-anonymous-checkbox"
feedback_input = "service-feedback-notification-feedback-input"
diff --git a/src/dispatch/plugins/dispatch_slack/feedback/interactive.py b/src/dispatch/plugins/dispatch_slack/feedback/interactive.py
index a9d1df5eadbc..258fc6fb7840 100644
--- a/src/dispatch/plugins/dispatch_slack/feedback/interactive.py
+++ b/src/dispatch/plugins/dispatch_slack/feedback/interactive.py
@@ -17,13 +17,14 @@
from datetime import datetime
from dispatch.auth.models import DispatchUser
-from dispatch.feedback.incident import service as incident_feedback_service
+from dispatch.feedback.incident import service as subject_feedback_service
from dispatch.feedback.incident.enums import FeedbackRating
from dispatch.feedback.incident.models import FeedbackCreate
from dispatch.feedback.service import service as feedback_service
from dispatch.individual import service as individual_service
from dispatch.feedback.service.models import ServiceFeedbackRating, ServiceFeedbackCreate
from dispatch.incident import service as incident_service
+from dispatch.case import service as case_service
from dispatch.participant import service as participant_service
from dispatch.feedback.service.reminder import service as reminder_service
from dispatch.plugin import service as plugin_service
@@ -45,6 +46,9 @@
ServiceFeedbackNotificationActionIds,
ServiceFeedbackNotificationActions,
ServiceFeedbackNotificationBlockIds,
+ CaseFeedbackNotificationActionIds,
+ CaseFeedbackNotificationActions,
+ CaseFeedbackNotificationBlockIds,
)
from dispatch.messaging.strings import (
ONCALL_SHIFT_FEEDBACK_RECEIVED,
@@ -195,7 +199,7 @@ def handle_incident_feedback_submission_event(
feedback_in = FeedbackCreate(
rating=rating, feedback=feedback, project=incident.project, incident=incident
)
- feedback = incident_feedback_service.create(db_session=db_session, feedback_in=feedback_in)
+ feedback = subject_feedback_service.create(db_session=db_session, feedback_in=feedback_in)
incident.feedback.append(feedback)
# we only really care if this exists, if it doesn't then flag is false
@@ -467,3 +471,109 @@ def handle_oncall_shift_feedback_submission_event(
)
except Exception as e:
log.exception(e)
+
+
+@app.action(
+ CaseFeedbackNotificationActions.provide,
+ middleware=[button_context_middleware, db_middleware],
+)
+def handle_case_feedback_direct_message_button_click(
+ ack: Ack,
+ body: dict,
+ client: WebClient,
+ respond: Respond,
+ db_session: Session,
+ context: BoltContext,
+):
+ """Handles the feedback button in the feedback direct message."""
+ ack()
+ case = case_service.get(db_session=db_session, case_id=context["subject"].id)
+
+ if not case:
+ message = "Sorry, you cannot submit feedback about this case. The case does not exist."
+ respond(message=message, ephemeral=True)
+ return
+
+ blocks = [
+ Context(
+ elements=[MarkdownText(text="Use this form to rate your experience about the case.")]
+ ),
+ rating_select(
+ action_id=CaseFeedbackNotificationActionIds.rating_select,
+ block_id=CaseFeedbackNotificationBlockIds.rating_select,
+ ),
+ feedback_input(
+ action_id=CaseFeedbackNotificationActionIds.feedback_input,
+ block_id=CaseFeedbackNotificationBlockIds.feedback_input,
+ ),
+ anonymous_checkbox(
+ action_id=CaseFeedbackNotificationActionIds.anonymous_checkbox,
+ block_id=CaseFeedbackNotificationBlockIds.anonymous_checkbox,
+ ),
+ ]
+
+ modal = Modal(
+ title="Case Feedback",
+ blocks=blocks,
+ submit="Submit",
+ close="Cancel",
+ callback_id=CaseFeedbackNotificationActions.submit,
+ private_metadata=context["subject"].json(),
+ ).build()
+
+ client.views_open(trigger_id=body["trigger_id"], view=modal)
+
+
+def ack_case_feedback_submission_event(ack: Ack) -> None:
+ """Handles the feedback submission event acknowledgement."""
+ modal = Modal(
+ title="Case Feedback", close="Close", blocks=[Section(text="Submitting feedback...")]
+ ).build()
+ ack(response_action="update", view=modal)
+
+
+@app.view(
+ CaseFeedbackNotificationActions.submit,
+ middleware=[action_context_middleware, db_middleware, user_middleware, modal_submit_middleware],
+)
+def handle_case_feedback_submission_event(
+ ack: Ack,
+ body: dict,
+ context: BoltContext,
+ user: DispatchUser,
+ client: WebClient,
+ db_session: Session,
+ form_data: dict,
+):
+ # TODO: handle multiple organizations during submission
+ ack_case_feedback_submission_event(ack=ack)
+ case = case_service.get(db_session=db_session, case_id=context["subject"].id)
+
+ feedback = form_data.get(CaseFeedbackNotificationBlockIds.feedback_input, "")
+ rating = form_data.get(CaseFeedbackNotificationBlockIds.rating_select, {}).get("value")
+
+ feedback_in = FeedbackCreate(rating=rating, feedback=feedback, project=case.project, case=case)
+ feedback = subject_feedback_service.create(db_session=db_session, feedback_in=feedback_in)
+ case.feedback.append(feedback)
+
+ # we only really care if this exists, if it doesn't then flag is false
+ if not form_data.get(CaseFeedbackNotificationBlockIds.anonymous_checkbox):
+ participant = participant_service.get_by_case_id_and_email(
+ db_session=db_session, case_id=context["subject"].id, email=user.email
+ )
+ participant.feedback.append(feedback)
+ db_session.add(participant)
+
+ db_session.add(case)
+ db_session.commit()
+
+ modal = Modal(
+ title="Case Feedback",
+ close="Close",
+ blocks=[Section(text="Submitting feedback... Success!")],
+ ).build()
+
+ client.views_update(
+ view_id=body["view"]["id"],
+ view=modal,
+ )
diff --git a/src/dispatch/static/dispatch/src/feedback/incident/Table.vue b/src/dispatch/static/dispatch/src/feedback/incident/Table.vue
index 894ab2fcf007..d6b4184a063c 100644
--- a/src/dispatch/static/dispatch/src/feedback/incident/Table.vue
+++ b/src/dispatch/static/dispatch/src/feedback/incident/Table.vue
@@ -3,7 +3,7 @@
- Incident feedback
+ Incident and Case feedback
@@ -49,6 +49,18 @@
{{ item.project.name }}
+
+ {{ item.incident.name }}
+ {{ item.case.name }}
+
+
+ {{ item.incident.title }}
+ {{ item.case.title }}
+
+
+ Incident
+ Case
+
@@ -92,8 +104,9 @@ export default {
data() {
return {
headers: [
- { title: "Incident Name", value: "incident.name", sortable: false },
- { title: "Incident Title", value: "incident.title", sortable: false },
+ { title: "Type", key: "type", sortable: false },
+ { title: "Name", key: "name", sortable: false },
+ { title: "Title", key: "title", sortable: false },
{ title: "Participant", value: "participant", sortable: true },
{ title: "Rating", value: "rating", sortable: true },
{ title: "Feedback", value: "feedback", sortable: true },
@@ -117,6 +130,7 @@ export default {
"table.options.descending",
"table.options.filters",
"table.options.filters.incident",
+ "table.options.filters.case",
"table.options.filters.rating",
"table.options.filters.feedback",
"table.options.filters.participant",
@@ -166,6 +180,7 @@ export default {
vm.sortBy,
vm.descending,
vm.incident,
+ vm.case,
vm.rating,
vm.feedback,
vm.project,
diff --git a/src/dispatch/static/dispatch/src/feedback/incident/TableFilterDialog.vue b/src/dispatch/static/dispatch/src/feedback/incident/TableFilterDialog.vue
index 8c2863744d5a..37c273669b10 100644
--- a/src/dispatch/static/dispatch/src/feedback/incident/TableFilterDialog.vue
+++ b/src/dispatch/static/dispatch/src/feedback/incident/TableFilterDialog.vue
@@ -16,6 +16,9 @@
+
+
+
@@ -30,6 +33,7 @@ import { sum } from "lodash"
import { mapFields } from "vuex-map-fields"
import IncidentCombobox from "@/incident/IncidentFilterCombobox.vue"
+import CaseCombobox from "@/case/CaseFilterCombobox.vue"
import ProjectCombobox from "@/project/ProjectCombobox.vue"
export default {
@@ -38,6 +42,7 @@ export default {
components: {
IncidentCombobox,
ProjectCombobox,
+ CaseCombobox,
},
props: {
@@ -53,6 +58,7 @@ export default {
return {
display: false,
local_incident: [],
+ local_case: [],
local_project: this.projects,
}
},
@@ -60,11 +66,12 @@ export default {
computed: {
...mapFields("incident_feedback", [
"table.options.filters.incident",
+ "table.options.filters.case",
"table.options.filters.project",
]),
numFilters: function () {
- return sum([this.incident.length, this.project.length])
+ return sum([this.incident.length, this.project.length, this.case.length])
},
},
@@ -73,6 +80,7 @@ export default {
// we set the filter values
this.project = this.local_project
this.incident = this.local_incident
+ this.case = this.local_case
// we close the dialog
this.display = false
diff --git a/src/dispatch/static/dispatch/src/feedback/incident/store.js b/src/dispatch/static/dispatch/src/feedback/incident/store.js
index ee6413ec17c3..727e87107f4f 100644
--- a/src/dispatch/static/dispatch/src/feedback/incident/store.js
+++ b/src/dispatch/static/dispatch/src/feedback/incident/store.js
@@ -9,6 +9,7 @@ const getDefaultSelectedState = () => {
feedback: null,
id: null,
incident: null,
+ case: null,
participant: null,
project: null,
rating: null,
@@ -36,6 +37,7 @@ const state = {
sortBy: ["created_at"],
descending: [true],
filters: {
+ case: [],
incident: [],
rating: [],
feedback: [],
diff --git a/src/dispatch/static/dispatch/src/router/config.js b/src/dispatch/static/dispatch/src/router/config.js
index 613214b6ce86..cf9c90fbf0e5 100644
--- a/src/dispatch/static/dispatch/src/router/config.js
+++ b/src/dispatch/static/dispatch/src/router/config.js
@@ -359,7 +359,7 @@ export const protectedRoute = [
{
path: "/:organization/feedback/incident",
name: "IncidentFeedbackTable",
- meta: { title: "Incident feedback", group: "feedback" },
+ meta: { title: "Incident and Case feedback", group: "feedback" },
component: () => import("@/feedback/incident/Table.vue"),
},
{
From 9546962d8a77e18a7ba66a887d89ea6dbbf64cd3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:23:51 -0700
Subject: [PATCH 09/19] chore(deps-dev): bump sass in
/src/dispatch/static/dispatch (#5358)
Bumps [sass](https://github.com/sass/dart-sass) from 1.79.5 to 1.80.2.
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.79.5...1.80.2)
---
updated-dependencies:
- dependency-name: sass
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
src/dispatch/static/dispatch/package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/dispatch/static/dispatch/package-lock.json b/src/dispatch/static/dispatch/package-lock.json
index adb3224824d8..b4f7245eb30b 100644
--- a/src/dispatch/static/dispatch/package-lock.json
+++ b/src/dispatch/static/dispatch/package-lock.json
@@ -6354,9 +6354,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/sass": {
- "version": "1.79.5",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz",
- "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==",
+ "version": "1.80.2",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.2.tgz",
+ "integrity": "sha512-9wXY8cGBlUmoUoT+vwOZOFCiS+naiWVjqlreN9ar9PudXbGwlMTFwCR5K9kB4dFumJ6ib98wZyAObJKsWf1nAA==",
"dev": true,
"dependencies": {
"@parcel/watcher": "^2.4.1",
From 743c540fbdf3ef566dba44df215346b59d8cc2f2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:23:59 -0700
Subject: [PATCH 10/19] chore(deps): bump vuetify in
/src/dispatch/static/dispatch (#5354)
Bumps [vuetify](https://github.com/vuetifyjs/vuetify/tree/HEAD/packages/vuetify) from 3.7.2 to 3.7.3.
- [Release notes](https://github.com/vuetifyjs/vuetify/releases)
- [Commits](https://github.com/vuetifyjs/vuetify/commits/v3.7.3/packages/vuetify)
---
updated-dependencies:
- dependency-name: vuetify
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
src/dispatch/static/dispatch/package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/dispatch/static/dispatch/package-lock.json b/src/dispatch/static/dispatch/package-lock.json
index b4f7245eb30b..dbc9bf9651d1 100644
--- a/src/dispatch/static/dispatch/package-lock.json
+++ b/src/dispatch/static/dispatch/package-lock.json
@@ -7716,9 +7716,9 @@
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"node_modules/vuetify": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.2.tgz",
- "integrity": "sha512-q0WTcRG977+a9Dqhb8TOaPm+Xmvj0oVhnBJhAdHWFSov3HhHTTxlH2nXP/GBTXZuuMHDbBeIWFuUR2/1Fx0PPw==",
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.3.tgz",
+ "integrity": "sha512-bpuvBpZl1/+nLlXDgdVXekvMNR6W/ciaoa8CYlpeAzAARbY8zUFSoBq05JlLhkIHI58AnzKVy4c09d0OtfYAPg==",
"engines": {
"node": "^12.20 || >=14.13"
},
From 0df209335aa9e821532770c57c4e98f5490abc88 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:06 -0700
Subject: [PATCH 11/19] chore(deps-dev): bump faker from 30.3.0 to 30.6.0
(#5353)
Bumps [faker](https://github.com/joke2k/faker) from 30.3.0 to 30.6.0.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.md)
- [Commits](https://github.com/joke2k/faker/compare/v30.3.0...v30.6.0)
---
updated-dependencies:
- dependency-name: faker
dependency-type: direct:development
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements-dev.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index ed40e62269f0..a3ec83183758 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -32,7 +32,7 @@ executing==2.0.1
# stack-data
factory-boy==3.3.1
# via -r requirements-dev.in
-faker==30.3.0
+faker==30.6.0
# via
# -r requirements-dev.in
# factory-boy
From 9d47e8267562b8d2e72a825c9d109eca49220dbf Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:14 -0700
Subject: [PATCH 12/19] chore(deps-dev): bump @playwright/test in
/src/dispatch/static/dispatch (#5347)
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.48.0 to 1.48.1.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.48.0...v1.48.1)
---
updated-dependencies:
- dependency-name: "@playwright/test"
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
.../static/dispatch/package-lock.json | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/dispatch/static/dispatch/package-lock.json b/src/dispatch/static/dispatch/package-lock.json
index dbc9bf9651d1..61fce4a9bdb9 100644
--- a/src/dispatch/static/dispatch/package-lock.json
+++ b/src/dispatch/static/dispatch/package-lock.json
@@ -1233,12 +1233,12 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.48.0",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz",
- "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==",
+ "version": "1.48.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.1.tgz",
+ "integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==",
"dev": true,
"dependencies": {
- "playwright": "1.48.0"
+ "playwright": "1.48.1"
},
"bin": {
"playwright": "cli.js"
@@ -5770,12 +5770,12 @@
}
},
"node_modules/playwright": {
- "version": "1.48.0",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz",
- "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==",
+ "version": "1.48.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz",
+ "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==",
"dev": true,
"dependencies": {
- "playwright-core": "1.48.0"
+ "playwright-core": "1.48.1"
},
"bin": {
"playwright": "cli.js"
@@ -5788,9 +5788,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.48.0",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz",
- "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==",
+ "version": "1.48.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz",
+ "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
From 633b98231012567ad07abb4b0ec6e448c83d621a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:22 -0700
Subject: [PATCH 13/19] chore(deps): bump psycopg2-binary from 2.9.9 to 2.9.10
(#5346)
Bumps [psycopg2-binary](https://github.com/psycopg/psycopg2) from 2.9.9 to 2.9.10.
- [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS)
- [Commits](https://github.com/psycopg/psycopg2/commits)
---
updated-dependencies:
- dependency-name: psycopg2-binary
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements-base.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-base.txt b/requirements-base.txt
index 27c35de88fd4..8b96fc206b80 100644
--- a/requirements-base.txt
+++ b/requirements-base.txt
@@ -279,7 +279,7 @@ protobuf==4.23.4
# -r requirements-base.in
# google-api-core
# googleapis-common-protos
-psycopg2-binary==2.9.9
+psycopg2-binary==2.9.10
# via -r requirements-base.in
pyasn1==0.5.1
# via
From 2779fa781149d3fc488ca3d38ae9da02439bd02d Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:30 -0700
Subject: [PATCH 14/19] chore(deps): bump uvicorn from 0.31.1 to 0.32.0 (#5344)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.31.1 to 0.32.0.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.31.1...0.32.0)
---
updated-dependencies:
- dependency-name: uvicorn
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements-base.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-base.txt b/requirements-base.txt
index 8b96fc206b80..1083bca909a0 100644
--- a/requirements-base.txt
+++ b/requirements-base.txt
@@ -483,7 +483,7 @@ urllib3==2.0.7
# pdpyras
# requests
# sentry-sdk
-uvicorn==0.31.1
+uvicorn==0.32.0
# via -r requirements-base.in
uvloop==0.20.0
# via -r requirements-base.in
From b266b197d7fac85d031a82df7ffc28a0e210bb06 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:38 -0700
Subject: [PATCH 15/19] chore(deps-dev): bump vite in
/src/dispatch/static/dispatch (#5343)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.8 to 5.4.9.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.9/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.9/packages/vite)
---
updated-dependencies:
- dependency-name: vite
dependency-type: direct:development
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
src/dispatch/static/dispatch/package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/dispatch/static/dispatch/package-lock.json b/src/dispatch/static/dispatch/package-lock.json
index 61fce4a9bdb9..3ac5b3659721 100644
--- a/src/dispatch/static/dispatch/package-lock.json
+++ b/src/dispatch/static/dispatch/package-lock.json
@@ -6977,9 +6977,9 @@
}
},
"node_modules/vite": {
- "version": "5.4.8",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz",
- "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==",
+ "version": "5.4.9",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz",
+ "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==",
"devOptional": true,
"dependencies": {
"esbuild": "^0.21.3",
From 7771b4216cce04304aeef08ba680cc12998cbb85 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:46 -0700
Subject: [PATCH 16/19] chore(deps): bump apexcharts in
/src/dispatch/static/dispatch (#5342)
Bumps [apexcharts](https://github.com/apexcharts/apexcharts.js) from 3.54.0 to 3.54.1.
- [Release notes](https://github.com/apexcharts/apexcharts.js/releases)
- [Commits](https://github.com/apexcharts/apexcharts.js/compare/v3.54.0...v3.54.1)
---
updated-dependencies:
- dependency-name: apexcharts
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
src/dispatch/static/dispatch/package-lock.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/dispatch/static/dispatch/package-lock.json b/src/dispatch/static/dispatch/package-lock.json
index 3ac5b3659721..70859ef7dc32 100644
--- a/src/dispatch/static/dispatch/package-lock.json
+++ b/src/dispatch/static/dispatch/package-lock.json
@@ -2911,9 +2911,9 @@
}
},
"node_modules/apexcharts": {
- "version": "3.54.0",
- "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.0.tgz",
- "integrity": "sha512-ZgI/seScffjLpwNRX/gAhIkAhpCNWiTNsdICv7qxnF0xisI23XSsaENUKIcMlyP1rbe8ECgvybDnp7plZld89A==",
+ "version": "3.54.1",
+ "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.54.1.tgz",
+ "integrity": "sha512-E4et0h/J1U3r3EwS/WlqJCQIbepKbp6wGUmaAwJOMjHUP4Ci0gxanLa7FR3okx6p9coi4st6J853/Cb1NP0vpA==",
"dependencies": {
"@yr/monotone-cubic-spline": "^1.0.3",
"svg.draggable.js": "^2.2.2",
From 2308a7c4a1ff81266d735726116a832f2aad77e0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:24:54 -0700
Subject: [PATCH 17/19] chore(deps): bump uvloop from 0.20.0 to 0.21.0 (#5341)
Bumps [uvloop](https://github.com/MagicStack/uvloop) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/MagicStack/uvloop/releases)
- [Commits](https://github.com/MagicStack/uvloop/compare/v0.20.0...v0.21.0)
---
updated-dependencies:
- dependency-name: uvloop
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements-base.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-base.txt b/requirements-base.txt
index 1083bca909a0..58fb2f665655 100644
--- a/requirements-base.txt
+++ b/requirements-base.txt
@@ -485,7 +485,7 @@ urllib3==2.0.7
# sentry-sdk
uvicorn==0.32.0
# via -r requirements-base.in
-uvloop==0.20.0
+uvloop==0.21.0
# via -r requirements-base.in
validators==0.18.2
# via -r requirements-base.in
From d41953c377db80adbb74f84a064d594e4c474a77 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 18 Oct 2024 13:25:01 -0700
Subject: [PATCH 18/19] chore(deps): bump pyparsing from 3.1.4 to 3.2.0 (#5340)
Bumps [pyparsing](https://github.com/pyparsing/pyparsing) from 3.1.4 to 3.2.0.
- [Release notes](https://github.com/pyparsing/pyparsing/releases)
- [Changelog](https://github.com/pyparsing/pyparsing/blob/master/CHANGES)
- [Commits](https://github.com/pyparsing/pyparsing/compare/3.1.4...3.2.0)
---
updated-dependencies:
- dependency-name: pyparsing
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
requirements-base.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/requirements-base.txt b/requirements-base.txt
index 58fb2f665655..0501d130a21c 100644
--- a/requirements-base.txt
+++ b/requirements-base.txt
@@ -308,7 +308,7 @@ pyjwt[crypto]==2.8.0
# msal
# oauthlib
# pyjwt
-pyparsing==3.1.4
+pyparsing==3.2.0
# via
# -r requirements-base.in
# httplib2
From 9c1627374581228eb30ead1cd4b867e2df6cff02 Mon Sep 17 00:00:00 2001
From: David Whittaker <84562015+whitdog47@users.noreply.github.com>
Date: Mon, 21 Oct 2024 11:57:14 -0700
Subject: [PATCH 19/19] Allow for dictionary in metadata (#5366)
---
src/dispatch/plugin/models.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/dispatch/plugin/models.py b/src/dispatch/plugin/models.py
index e7a6b3760ff1..1cae6c5f3f0e 100644
--- a/src/dispatch/plugin/models.py
+++ b/src/dispatch/plugin/models.py
@@ -219,7 +219,7 @@ class PluginInstanceUpdate(PluginBase):
class KeyValue(DispatchBase):
key: str
- value: str | List[str]
+ value: str | List[str] | dict
class PluginMetadata(DispatchBase):