Skip to content

Commit

Permalink
Creating fallback in case Jira fails (#5415)
Browse files Browse the repository at this point in the history
  • Loading branch information
whitdog47 authored Nov 4, 2024
1 parent 624110b commit e8c424f
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/dispatch/incident/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def incident_create_resources(
) -> Incident:
"""Creates all resources required for incidents."""
# we create the incident ticket
if not incident.ticket:
if not incident.ticket or incident.ticket.resource_type == "jira-error-ticket":
ticket_flows.create_incident_ticket(incident=incident, db_session=db_session)

# we update the channel name immediately for dedicated channel cases escalated -> incident
Expand Down
136 changes: 91 additions & 45 deletions src/dispatch/plugins/dispatch_jira/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@

from typing import Any
import json
import logging

import requests
from requests.auth import HTTPBasicAuth

from sqlalchemy.orm import Session

from pydantic import Field, SecretStr, AnyHttpUrl

from jinja2 import Template
Expand All @@ -20,14 +23,21 @@
from dispatch.decorators import apply, counter, timer
from dispatch.enums import DispatchEnum
from dispatch.plugins import dispatch_jira as jira_plugin
from dispatch.case import service as case_service
from dispatch.incident import service as incident_service
from dispatch.plugins.bases import TicketPlugin
from dispatch.project.models import Project

from .templates import (
CASE_ISSUE_SUMMARY_TEMPLATE,
INCIDENT_ISSUE_SUMMARY_NO_RESOURCES_TEMPLATE,
INCIDENT_ISSUE_SUMMARY_TEMPLATE,
)

from dispatch.config import DISPATCH_UI_URL

log = logging.getLogger(__name__)


class HostingType(DispatchEnum):
"""Type of Jira deployment."""
Expand Down Expand Up @@ -261,6 +271,16 @@ def update(
return data


def create_fallback_ticket(id: int, project: Project, db_session: Session):
resource_id = f"dispatch-{project.organization.slug}-{project.slug}-{id}"

return {
"resource_id": resource_id,
"weblink": f"{DISPATCH_UI_URL}/{project.organization.name}/incidents/{resource_id}?project={project.name}",
"resource_type": "jira-error-ticket",
}


@apply(counter, exclude=["__init__"])
@apply(timer, exclude=["__init__"])
class JiraTicketPlugin(TicketPlugin):
Expand All @@ -285,38 +305,51 @@ def create(
db_session=None,
):
"""Creates an incident Jira issue."""
client = create_client(self.configuration)
try:
client = create_client(self.configuration)

assignee = get_user_field(client, self.configuration, commander_email)
assignee = get_user_field(client, self.configuration, commander_email)

reporter = assignee
if reporter_email != commander_email:
reporter = get_user_field(client, self.configuration, reporter_email)
reporter = assignee
if reporter_email != commander_email:
reporter = get_user_field(client, self.configuration, reporter_email)

project_id, issue_type_name = process_plugin_metadata(incident_type_plugin_metadata)
project_id, issue_type_name = process_plugin_metadata(incident_type_plugin_metadata)

if not project_id:
project_id = self.configuration.default_project_id
if not project_id:
project_id = self.configuration.default_project_id

# NOTE: to support issue creation by project id or key
project = {"id": project_id}
if not project_id.isdigit():
project = {"key": project_id}
# NOTE: to support issue creation by project id or key
project = {"id": project_id}
if not project_id.isdigit():
project = {"key": project_id}

if not issue_type_name:
issue_type_name = self.configuration.default_issue_type_name
if not issue_type_name:
issue_type_name = self.configuration.default_issue_type_name

issuetype = {"name": issue_type_name}
issuetype = {"name": issue_type_name}

issue_fields = {
"project": project,
"issuetype": issuetype,
"assignee": assignee,
"reporter": reporter,
"summary": title,
}
issue_fields = {
"project": project,
"issuetype": issuetype,
"assignee": assignee,
"reporter": reporter,
"summary": title,
}

ticket = create(self.configuration, client, issue_fields)
except Exception as e:
log.exception(
f"Failed to create Jira ticket for incident_id: {incident_id}. "
f"Creating incident ticket with core plugin instead. Error: {e}"
)
# fall back to creating a ticket without the plugin
incident = incident_service.get(db_session=db_session, incident_id=incident_id)
ticket = create_fallback_ticket(
id=incident.id, project=incident.project, db_session=db_session
)

return create(self.configuration, client, issue_fields)
return ticket

def update(
self,
Expand Down Expand Up @@ -378,36 +411,49 @@ def create_case_ticket(
db_session=None,
):
"""Creates a case Jira issue."""
client = create_client(self.configuration)
try:
client = create_client(self.configuration)

assignee = get_user_field(client, self.configuration, assignee_email)
# TODO(mvilanova): enable reporter email and replace assignee email
# reporter = get_user_field(client, self.configuration, reporter_email)
reporter = assignee
assignee = get_user_field(client, self.configuration, assignee_email)
# TODO(mvilanova): enable reporter email and replace assignee email
# reporter = get_user_field(client, self.configuration, reporter_email)
reporter = assignee

project_id, issue_type_name = process_plugin_metadata(case_type_plugin_metadata)
project_id, issue_type_name = process_plugin_metadata(case_type_plugin_metadata)

if not project_id:
project_id = self.configuration.default_project_id
if not project_id:
project_id = self.configuration.default_project_id

project = {"id": project_id}
if not project_id.isdigit():
project = {"key": project_id}
project = {"id": project_id}
if not project_id.isdigit():
project = {"key": project_id}

if not issue_type_name:
issue_type_name = self.configuration.default_issue_type_name
if not issue_type_name:
issue_type_name = self.configuration.default_issue_type_name

issuetype = {"name": issue_type_name}
issuetype = {"name": issue_type_name}

issue_fields = {
"project": project,
"issuetype": issuetype,
"assignee": assignee,
"reporter": reporter,
"summary": title,
}
issue_fields = {
"project": project,
"issuetype": issuetype,
"assignee": assignee,
"reporter": reporter,
"summary": title,
}

return create(self.configuration, client, issue_fields)
ticket = create(self.configuration, client, issue_fields)
except Exception as e:
log.exception(
(
f"Failed to create Jira ticket for case_id: {case_id}. "
f"Creating case ticket with core plugin instead. Error: {e}"
)
)
# fall back to creating a ticket without the plugin
case = case_service.get(db_session=db_session, case_id=case_id)
ticket = create_fallback_ticket(id=case.id, project=case.project, db_session=db_session)

return ticket

def create_task_ticket(
self,
Expand Down
12 changes: 10 additions & 2 deletions src/dispatch/static/dispatch/src/incident/ResourcesTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
<v-list-item v-if="ticket" :href="ticket.weblink" target="_blank" class="my-3">
<v-list-item-title>Ticket</v-list-item-title>
<v-list-item-subtitle>{{ ticket.description }}</v-list-item-subtitle>

<div
class="text-caption"
v-if="(!ticket || ticket.resource_type == 'jira-error-ticket') && ticketPluginEnabled"
>
<span style="color: red">Ticket error: </span>
<span>
No Jira ticket created. Use Recreate Missing Resources to regenerate the ticket.
</span>
</div>
<template #append>
<v-icon>mdi-open-in-new</v-icon>
</template>
Expand Down Expand Up @@ -49,7 +57,7 @@
</span>
<span
v-if="
(!ticket && ticketPluginEnabled) ||
((!ticket || ticket.resource_type == 'jira-error-ticket') && ticketPluginEnabled) ||
(!conference && conferencePluginEnabled) ||
(!conversation && conversationPluginEnabled) ||
(!storage && storagePluginEnabled) ||
Expand Down
3 changes: 2 additions & 1 deletion src/dispatch/ticket/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ def create_incident_ticket(incident: Incident, db_session: Session):
log.error(f"Incident ticket not created. Plugin {plugin.plugin.slug} encountered an error.")
return

external_ticket.update({"resource_type": plugin.plugin.slug})
if "resource_type" not in external_ticket:
external_ticket.update({"resource_type": plugin.plugin.slug})

# we create the internal incident ticket
ticket_in = TicketCreate(**external_ticket)
Expand Down

0 comments on commit e8c424f

Please sign in to comment.