Skip to content

Commit

Permalink
Add incident sorting support to UI
Browse files Browse the repository at this point in the history
  • Loading branch information
VladimirFilonov committed Sep 16, 2024
1 parent 4fe75d6 commit 1b6a1b8
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 39 deletions.
2 changes: 1 addition & 1 deletion docs/openapi.json

Large diffs are not rendered by default.

75 changes: 68 additions & 7 deletions keep-ui/app/incidents/incident-table-component.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,74 @@
import {Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow} from "@tremor/react";
import { flexRender, Table as ReactTable } from "@tanstack/react-table";
import React from "react";
import {Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow} from "@tremor/react";
import {flexRender, Header, Table as ReactTable} from "@tanstack/react-table";
import React, {ReactNode} from "react";
import { IncidentDto } from "./model";
import { useRouter } from "next/navigation";

import {FaArrowDown, FaArrowRight, FaArrowUp} from "react-icons/fa";
interface Props {
table: ReactTable<IncidentDto>;
}

interface SortableHeaderCellProps {
header: Header<IncidentDto, unknown>;
children: ReactNode;
}

const SortableHeaderCell = ({
header,
children,
}: SortableHeaderCellProps) => {

const { column } = header;

return (
<TableHeaderCell
// className="text-tremor-content-strong dark:text-dark-tremor-content-strong"
className={`relative ${
column.getIsPinned() === false ? "hover:bg-slate-100" : ""
} group`}
>
<div className="flex items-center">
{column.getCanSort() && (
<>
<Icon
className="cursor-pointer"
size="xs"
color="neutral"
onClick={(event) => {
console.log("clicked for sorting");
event.stopPropagation();
const toggleSorting = header.column.getToggleSortingHandler();
if (toggleSorting) toggleSorting(event);
}}
tooltip={
column.getNextSortingOrder() === "asc"
? "Sort ascending"
: column.getNextSortingOrder() === "desc"
? "Sort descending"
: "Clear sort"
}
icon= {column.getIsSorted() ? (
column.getIsSorted() === "asc" ? FaArrowDown : FaArrowUp
) : (
FaArrowRight
)}
>
{/* Icon logic */}

</Icon>
{/* Custom styled vertical line separator */}
<div className="w-px h-5 mx-2 bg-gray-400"></div>
</>
)}

{children} {/* Column name or text */}
</div>

</TableHeaderCell>

);
};

