Skip to content

Commit

Permalink
Merge branch 'main' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
talboren authored Dec 30, 2024
2 parents 93ee2c0 + 0dc943f commit 299434e
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 69 deletions.
3 changes: 2 additions & 1 deletion keep-ui/app/(keep)/incidents/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { IncidentList } from "@/features/incident-list";
import { getIncidents, GetIncidentsParams } from "@/entities/incidents/api";
import { PaginatedIncidentsDto } from "@/entities/incidents/model";
import { createServerApiClient } from "@/shared/api/server";
import {DefaultIncidentFilters} from "@/entities/incidents/model/models";

const defaultIncidentsParams: GetIncidentsParams = {
confirmed: true,
limit: 20,
offset: 0,
sorting: { id: "creation_time", desc: true },
filters: {},
filters: DefaultIncidentFilters,
};

export default async function Page() {
Expand Down
6 changes: 6 additions & 0 deletions keep-ui/entities/incidents/model/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export enum Status {
Resolved = "resolved",
Acknowledged = "acknowledged",
Merged = "merged",
Deleted = "deleted",
}

export const DefaultIncidentFilteredStatuses: string[] = [Status.Firing, Status.Acknowledged, Status.Merged];
export const DefaultIncidentFilters: object = {
"status": DefaultIncidentFilteredStatuses,
}

export interface IncidentDto {
Expand Down
10 changes: 9 additions & 1 deletion keep-ui/entities/incidents/ui/statuses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ExclamationCircleIcon,
PauseIcon,
} from "@heroicons/react/24/outline";
import { IoIosGitPullRequest } from "react-icons/io";
import {IoIosGitPullRequest, IoIosTrash} from "react-icons/io";
import React from "react";
import { capitalize } from "@/utils/helpers";

Expand Down Expand Up @@ -49,6 +49,14 @@ export const STATUS_ICONS = {
className="w-4 h-4 mr-2"
/>
),
[Status.Deleted]: (
<Icon
icon={IoIosTrash}
tooltip={capitalize(Status.Deleted)}
color="gray"
className="w-4 h-4 mr-2"
/>
),
};

