Skip to content

Commit

Permalink
Allows to handoff incidents in bulk (#2984)
Browse files Browse the repository at this point in the history
* Adds dialog to handoff incidents in the Web UI

* Removes commented code

* Improvements

* Bugfixes

* Improvements

* New dialog version

* Improvements

* Removes Pydantic model

* Removes incident view

* removes bulkHandoff api call

* removes commented code

* makes eslint happy
  • Loading branch information
mvilanova authored Feb 13, 2023
1 parent b701156 commit 8a7cc38
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 37 deletions.
33 changes: 16 additions & 17 deletions src/dispatch/incident/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def get_incidents(
expand: bool = Query(default=False),
):
"""Retrieves a list of incidents."""
print(expand)
pagination = search_filter_sort_paginate(model="Incident", **common)

if expand:
Expand Down Expand Up @@ -175,6 +174,22 @@ def update_incident(
return incident


@router.delete(
"/{incident_id}",
response_model=None,
summary="Delete an incident.",
dependencies=[Depends(PermissionsDependency([IncidentEditPermission]))],
)
def delete_incident(
*,
incident_id: PrimaryKey,
db_session: Session = Depends(get_db),
current_incident: Incident = Depends(get_current_incident),
):
"""Deletes an incident."""
delete(db_session=db_session, incident_id=current_incident.id)


@router.post(
"/{incident_id}/join",
summary="Adds an individual to an incident.",
Expand Down Expand Up @@ -271,22 +286,6 @@ def create_executive_report(
)


@router.delete(
"/{incident_id}",
response_model=None,
summary="Delete an incident.",
dependencies=[Depends(PermissionsDependency([IncidentEditPermission]))],
)
def delete_incident(
*,
incident_id: PrimaryKey,
db_session: Session = Depends(get_db),
current_incident: Incident = Depends(get_current_incident),
):
"""Deletes an incident."""
delete(db_session=db_session, incident_id=current_incident.id)


def get_month_range(relative):
today = date.today()
relative_month = today - relativedelta(months=relative)
Expand Down
10 changes: 4 additions & 6 deletions src/dispatch/models.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
from typing import Optional
from datetime import datetime, timedelta

import validators

from pydantic import BaseModel
from pydantic.fields import Field
from pydantic.networks import EmailStr
from pydantic import BaseModel, validator
from pydantic.types import conint, constr, SecretStr

from sqlalchemy import Boolean, Column, DateTime, Integer, String, event, ForeignKey
from sqlalchemy import func
from sqlalchemy.orm import relationship
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import Boolean, Column, DateTime, Integer, String, event, ForeignKey
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import relationship

# pydantic type that limits the range of primary keys
PrimaryKey = conint(gt=0, lt=2147483647)
Expand Down
9 changes: 7 additions & 2 deletions src/dispatch/static/dispatch/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,16 @@ instance.interceptors.response.use(
}

if (err.response.status == 500) {
let errorText = err.response.data.detail.map(({ msg }) => msg).join(" ")
let errorText = ""
if (err.response.data.detail) {
errorText = err.response.data.detail.map(({ msg }) => msg).join(" ")
}

if (errorText.length == 0) {
errorText =
"Something has gone wrong, please retry or let your admin know that you received this error."
"Something has gone wrong. Please, retry or let your admin know that you received this error."
}

store.commit(
"notification_backend/addBeNotification",
{
Expand Down
10 changes: 8 additions & 2 deletions src/dispatch/static/dispatch/src/case/DetailsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,10 @@
</template>

<script>
import { mapFields } from "vuex-map-fields"
import { ValidationProvider, extend } from "vee-validate"
import { mapFields } from "vuex-map-fields"
import { required } from "vee-validate/dist/rules"
import CaseFilterCombobox from "@/case/CaseFilterCombobox.vue"
import CasePrioritySelect from "@/case/priority/CasePrioritySelect.vue"
import CaseSeveritySelect from "@/case/severity/CaseSeveritySelect.vue"
Expand All @@ -123,12 +124,15 @@ import IncidentFilterCombobox from "@/incident/IncidentFilterCombobox.vue"
import ParticipantSelect from "@/incident/ParticipantSelect.vue"
import ProjectSelect from "@/project/ProjectSelect.vue"
import TagFilterAutoComplete from "@/tag/TagFilterAutoComplete.vue"
extend("required", {
...required,
message: "This field is required",
})
export default {
name: "CaseDetailsTab",
components: {
CaseFilterCombobox,
CasePrioritySelect,
Expand All @@ -141,12 +145,14 @@ export default {
TagFilterAutoComplete,
ValidationProvider,
},
data() {
return {
statuses: ["New", "Triage", "Escalated", "Closed"],
visibilities: ["Open", "Restricted"],
}
},
computed: {
...mapFields("case_management", [
"selected.assignee",
Expand All @@ -160,11 +166,11 @@ export default {
"selected.id",
"selected.incidents",
"selected.name",
"selected.signals",
"selected.project",
"selected.related",
"selected.reported_at",
"selected.resolution",
"selected.signals",
"selected.status",
"selected.tags",
"selected.title",
Expand Down
19 changes: 18 additions & 1 deletion src/dispatch/static/dispatch/src/incident/BulkEditSheet.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
<template>
<v-bottom-sheet v-model="showBulkEdit" hide-overlay persistent>
<handoff-dialog />
<v-card :loading="bulkEditLoading" tile>
<v-list>
<v-list-item>
<v-list-item-content>
<v-list-item-subtitle>{{ selected.length }} selected</v-list-item-subtitle>
</v-list-item-content>
<v-spacer />
<v-list-item-icon>
<v-btn text @click="showHandoffDialog()">
<v-icon>mdi-account-arrow-right</v-icon>
Handoff
</v-btn>
</v-list-item-icon>
<v-list-item-icon>
<v-btn text @click="saveBulk({ status: 'Active' })">
<v-icon>mdi-check</v-icon>
Expand Down Expand Up @@ -40,16 +47,26 @@
<script>
import { mapFields } from "vuex-map-fields"
import { mapActions } from "vuex"
import HandoffDialog from "@/incident/HandoffDialog.vue"
export default {
name: "IncidentBulkEditSheet",
components: {
HandoffDialog,
},
computed: {
...mapFields("incident", ["table.rows.selected", "table.bulkEditLoading"]),
showBulkEdit: function () {
return this.selected.length ? true : false
},
},
methods: {
...mapActions("incident", ["saveBulk", "deleteBulk"]),
...mapActions("incident", ["saveBulk", "deleteBulk", "showHandoffDialog"]),
},
}
</script>
58 changes: 58 additions & 0 deletions src/dispatch/static/dispatch/src/incident/HandoffDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<v-dialog v-model="showHandoffDialog" persistent max-width="800px">
<v-card>
<v-card-title>
<span class="headline">Handoff Incidents</span>
</v-card-title>
<v-card-text>
Select the new commander for the selected incidents.
</v-card-text>
<v-card-actions>
<v-container grid-list-md>
<v-layout wrap>
<v-flex xs12>
<participant-select v-model="commander" label="Incident Commander" :project="project"/>
</v-flex>
<!-- <v-flex xs12> -->
<!-- <v-checkbox v-model="report" label="Generate Report"/> -->
<!-- </v-flex> -->
<v-btn color="blue en-1" text @click="closeHandoffDialog()"> Cancel </v-btn>
<v-btn color="red en-1" text :loading="loading" @click="saveBulk({commander: commander})">
Handoff
</v-btn>
</v-layout>
</v-container>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script>
import { mapFields } from "vuex-map-fields"
import { mapActions } from "vuex"
import ParticipantSelect from "@/incident/ParticipantSelect.vue"
export default {
name: "IncidentHandoffDialog",
data() {
return {
commander: { individual: { name: "Commander Name" }},
report: false,
}
},
components: {
ParticipantSelect,
},
computed: {
...mapFields("incident", ["dialogs.showHandoffDialog", "selected.loading", "selected.project"]),
},
methods: {
...mapActions("incident", ["closeHandoffDialog", "saveBulk", "resetSelected"]),
},
}
</script>
4 changes: 2 additions & 2 deletions src/dispatch/static/dispatch/src/incident/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ import IncidentPriority from "@/incident/priority/IncidentPriority.vue"
import IncidentSeverity from "@/incident/severity/IncidentSeverity.vue"
import IncidentStatus from "@/incident/status/IncidentStatus.vue"
import NewSheet from "@/incident/NewSheet.vue"
import WorkflowRunModal from "@/workflow/RunModal.vue"
import ReportDialog from "@/incident/ReportDialog.vue"
import RouterUtils from "@/router/utils"
import TableExportDialog from "@/incident/TableExportDialog.vue"
import TableFilterDialog from "@/incident/TableFilterDialog.vue"
import WorkflowRunModal from "@/workflow/RunModal.vue"
export default {
name: "IncidentTable",
Expand All @@ -148,9 +148,9 @@ export default {
IncidentStatus,
NewSheet,
ReportDialog,
WorkflowRunModal,
TableExportDialog,
TableFilterDialog,
WorkflowRunModal,
},
props: {
Expand Down
12 changes: 6 additions & 6 deletions src/dispatch/static/dispatch/src/incident/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ export default {
return API.put(`/${resource}/${incidentId}`, payload)
},

createReport(incidentId, type, payload) {
return API.post(`/${resource}/${incidentId}/report/${type}`, payload)
},

bulkUpdate(incidents, payload) {
return Promise.all(
incidents.map((incident) => {
Expand All @@ -39,6 +35,10 @@ export default {
)
},

delete(incidentId) {
return API.delete(`/${resource}/${incidentId}`)
},

bulkDelete(incidents) {
return Promise.all(
incidents.map((incident) => {
Expand All @@ -55,7 +55,7 @@ export default {
return API.post(`/${resource}/${incidentId}/subscribe`, payload)
},

delete(incidentId) {
return API.delete(`/${resource}/${incidentId}`)
createReport(incidentId, type, payload) {
return API.post(`/${resource}/${incidentId}/report/${type}`, payload)
},
}
14 changes: 13 additions & 1 deletion src/dispatch/static/dispatch/src/incident/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ const state = {
},
dialogs: {
showDeleteDialog: false,
showReportDialog: false,
showEditSheet: false,
showExport: false,
showHandoffDialog: false,
showNewSheet: false,
showReportDialog: false,
},
report: {
...getDefaultReportState(),
Expand Down Expand Up @@ -214,6 +215,14 @@ const actions = {
closeExport({ commit }) {
commit("SET_DIALOG_SHOW_EXPORT", false)
},
showHandoffDialog({ commit }, value) {
commit("SET_DIALOG_SHOW_HANDOFF", true)
commit("SET_SELECTED", value)
},
closeHandoffDialog({ commit }) {
commit("SET_DIALOG_SHOW_HANDOFF", false)
commit("RESET_SELECTED")
},
report({ commit, dispatch }) {
commit("SET_SELECTED_LOADING", true)
return IncidentApi.create(state.selected)
Expand Down Expand Up @@ -381,6 +390,9 @@ const mutations = {
SET_DIALOG_SHOW_EXPORT(state, value) {
state.dialogs.showExport = value
},
SET_DIALOG_SHOW_HANDOFF(state, value) {
state.dialogs.showHandoffDialog = value
},
SET_DIALOG_DELETE(state, value) {
state.dialogs.showDeleteDialog = value
},
Expand Down

0 comments on commit 8a7cc38

Please sign in to comment.