Skip to content

Commit

Permalink
refactor: update severities (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
sanjeevan authored Jul 26, 2024
1 parent a32429d commit c961bb7
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 68 deletions.
1 change: 1 addition & 0 deletions backend/app/models/incident_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class IncidentType(Base, TimestampMixin, SoftDeleteMixin):
description: Mapped[str] = mapped_column(UnicodeText, nullable=False)
is_editable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
is_deletable: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

# relationships
organisation: Mapped["Organisation"] = relationship("Organisation", back_populates="incident_types")
Expand Down
2 changes: 2 additions & 0 deletions backend/app/repos/incident_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ def create_incident_type(
is_editable: bool = True,
is_deletable: bool = True,
fields: list[ModelIdSchema] | None = None,
is_default: bool = False,
) -> IncidentType:
model = IncidentType()
model.organisation_id = organisation.id
model.name = name
model.description = description
model.is_deletable = is_deletable
model.is_editable = is_editable
model.is_default = is_default
self.session.add(model)
self.session.flush()

Expand Down
4 changes: 2 additions & 2 deletions backend/app/routes/incident_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@router.get("/search", response_model=PaginatedResults[IncidentTypeSchema])
async def incident_types_search(
user: CurrentUser,
_: CurrentUser,
db: DatabaseSession,
organisation: CurrentOrganisation,
):
Expand All @@ -28,7 +28,7 @@ async def incident_types_search(

@router.post("", response_model=IncidentTypeSchema)
async def incident_types_create(
user: CurrentUser,
_: CurrentUser,
db: DatabaseSession,
create_in: CreateIncidentTypeSchema,
organisation: CurrentOrganisation,
Expand Down
1 change: 1 addition & 0 deletions backend/app/schemas/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class IncidentTypeSchema(ModelSchema):
description: str
is_editable: bool
is_deletable: bool
is_default: bool
fields: list["FieldSchema"]


Expand Down
31 changes: 15 additions & 16 deletions backend/app/services/onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class IncidentRoleItemType(TypedDict):


# data files
TIMESTAMPS_SEED_DATA_PATH = "/srv/scripts/timestamps.yaml"
TIMESTAMPS_SEED_DATA_PATH = "/srv/data/timestamps.yaml"
SEVERITIES_SEED_DATA_PATH = "/srv/data/severities.yaml"


class OnboardingService:
Expand Down Expand Up @@ -151,21 +152,17 @@ def _create_form_fields(self, form: Form, field_descriptions: list[dict[str, Any
)

def _setup_severities(self, organisation: Organisation):
descriptors = [
{"name": "Critical", "description": "A critical incident with very high impact"},
{"name": "Major", "description": "A major incident with significant impact"},
{"name": "Minor", "description": "A minor incident with low impact"},
]

for idx, item in enumerate(descriptors):
severity = self.severity_repo.get_severity_by_name(organisation=organisation, name=item["name"])
if not severity:
self.severity_repo.create_severity(
organisation=organisation,
name=item["name"],
description=item["description"],
rating=idx,
)
with open(SEVERITIES_SEED_DATA_PATH) as fp:
data = yaml.load(fp, Loader=yaml.CLoader)
for idx, item in enumerate(data["severities"]):
severity = self.severity_repo.get_severity_by_name(organisation=organisation, name=item["name"])
if not severity:
self.severity_repo.create_severity(
organisation=organisation,
name=item["name"],
description=item["description"],
rating=idx,
)

def _setup_incident_types(self, organisation: Organisation):
descriptors: list[dict[str, Any]] = [
Expand All @@ -174,6 +171,7 @@ def _setup_incident_types(self, organisation: Organisation):
"description": "Default incident type",
"is_deletable": False,
"is_editable": True,
"is_default": True,
},
]

Expand All @@ -186,6 +184,7 @@ def _setup_incident_types(self, organisation: Organisation):
description=item["description"],
is_editable=item["is_editable"],
is_deletable=item["is_deletable"],
is_default=item["is_default"],
)

def _setup_incident_statuses(self, organisation: Organisation):
Expand Down
15 changes: 15 additions & 0 deletions backend/data/severities.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
severities:
- name: SEV-1
description: Critical system failure with severe business impact. Immediate response required. Major outage affecting core services or revenue streams.

- name: SEV-2
description: Significant disruption to important functions. Urgent attention needed. Partial service outage or severe performance degradation.

- name: SEV-3
description: Noticeable issues affecting some users or services. Needs attention soon, but business continues. Minor service degradation or non-critical feature failures.

- name: SEV-4
description: Low-impact problems with minimal urgency. Often cosmetic issues or minor bugs that don't hinder core functionality. Addressed during normal operations.

- name: SEV-5
description: Lowest priority. Usually reserved for future enhancements or very minor issues. "Nice-to-fix" items that don't affect day-to-day operations.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""add default to incident type
Revision ID: 3e7d09e28b97
Revises: 3c885bcaa883
Create Date: 2024-07-26 21:45:52.430021
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision: str = "3e7d09e28b97"
down_revision: Union[str, None] = "3c885bcaa883"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column("incident_type", sa.Column("is_default", sa.Boolean(), nullable=False, server_default="0"))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("incident_type", "is_default")
# ### end Alembic commands ###
13 changes: 12 additions & 1 deletion backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pytest-env = "^1.1.3"
types-pytz = "^2024.1.0.20240417"
black = "^24.4.2"
isort = "^5.13.2"
types-pyyaml = "^6.0.12.20240724"

[build-system]
requires = ["poetry-core"]
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/Incident/DeclareIncidentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,15 @@ const createValidationSchema = (formFields: IFormField[]) => {
return Yup.object().shape(fieldsShape)
}

const createDefaultValues = (formFields: IFormField[]) => {
const createDefaultValues = (formFields: IFormField[], incidentTypes: IIncidentType[]) => {
const defaultValues: Record<string, string> = {}

for (const field of formFields) {
defaultValues[field.id] = ''
if (field.field.kind === FieldKind.INCIDENT_TYPE) {
defaultValues[field.id] = incidentTypes.find((it) => it.isDefault)?.id ?? ''
} else {
defaultValues[field.id] = field.defaultValue ? field.defaultValue : ''
}
}

return defaultValues
Expand Down Expand Up @@ -127,7 +131,7 @@ const DeclareIncidentForm: React.FC<Props> = ({ onSubmit, form }) => {
<Formik
validationSchema={createValidationSchema(form.formFields)}
onSubmit={handleSubmit}
initialValues={createDefaultValues(form.formFields)}
initialValues={createDefaultValues(form.formFields, incidentTypes)}
>
{({ isSubmitting }) => (
<Form className="space-y-2">
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/Layout/SettingsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const RightColumn = styled.div`
margin-top: 8px;
border-top: 1px solid var(--color-gray-200);
border-left: 1px solid var(--color-gray-200);
border-radius: 4px 0 0 0;
`
const LeftColumn = styled.div`
padding: 1rem;
Expand Down
106 changes: 61 additions & 45 deletions frontend/src/components/Sections/SettingsSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useCallback } from 'react'
import { generatePath, Link, useLocation } from 'react-router-dom'
import styled from 'styled-components'
import { generatePath, Link, useMatch } from 'react-router-dom'
import styled, { css } from 'styled-components'

import home from '@/assets/icons/home.svg'
import puzzle from '@/assets/icons/puzzle.svg'
Expand All @@ -17,11 +16,21 @@ const MenuItems = styled.div`
padding: 0;
margin: 0;
`
const Item = styled(Link)`
interface ItemProps {
$selected?: boolean
}

const onSelectItem = css`
background-color: var(--color-gray-100);
border-radius: 0.4rem;
`

const Item = styled(Link)<ItemProps>`
display: block;
color: #1f4d63;
text-decoration: none;
margin-bottom: 1rem;
padding: 0.25rem 0.5rem;
&:visited {
color: #1f4d63;
Expand All @@ -30,6 +39,8 @@ const Item = styled(Link)`
&:hover {
color: var(--color-blue-700);
}
${(props) => props.$selected && onSelectItem}
`
const SubMenu = styled.div`
margin-top: 1rem;
Expand All @@ -51,52 +62,57 @@ interface Props {
user: IUser
}

const SettingsSidebar: React.FC<Props> = () => {
interface MenuItemProps {
to: RoutePaths
}

const MenuItem: React.FC<MenuItemProps & React.PropsWithChildren> = ({ to, children }) => {
const match = useMatch(to)
const { organisation } = useGlobal()
const x = useLocation()
console.log(x.pathname)

const generateSettingsPath = useCallback(
(path: string) => {
return generatePath(path, { organisation: organisation!.slug })
},
[organisation]

// This should not happen
if (!organisation) {
return null
}

return (
<Item to={generatePath(to, { organisation: organisation.slug, id: '' })} $selected={match ? true : false}>
{children}
</Item>
)
}

const SettingsSidebar: React.FC<Props> = () => {
return (
<Root>
{organisation && (
<>
<MenuItems>
<BackContainer>
<Item to={RoutePaths.DASHBOARD}>&laquo; Back to Dashboard</Item>
</BackContainer>

<SubMenu>
<SubMenuTitle>
<Icon icon={home} fixedWidth={true} /> Workspace
</SubMenuTitle>
<SubItems>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_INDEX)}>Overview</Item>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_FIELDS)}>Custom fields</Item>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_ROLES)}>Roles</Item>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_SEVERITY)}>Severities</Item>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_TIMESTAMPS)}>Timestamps</Item>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_TYPES)}>Types</Item>
</SubItems>
</SubMenu>

<SubMenu>
<SubMenuTitle>
<Icon icon={puzzle} fixedWidth={true} /> Integrations
</SubMenuTitle>
<SubItems>
<Item to={generateSettingsPath(RoutePaths.SETTINGS_SLACK)}>Slack</Item>
</SubItems>
</SubMenu>
</MenuItems>
</>
)}
<MenuItems>
<BackContainer>
<Item to={RoutePaths.DASHBOARD}>&laquo; Back to Dashboard</Item>
</BackContainer>