export function StatusIcon({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function IncidentChangeStatusSelect({
const { changeStatus } = useIncidentActions();
const statusOptions = useMemo(
() =>
Object.values(Status).map((status) => ({
Object.values(Status).filter((status) => status != Status.Deleted || value == Status.Deleted).map((status) => ({
value: status,
label: (
<div className="flex items-center">
Expand All @@ -57,7 +57,7 @@ export function IncidentChangeStatusSelect({
</div>
),
})),
[]
[value]
);

const handleChange = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { createContext, useState, FC, PropsWithChildren } from "react";
import { useSearchParams, useRouter, usePathname } from "next/navigation";
import { useIncidentsMeta } from "../../../utils/hooks/useIncidents";
import type { IncidentsMetaDto } from "@/entities/incidents/model";
import { DefaultIncidentFilteredStatuses } from "@/entities/incidents/model/models";

interface IIncidentFilterContext {
meta: IncidentsMetaDto | undefined;
Expand Down Expand Up @@ -43,16 +44,19 @@ export const IncidentFilterContextProvider: FC<PropsWithChildren> = ({
const { data: incidentsMeta, isLoading } = useIncidentsMeta();

const setFilterValue = useCallback(
(filterName: string) => {
(filterName: string, defaultValues: string[] | undefined = undefined) => {
return () => {
if (incidentsMeta === undefined) return [];

const values = searchParams?.get(filterName);
const valuesArray = values
?.split(",")
.filter((value) =>
incidentsMeta[filterName as keyof IncidentsMetaDto]?.includes(value)
);
let valuesArray = values?.split(",")
if (!valuesArray) {
valuesArray = defaultValues;
}

valuesArray = valuesArray?.filter((value) =>
incidentsMeta[filterName as keyof IncidentsMetaDto]?.includes(value)
);

return (valuesArray || []) as string[];
};
Expand All @@ -61,7 +65,7 @@ export const IncidentFilterContextProvider: FC<PropsWithChildren> = ({
);

const [statuses, setStatuses] = useState<string[]>(
setFilterValue("statuses")
setFilterValue("statuses", incidentsMeta?.statuses.filter((status) => DefaultIncidentFilteredStatuses.includes(status)))
);
const [severities, setSeverities] = useState<string[]>(
setFilterValue("severities")
Expand All @@ -76,7 +80,7 @@ export const IncidentFilterContextProvider: FC<PropsWithChildren> = ({

useEffect(() => {
if (!isLoading) {
setStatuses(setFilterValue("statuses"));
setStatuses(setFilterValue("statuses", incidentsMeta?.statuses.filter((status) => DefaultIncidentFilteredStatuses.includes(status))));
setSeverities(setFilterValue("severities"));
setAssignees(setFilterValue("assignees"));
setServices(setFilterValue("services"));
Expand Down
4 changes: 3 additions & 1 deletion keep/api/bl/incidents_bl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@

class IncidentBl:
def __init__(
self, tenant_id: str, session: Session, pusher_client: Optional[Pusher] = None
self, tenant_id: str, session: Session, pusher_client: Optional[Pusher] = None,
user: str = None
):
self.tenant_id = tenant_id
self.user = user
self.session = session
self.pusher_client = pusher_client
self.logger = logging.getLogger(__name__)
Expand Down
38 changes: 14 additions & 24 deletions keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from sqlalchemy.dialects.postgresql import insert as pg_insert
from sqlalchemy.dialects.sqlite import insert as sqlite_insert
from sqlalchemy.exc import IntegrityError, OperationalError
from sqlalchemy.orm import joinedload, selectinload, subqueryload
from sqlalchemy.orm import joinedload, subqueryload
from sqlalchemy.sql import exists, expression
from sqlmodel import Session, SQLModel, col, or_, select, text

Expand Down Expand Up @@ -1786,6 +1786,7 @@ def get_incident_for_grouping_rule(
.where(Incident.rule_id == rule.id)
.where(Incident.rule_fingerprint == rule_fingerprint)
.where(Incident.status != IncidentStatus.RESOLVED.value)
.where(Incident.status != IncidentStatus.DELETED.value)
.order_by(Incident.creation_time.desc())
).first()

Expand Down Expand Up @@ -2921,25 +2922,16 @@ def is_alert_assigned_to_incident(
with Session(engine) as session:
assigned = session.exec(
select(LastAlertToIncident)
.join(Incident, LastAlertToIncident.incident_id == Incident.id)
.where(LastAlertToIncident.fingerprint == fingerprint)
.where(LastAlertToIncident.incident_id == incident_id)
.where(LastAlertToIncident.tenant_id == tenant_id)
.where(LastAlertToIncident.deleted_at == NULL_FOR_DELETED_AT)
.where(Incident.status != IncidentStatus.DELETED.value)
).first()
return assigned is not None


def get_incidents(tenant_id) -> List[Incident]:
with Session(engine) as session:
incidents = session.exec(
select(Incident)
.options(selectinload(Incident.alerts))
.where(Incident.tenant_id == tenant_id)
.order_by(desc(Incident.creation_time))
).all()
return incidents


def get_alert_audit(
tenant_id: str, fingerprint: str | list[str], limit: int = 50
) -> List[AlertAudit]:
Expand Down Expand Up @@ -3493,27 +3485,25 @@ def delete_incident_by_id(
if isinstance(incident_id, str):
incident_id = __convert_to_uuid(incident_id)
with Session(engine) as session:
incident = (
session.query(Incident)
incident = session.exec(
select(Incident)
.filter(
Incident.tenant_id == tenant_id,
Incident.id == incident_id,
)
.first()
)

# Delete all associations with alerts:
).first()

(
session.query(LastAlertToIncident)
session.execute(
update(Incident)
.where(
LastAlertToIncident.tenant_id == tenant_id,
LastAlertToIncident.incident_id == incident.id,
Incident.tenant_id == tenant_id,
Incident.id == incident.id,
)
.delete()
.values({
"status": IncidentStatus.DELETED.value
})
)

session.delete(incident)
session.commit()
return True

Expand Down
2 changes: 2 additions & 0 deletions keep/api/models/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ class IncidentStatus(Enum):
ACKNOWLEDGED = "acknowledged"
# Incident was merged with another incident
MERGED = "merged"
# Incident was removed
DELETED = "deleted"


class IncidentSeverity(SeverityBaseInterface):
Expand Down
62 changes: 30 additions & 32 deletions tests/test_incidents.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti
def test_get_last_incidents(db_session, create_alert):

severity_cycle = cycle([s.order for s in IncidentSeverity])
status_cycle = cycle([s.value for s in IncidentStatus])
status_cycle = cycle([s.value for s in IncidentStatus if s not in [IncidentStatus.MERGED, IncidentStatus.DELETED]])
services_cycle = cycle(["keep", None])

for i in range(60):
Expand All @@ -244,33 +244,31 @@ def test_get_last_incidents(db_session, create_alert):
"status": status,
},
)
# Merged incidents don't have alerts
if status != IncidentStatus.MERGED.value:
create_alert(
f"alert-test-{i}",
AlertStatus(status),
datetime.utcnow(),
{
"severity": AlertSeverity.from_number(severity),
"service": service,
},
)
alert = db_session.query(Alert).order_by(Alert.timestamp.desc()).first()
create_alert(
f"alert-test-{i}",
AlertStatus(status),
datetime.utcnow(),
{
"severity": AlertSeverity.from_number(severity),
"service": service,
},
)
alert = db_session.query(Alert).order_by(Alert.timestamp.desc()).first()

create_alert(
f"alert-test-2-{i}",
AlertStatus(status),
datetime.utcnow(),
{
"severity": AlertSeverity.from_number(severity),
"service": service,
},
)
alert2 = db_session.query(Alert).order_by(Alert.timestamp.desc()).first()
create_alert(
f"alert-test-2-{i}",
AlertStatus(status),
datetime.utcnow(),
{
"severity": AlertSeverity.from_number(severity),
"service": service,
},
)
alert2 = db_session.query(Alert).order_by(Alert.timestamp.desc()).first()

add_alerts_to_incident_by_incident_id(
SINGLE_TENANT_UUID, incident.id, [alert.fingerprint, alert2.fingerprint]
)
add_alerts_to_incident_by_incident_id(
SINGLE_TENANT_UUID, incident.id, [alert.fingerprint, alert2.fingerprint]
)

incidents_default, incidents_default_count = get_last_incidents(SINGLE_TENANT_UUID)
assert len(incidents_default) == 0
Expand Down Expand Up @@ -333,8 +331,8 @@ def test_get_last_incidents(db_session, create_alert):
SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_2, limit=100
)
assert (
len(incidents_with_filters_2) == 15 + 15
) # 15 confirmed, 15 acknowledged because 60 incidents with cycled status
len(incidents_with_filters_2) == 20 + 20
) # 20 confirmed, 20 acknowledged because 60 incidents with cycled status
assert all(
[i.status in ["firing", "acknowledged"] for i in incidents_with_filters_2]
)
Expand All @@ -343,7 +341,7 @@ def test_get_last_incidents(db_session, create_alert):
incidents_with_filters_3, _ = get_last_incidents(
SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_3, limit=100
)
assert len(incidents_with_filters_3) == 45 # 60 minus 15 merged with no alerts
assert len(incidents_with_filters_3) == 60
assert all(["keep" in i.sources for i in incidents_with_filters_3])

filters_4 = {"sources": ["grafana"]}
Expand Down Expand Up @@ -458,7 +456,7 @@ def test_incident_metadata(
db_session, client, test_app, setup_stress_alerts_no_elastic
):
severity_cycle = cycle([s.order for s in IncidentSeverity])
status_cycle = cycle([s.value for s in IncidentStatus])
status_cycle = cycle([s.value for s in IncidentStatus if s != IncidentStatus.DELETED.value])
sources_cycle = cycle(["keep", "keep-test", "keep-test-2"])
services_cycle = cycle(["keep", "keep-test", "keep-test-2"])

Expand Down Expand Up @@ -1177,12 +1175,12 @@ def test_incident_bl_delete_incident(db_session):

incident_dto = incident_bl.create_incident(incident_dto_in)

incidents_count = db_session.query(Incident).count()
incidents_count = db_session.query(Incident).filter(Incident.status != IncidentStatus.DELETED.value).count()
assert incidents_count == 1

incident_bl.delete_incident(incident_dto.id)

incidents_count = db_session.query(Incident).count()
incidents_count = db_session.query(Incident).filter(Incident.status != IncidentStatus.DELETED.value).count()
assert incidents_count == 0

# Check pusher
Expand Down

0 comments on commit 299434e

Please sign in to comment.