Skip to content

Commit

Permalink
Allows incident title in ticket name (#5397)
Browse files Browse the repository at this point in the history
  • Loading branch information
whitdog47 authored Oct 28, 2024
1 parent 057c699 commit 169580d
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Adds configuration to the Dispatch Ticket PluginInstance
Revision ID: 24322617ce9a
Revises: 3c49f62d7914
Create Date: 2024-10-25 15:15:38.078421
"""

from alembic import op
from pydantic import SecretStr, ValidationError
from pydantic.json import pydantic_encoder

from sqlalchemy import Column, Integer, ForeignKey, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy_utils import StringEncryptedType
from sqlalchemy_utils.types.encrypted.encrypted_type import AesEngine
from dispatch.config import DISPATCH_ENCRYPTION_KEY

# revision identifiers, used by Alembic.
revision = "24322617ce9a"
down_revision = "3c49f62d7914"
branch_labels = None
depends_on = None

Base = declarative_base()


def show_secrets_encoder(obj):
if isinstance(obj, SecretStr):
return obj.get_secret_value()
else:
return pydantic_encoder(obj)


def migrate_config(instances, slug, config):
for instance in instances:
if slug == instance.plugin.slug:
instance.configuration = config


class Plugin(Base):
__tablename__ = "plugin"
__table_args__ = {"schema": "dispatch_core"}
id = Column(Integer, primary_key=True)
slug = Column(String, unique=True)


class PluginInstance(Base):
__tablename__ = "plugin_instance"
id = Column(Integer, primary_key=True)
_configuration = Column(
StringEncryptedType(key=str(DISPATCH_ENCRYPTION_KEY), engine=AesEngine, padding="pkcs5")
)
plugin_id = Column(Integer, ForeignKey(Plugin.id))
plugin = relationship(Plugin, backref="instances")

@hybrid_property
def configuration(self):
"""Property that correctly returns a plugins configuration object."""
pass

@configuration.setter
def configuration(self, configuration):
"""Property that correctly sets a plugins configuration object."""
if configuration:
self._configuration = configuration.json(encoder=show_secrets_encoder)


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
from dispatch.plugins.dispatch_core.config import DispatchTicketConfiguration

bind = op.get_bind()
session = Session(bind=bind)

instances = session.query(PluginInstance).all()

try:
dispatch_ticket_config = DispatchTicketConfiguration(
use_incident_name=False,
)

migrate_config(instances, "dispatch-ticket", dispatch_ticket_config)

except ValidationError:
print(
"Skipping automatic migration of Dispatch ticket plugin, if you are using the Dispatch ticket plugin, please manually migrate."
)

session.commit()
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
13 changes: 12 additions & 1 deletion src/dispatch/plugins/dispatch_core/config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import logging
from dispatch.config import BaseConfigurationModel

from starlette.config import Config

from pydantic import Field

log = logging.getLogger(__name__)


config = Config(".env")


class DispatchTicketConfiguration(BaseConfigurationModel):
"""Dispatch ticket configuration"""

use_incident_name: bool = Field(
True,
title="Use Incident Name",
description="Use the incident name as the ticket title.",
)
13 changes: 10 additions & 3 deletions src/dispatch/plugins/dispatch_core/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
from dispatch.service.models import Service, ServiceRead
from dispatch.team import service as team_service
from dispatch.team.models import TeamContact, TeamContactRead
from dispatch.plugins.dispatch_core.config import DispatchTicketConfiguration
from dispatch.plugins.dispatch_core.service import create_resource_id

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -175,6 +177,9 @@ class DispatchTicketPlugin(TicketPlugin):
author = "Netflix"
author_url = "https://github.com/netflix/dispatch.git"

def __init__(self):
self.configuration_schema = DispatchTicketConfiguration

def create(
self,
incident_id: int,
Expand All @@ -187,9 +192,11 @@ def create(
"""Creates a Dispatch incident ticket."""
incident = incident_service.get(db_session=db_session, incident_id=incident_id)

resource_id = (
f"dispatch-{incident.project.organization.slug}-{incident.project.slug}-{incident.id}"
)
if self.configuration and self.configuration.use_incident_name:
resource_id = create_resource_id(f"{incident.project.slug}-{title}-{incident.id}")
else:
resource_id = f"dispatch-{incident.project.organization.slug}-{incident.project.slug}-{incident.id}"

return {
"resource_id": resource_id,
"weblink": f"{DISPATCH_UI_URL}/{incident.project.organization.name}/incidents/{resource_id}?project={incident.project.name}",
Expand Down
20 changes: 20 additions & 0 deletions src/dispatch/plugins/dispatch_core/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import re


def create_resource_id(title: str) -> str:
"""Creates a Slack-friendly resource id from the incident title."""
resource_id = title.lower()

# Replace any character that is not a lowercase letter or number with a hyphen
resource_id = re.sub(r"[^a-z0-9]", "-", resource_id)

# Replace multiple consecutive hyphens with a single hyphen
resource_id = re.sub(r"-+", "-", resource_id)

# Ensure the channel name is not longer than 80 characters
resource_id = resource_id[:80]

# Remove leading or trailing hyphens
resource_id = resource_id.strip("-")

return resource_id

0 comments on commit 169580d

Please sign in to comment.