export const IncidentTableComponent = (props: Props) => {

const { table } = props;
Expand All @@ -24,15 +85,15 @@ export const IncidentTableComponent = (props: Props) => {
>
{headerGroup.headers.map((header) => {
return (
<TableHeaderCell
className="text-tremor-content-strong dark:text-dark-tremor-content-strong"
<SortableHeaderCell
header={header}
key={header.id}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHeaderCell>
</SortableHeaderCell>
);
})}
</TableRow>
Expand Down
11 changes: 10 additions & 1 deletion keep-ui/app/incidents/incident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IncidentPlaceholder } from "./IncidentPlaceholder";
import Modal from "@/components/ui/Modal";
import { PlusCircleIcon } from "@heroicons/react/24/outline";
import PredictedIncidentsTable from "./predicted-incidents-table";
import {SortingState} from "@tanstack/react-table";

interface Pagination {
limit: number;
Expand All @@ -22,11 +23,17 @@ export default function Incident() {
offset: 0,
});

const [incidentsSorting, setIncidentsSorting] = useState<SortingState>([
{ id: "creation_time", desc: true },
]);

console.log('incidentSorting', incidentsSorting)

const {
data: incidents,
isLoading,
mutate: mutateIncidents,
} = useIncidents(true, incidentsPagination.limit, incidentsPagination.offset);
} = useIncidents(true, incidentsPagination.limit, incidentsPagination.offset, incidentsSorting[0]);
const {
data: predictedIncidents,
isLoading: isPredictedLoading,
Expand Down Expand Up @@ -102,6 +109,8 @@ export default function Incident() {
incidents={incidents}
mutate={mutateIncidents}
setPagination={setIncidentsPagination}
sorting={incidentsSorting}
setSorting={setIncidentsSorting}
editCallback={handleStartEdit}
/>
</Card>
Expand Down
57 changes: 36 additions & 21 deletions keep-ui/app/incidents/incidents-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import {
Badge,
} from "@tremor/react";
import {
DisplayColumnDef,
ExpandedState,
createColumnHelper,
getCoreRowModel,
useReactTable,
SortingState,
getSortedRowModel,
ColumnDef,
} from "@tanstack/react-table";
import { MdRemoveCircle, MdModeEdit } from "react-icons/md";
import { useSession } from "next-auth/react";
Expand All @@ -23,6 +25,8 @@ const columnHelper = createColumnHelper<IncidentDto>();
interface Props {
incidents: PaginatedIncidentsDto;
mutate: () => void;
sorting: SortingState,
setSorting: Dispatch<SetStateAction<any>>;
setPagination: Dispatch<SetStateAction<any>>;
editCallback: (rule: IncidentDto) => void;
}
Expand All @@ -31,6 +35,8 @@ export default function IncidentsTable({
incidents: incidents,
mutate,
setPagination,
sorting,
setSorting,
editCallback,
}: Props) {
const { data: session } = useSession();
Expand Down Expand Up @@ -76,22 +82,21 @@ export default function IncidentsTable({
header: "Group by value",
cell: ({ row }) => <div className="text-wrap">{row.original.rule_fingerprint || "-"}</div>,
}),
// columnHelper.display({
// id: "severity",
// header: "Severity",
// cell: (context) => {
// const severity = context.row.original.severity;
// let color;
// if (severity === "critical") color = "red";
// else if (severity === "info") color = "blue";
// else if (severity === "warning") color = "yellow";
// return <Badge color={color}>{severity}</Badge>;
// },
// }),
columnHelper.display({
id: "alert_count",
columnHelper.accessor("severity", {
id: "severity",
header: "Severity",
cell: (context) => {
const severity = context.row.original.severity;
let color;
if (severity === "critical") color = "red";
else if (severity === "info") color = "blue";
else if (severity === "warning") color = "yellow";
return <Badge color={color}>{severity}</Badge>;
},
}),
columnHelper.accessor("alerts_count", {
id: "alerts_count",
header: "Number of Alerts",
cell: (context) => context.row.original.number_of_alerts,
}),
columnHelper.display({
id: "alert_sources",
Expand Down Expand Up @@ -121,10 +126,10 @@ export default function IncidentsTable({
columnHelper.display({
id: "assignee",
header: "Assignee",
cell: (context) => context.row.original.assignee,
cell: ({row}) => row.original.assignee
}),
columnHelper.display({
id: "created_at",
columnHelper.accessor("creation_time", {
id: "creation_time",
header: "Created At",
cell: ({ row }) =>
new Date(row.original.creation_time + "Z").toLocaleString(),
Expand Down Expand Up @@ -160,17 +165,27 @@ export default function IncidentsTable({
</div>
),
}),
] as DisplayColumnDef<IncidentDto>[];
] as ColumnDef<IncidentDto>[];

const table = useReactTable({
columns,
data: incidents.items,
state: { expanded, pagination },
state: { expanded, pagination, sorting },
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
rowCount: incidents.count,
onPaginationChange: setTablePagination,
onExpandedChange: setExpanded,
onSortingChange: (value) => {
if (typeof value === "function") {
setSorting(value)
}
},
getSortedRowModel: getSortedRowModel(),
enableSorting: true,
enableMultiSort: false,
manualSorting: true,
debugTable: true,
});

return (
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/incidents/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface IncidentDto {
generated_summary: string;
assignee: string;
severity: string;
number_of_alerts: number;
alerts_count: number;
alert_sources: string[];
services: string[];
start_time?: Date;
Expand Down
4 changes: 2 additions & 2 deletions keep-ui/app/incidents/predicted-incidents-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ export default function PredictedIncidentsTable({
cell: ({ row }) => <div className="text-wrap">{row.original.generated_summary}</div>,
}),
columnHelper.display({
id: "alert_count",
id: "alerts_count",
header: "Number of Alerts",
cell: (context) => context.row.original.number_of_alerts,
cell: (context) => context.row.original.alerts_count,
}),
columnHelper.display({
id: "alert_sources",
Expand Down
4 changes: 3 additions & 1 deletion keep-ui/utils/hooks/useIncidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getApiURL } from "utils/apiUrl";
import { fetcher } from "utils/fetcher";
import { useWebsocket } from "./usePusher";
import { useCallback, useEffect } from "react";
import {SortingState} from "@tanstack/react-table";

interface IncidentUpdatePayload {
incident_id: string | null;
Expand All @@ -14,6 +15,7 @@ export const useIncidents = (
confirmed: boolean = true,
limit: number = 25,
offset: number = 0,
sorting: {id: string, desc: boolean} = {id: "creation_time", desc: false},
options: SWRConfiguration = {
revalidateOnFocus: false,
}
Expand All @@ -23,7 +25,7 @@ export const useIncidents = (
return useSWR<PaginatedIncidentsDto>(
() =>
session
? `${apiUrl}/incidents?confirmed=${confirmed}&limit=${limit}&offset=${offset}`
? `${apiUrl}/incidents?confirmed=${confirmed}&limit=${limit}&offset=${offset}&sorting=${sorting.desc ? "-" : ""}${sorting.id}`
: null,
(url) => fetcher(url, session?.accessToken),
options
Expand Down
4 changes: 2 additions & 2 deletions keep/api/models/alert.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ class IncidentDto(IncidentDtoIn):
last_seen_time: datetime.datetime | None
end_time: datetime.datetime | None

number_of_alerts: int
alerts_count: int
alert_sources: list[str]
severity: IncidentSeverity
assignee: str | None
Expand Down Expand Up @@ -407,7 +407,7 @@ def from_db_incident(cls, db_incident):
start_time=db_incident.start_time,
last_seen_time=db_incident.last_seen_time,
end_time=db_incident.end_time,
number_of_alerts=db_incident.alerts_count,
alerts_count=db_incident.alerts_count,
alert_sources=db_incident.sources,
severity=severity,
assignee=db_incident.assignee,
Expand Down
2 changes: 1 addition & 1 deletion keep/providers/opsgenie_provider/opsgenie_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def _query(self, query_type="", query="", **kwargs: dict):

return {
"alerts": alerts.data,
"number_of_alerts": len(alerts.data),
"alerts_count": len(alerts.data),
}


Expand Down
4 changes: 2 additions & 2 deletions tests/test_rules_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def test_incident_attributes(db_session):
assert results is not None
assert len(results) == 1
assert results[0].user_generated_name == "Incident generated by rule {}".format(rules[0].name)
assert results[0].number_of_alerts == i + 1
assert results[0].alerts_count == i + 1
assert results[0].last_seen_time.isoformat(timespec='milliseconds')+"Z" == alert.lastReceived
assert results[0].start_time == alerts[0].timestamp

Expand Down Expand Up @@ -321,7 +321,7 @@ def test_incident_severity(db_session):
assert results is not None
assert len(results) == 1
assert results[0].user_generated_name == "Incident generated by rule {}".format(rules[0].name)
assert results[0].number_of_alerts == 3
assert results[0].alerts_count == 3
assert results[0].severity.value == IncidentSeverity.INFO.value


Expand Down

0 comments on commit 1b6a1b8

Please sign in to comment.