<SubMenu>
<SubMenuTitle>
<Icon icon={home} fixedWidth={true} /> Workspace
</SubMenuTitle>
<SubItems>
<MenuItem to={RoutePaths.SETTINGS_INDEX}>Overview</MenuItem>
<MenuItem to={RoutePaths.SETTINGS_FIELDS}>Custom fields</MenuItem>
<MenuItem to={RoutePaths.SETTINGS_ROLES}>Roles</MenuItem>
<MenuItem to={RoutePaths.SETTINGS_SEVERITY}>Severities</MenuItem>
<MenuItem to={RoutePaths.SETTINGS_TIMESTAMPS}>Timestamps</MenuItem>
<MenuItem to={RoutePaths.SETTINGS_TYPES}>Types</MenuItem>
</SubItems>
</SubMenu>

<SubMenu>
<SubMenuTitle>
<Icon icon={puzzle} fixedWidth={true} /> Integrations
</SubMenuTitle>
<SubItems>
<MenuItem to={RoutePaths.SETTINGS_SLACK}>Slack</MenuItem>
</SubItems>
</SubMenu>
</MenuItems>
</Root>
)
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export interface IIncidentType extends IModel {
description: string
isEditable: boolean
isDeletable: boolean
isDefault: boolean
fields: Array<IField>
}

Expand Down

0 comments on commit c961bb7

Please sign in to comment.