Skip to content

Commit

Permalink
Adds support for deleting external tickets, groups, and storage
Browse files Browse the repository at this point in the history
  • Loading branch information
mvilanova committed Aug 11, 2022
1 parent bebd00c commit d6c728a
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 62 deletions.
30 changes: 22 additions & 8 deletions src/dispatch/case/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def case_new_create_flow(*, case_id: int, organization_slug: str, db_session=Non

if not group:
# we delete the ticket
ticket_flows.delete_ticket(ticket=ticket, project_id=case.project.id, db_session=db_session)
ticket_flows.delete_ticket(ticket=ticket, db_session=db_session)

# we delete the case
delete(db_session=db_session, case_id=case_id)
Expand All @@ -53,10 +53,10 @@ def case_new_create_flow(*, case_id: int, organization_slug: str, db_session=Non
storage = storage_flows.create_storage(obj=case, members=members, db_session=db_session)
if not storage:
# we delete the group
group_flows.delete_group(group=group, project_id=case.project.id, db_session=db_session)
group_flows.delete_group(group=group, db_session=db_session)

# we delete the ticket
ticket_flows.delete_ticket(ticket=ticket, project_id=case.project.id, db_session=db_session)
ticket_flows.delete_ticket(ticket=ticket, db_session=db_session)

# we delete the case
delete(db_session=db_session, case_id=case_id)
Expand All @@ -70,15 +70,13 @@ def case_new_create_flow(*, case_id: int, organization_slug: str, db_session=Non
)
if not document:
# we delete the storage
storage_flows.delete_storage(
storage=storage, project_id=case.project.id, db_session=db_session
)
storage_flows.delete_storage(storage=storage, db_session=db_session)

# we delete the group
group_flows.delete_group(group=group, project_id=case.project.id, db_session=db_session)
group_flows.delete_group(group=group, db_session=db_session)

# we delete the ticket
ticket_flows.delete_ticket(ticket=ticket, project_id=case.project.id, db_session=db_session)
ticket_flows.delete_ticket(ticket=ticket, db_session=db_session)

# we delete the case
delete(db_session=db_session, case_id=case_id)
Expand Down Expand Up @@ -139,6 +137,22 @@ def case_update_flow(
# we send the case updated notification


def case_delete_flow(case: Case, db_session: SessionLocal):
"""Runs the case delete flow."""
# we delete the external ticket
if case.ticket:
ticket_flows.delete_ticket(ticket=case.ticket, db_session=db_session)

# we delete the external groups
if case.groups:
for group in case.groups:
group_flows.delete_group(group=group, db_session=db_session)

# we delete the external storage
if case.storage:
storage_flows.delete_storage(storage=case.storage, db_session=db_session)


def case_new_status_flow(case: Case, db_session=None):
"""Runs the case new transition flow."""
pass
Expand Down
41 changes: 31 additions & 10 deletions src/dispatch/case/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from starlette.requests import Request
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, status

from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

# NOTE: define permissions before enabling the code block below
Expand All @@ -26,6 +27,7 @@

from .flows import (
case_closed_create_flow,
case_delete_flow,
case_escalated_create_flow,
case_new_create_flow,
case_triage_create_flow,
Expand All @@ -41,7 +43,7 @@


def get_current_case(*, db_session: Session = Depends(get_db), request: Request) -> Case:
"""Fetches case or returns an HTTP 404."""
"""Fetches a case or returns an HTTP 404."""
case = get(db_session=db_session, case_id=request.path_params["case_id"])
if not case:
raise HTTPException(
Expand All @@ -51,13 +53,13 @@ def get_current_case(*, db_session: Session = Depends(get_db), request: Request)
return case


@router.get("", summary="Retrieve a list of all cases.")
@router.get("", summary="Retrieves all cases.")
def get_cases(
*,
common: dict = Depends(common_parameters),
include: List[str] = Query([], alias="include[]"),
):
"""Retrieve a list of all cases."""
"""Retrieves all cases."""
pagination = search_filter_sort_paginate(model="Case", **common)

if include:
Expand All @@ -74,7 +76,7 @@ def get_cases(
return json.loads(CasePagination(**pagination).json())


@router.post("", response_model=CaseRead, summary="Create a new case.")
@router.post("", response_model=CaseRead, summary="Creates a new case.")
def create_case(
*,
db_session: Session = Depends(get_db),
Expand Down Expand Up @@ -129,7 +131,7 @@ def update_case(
current_user: DispatchUser = Depends(get_current_user),
background_tasks: BackgroundTasks,
):
"""Update an existing case."""
"""Updates an existing case."""
# we store the previous state of the case in order to be able to detect changes
previous_case = CaseRead.from_orm(current_case)

Expand All @@ -151,14 +153,33 @@ def update_case(
@router.delete(
"/{case_id}",
response_model=CaseRead,
summary="Delete an case.",
summary="Deletes an existing case.",
# dependencies=[Depends(PermissionsDependency([CaseEditPermission]))],
)
def delete_case(
*,
case_id: PrimaryKey,
db_session: Session = Depends(get_db),
current_case: Case = Depends(get_current_case),
organization: OrganizationSlug,
case_id: PrimaryKey,
background_tasks: BackgroundTasks,
):
"""Delete an individual case."""
delete(db_session=db_session, case_id=current_case.id)
"""Deletes an existing case."""
# we get the internal case
case = get(db_session=db_session, case_id=case_id)

# we run the case delete flow
case_delete_flow(case=case, db_session=db_session)

# we delete the internal case
try:
delete(db_session=db_session, case_id=case_id)
except IntegrityError as e:
log.exception(e)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=[
{
"msg": f"Case {case.name} could not be deleted. Make sure the case has no relationships to other cases or incidents before deleting it."
}
],
)
15 changes: 6 additions & 9 deletions src/dispatch/group/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,15 @@ def create_group(
return group


def delete_group(group: Group, project_id: int, db_session: SessionLocal):
def delete_group(group: Group, db_session: SessionLocal):
"""Deletes an existing group."""
# we delete the external group
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=project_id, plugin_type="participant-group"
db_session=db_session, project_id=group.case.project.id, plugin_type="participant-group"
)
if plugin:
# TODO(mvilanova): implement deleting the external group
# plugin.instance.delete()
pass
try:
plugin.instance.delete(email=group.email)
except Exception as e:
log.exception(e)
else:
log.warning("Group not deleted. No group plugin enabled.")

# we delete the internal group
delete(db_session=db_session, group_id=group.id)
4 changes: 2 additions & 2 deletions src/dispatch/plugins/dispatch_google/drive/drive.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ def copy_file(client: Any, folder_id: str, file_id: str, new_file_name: str):
)


def delete_file(client: Any, folder_id: str, file_id: str):
"""Deletes a file from a teamdrive."""
def delete_file(client: Any, file_id: str):
"""Deletes a folder or file from a Google Drive."""
return make_call(client.files(), "delete", fileId=file_id, supportsAllDrives=True)


Expand Down
27 changes: 13 additions & 14 deletions src/dispatch/plugins/dispatch_google/drive/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
from dispatch.plugins.bases import StoragePlugin, TaskPlugin
from dispatch.plugins.dispatch_google import drive as google_drive_plugin
from dispatch.plugins.dispatch_google.common import get_service

from dispatch.plugins.dispatch_google.config import GoogleConfiguration

from .drive import (
Roles,
add_domain_permission,
add_permission,
add_reply,
copy_file,
create_file,
delete_file,
download_google_document,
list_files,
mark_as_readonly,
move_file,
remove_permission,
add_domain_permission,
add_reply,
mark_as_readonly,
)
from .task import get_task_activity

Expand Down Expand Up @@ -72,13 +72,13 @@ def add_participant(
role: str = "owner",
user_type: str = "user",
):
"""Adds participants to existing Google Drive."""
"""Adds participants to an existing Google Drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
for p in participants:
add_permission(client, p, team_drive_or_file_id, role, user_type)

def remove_participant(self, folder_id: str, participants: List[str]):
"""Removes participants from existing Google Drive."""
"""Removes participants from an existing Google Drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
for p in participants:
remove_permission(client, p, folder_id)
Expand All @@ -101,35 +101,34 @@ def create_file(
role: str = Roles.writer,
file_type: str = "folder",
):
"""Creates a new file in existing Google Drive."""
"""Creates a new file in an existing Google Drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
response = create_file(client, parent_id, name, participants, role, file_type)
response["weblink"] = response["webViewLink"]
return response

def delete_file(self, folder_id: str, file_id: str):
"""Removes a file from existing Google Drive."""
def delete_file(self, file_id: str):
"""Deletes a file or folder from an existing Google Drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
response = delete_file(client, folder_id, file_id)
response["weblink"] = response["webViewLink"]
response = delete_file(client, file_id)
return response

def copy_file(self, folder_id: str, file_id: str, name: str):
"""Creates a copy of the given file and places it in the specified team drive."""
"""Creates a copy of the given file and places it in the specified Google Drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
response = copy_file(client, folder_id, file_id, name)
response["weblink"] = response["webViewLink"]
return response

def move_file(self, new_folder_id: str, file_id: str):
"""Moves a file from one team drive to another."""
"""Moves a file from one Google drive to another."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
response = move_file(client, new_folder_id, file_id)
response["weblink"] = response["webViewLink"]
return response

def list_files(self, folder_id: str, q: str = None):
"""Lists all files in team drive."""
"""Lists all files in a Google drive."""
client = get_service(self.configuration, "drive", "v3", self.scopes)
return list_files(client, folder_id, q)

Expand Down
6 changes: 6 additions & 0 deletions src/dispatch/plugins/dispatch_jira/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,9 @@ def update_case_ticket(
)

return update(self.configuration, client, issue, issue_fields, status)

def delete(self, ticket_id: str):
"""Deletes a Jira issue."""
client = create_client(self.configuration)
issue = client.issue(ticket_id)
issue.delete()
7 changes: 6 additions & 1 deletion src/dispatch/static/dispatch/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,15 @@ instance.interceptors.response.use(
}

if (err.response.status == 500) {
let 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."
}
store.commit(
"notification_backend/addBeNotification",
{
text: "Something has gone wrong, please retry or let your admin know that you received this error.",
text: errorText,
type: "error",
},
{ root: true }
Expand Down
15 changes: 6 additions & 9 deletions src/dispatch/storage/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,15 @@ def create_storage(obj: Any, members: List[str], db_session: SessionLocal):
return storage


def delete_storage(storage: Storage, project_id: int, db_session: SessionLocal):
def delete_storage(storage: Storage, db_session: SessionLocal):
"""Deletes an existing storage."""
# we delete the external storage
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=project_id, plugin_type="storage"
db_session=db_session, project_id=storage.case.project.id, plugin_type="storage"
)
if plugin:
# TODO(mvilanova): implement deleting the external storage
# plugin.instance.delete()
pass
try:
plugin.instance.delete_file(file_id=storage.resource_id)
except Exception as e:
log.exception(e)
else:
log.warning("Storage not deleted. No storage plugin enabled.")

# we delete the internal storage
delete(db_session=db_session, storage_id=storage.id)
15 changes: 6 additions & 9 deletions src/dispatch/ticket/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,15 @@ def update_case_ticket(
)


def delete_ticket(ticket: Ticket, project_id: int, db_session: SessionLocal):
def delete_ticket(ticket: Ticket, db_session: SessionLocal):
"""Deletes a ticket."""
# we delete the external ticket
plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=project_id, plugin_type="ticket"
db_session=db_session, project_id=ticket.case.project.id, plugin_type="ticket"
)
if plugin:
# TODO(mvilanova): implement deleting the external ticket
# plugin.instance.delete_case_ticket()
pass
try:
plugin.instance.delete(ticket_id=ticket.resource_id)
except Exception as e:
log.exception(e)
else:
log.warning("Ticket not deleted. No ticket plugin enabled.")

# we delete the internal ticket
delete(db_session=db_session, ticket_id=ticket.id)

0 comments on commit d6c728a

Please sign in to comment.