Skip to content

Commit

Permalink
feat: frontend handles the paginated results from the alerts endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
ohadch committed Oct 22, 2024
1 parent 117518f commit ea290f8
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 103 deletions.
189 changes: 102 additions & 87 deletions keep-ui/app/alerts/models.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,76 @@
import { IncidentDto } from "@/app/incidents/models";

export enum Severity {
Critical = "critical",
High = "high",
Warning = "warning",
Low = "low",
Info = "info",
Error = "error",
Critical = "critical",
High = "high",
Warning = "warning",
Low = "low",
Info = "info",
Error = "error",
}

export const severityMapping: { [id: number]: string } = {
1: Severity.Low,
2: Severity.Info,
3: Severity.Warning,
4: Severity.High,
5: Severity.Critical,
}
1: Severity.Low,
2: Severity.Info,
3: Severity.Warning,
4: Severity.High,
5: Severity.Critical,
};

export const reverseSeverityMapping: { [id: string]: number } = {
[Severity.Low]: 1,
[Severity.Info]: 2,
[Severity.Warning]: 3,
[Severity.High]: 4,
[Severity.Critical]: 5,
}
[Severity.Low]: 1,
[Severity.Info]: 2,
[Severity.Warning]: 3,
[Severity.High]: 4,
[Severity.Critical]: 5,
};

export enum Status {
Firing = "firing",
Resolved = "resolved",
Acknowledged = "acknowledged",
Suppressed = "suppressed",
Pending = "pending",
Firing = "firing",
Resolved = "resolved",
Acknowledged = "acknowledged",
Suppressed = "suppressed",
Pending = "pending",
}


export interface AlertDto {
id: string;
event_id: string;
name: string;
status: Status;
lastReceived: Date;
environment: string;
isDuplicate?: boolean;
duplicateReason?: string;
service?: string;
source: string[];
message?: string;
description?: string;
severity?: Severity;
url?: string;
pushed: boolean;
generatorURL?: string;
fingerprint: string;
deleted: boolean;
dismissed: boolean;
assignee?: string;
ticket_url: string;
ticket_status?: string;
playbook_url?: string;
providerId?: string;
group?: boolean;
note?: string;
isNoisy?: boolean;
enriched_fields: string[];
incident?: string;
id: string;
event_id: string;
name: string;
status: Status;
lastReceived: Date;
environment: string;
isDuplicate?: boolean;
duplicateReason?: string;
service?: string;
source: string[];
message?: string;
description?: string;
severity?: Severity;
url?: string;
pushed: boolean;
generatorURL?: string;
fingerprint: string;
deleted: boolean;
dismissed: boolean;
assignee?: string;
ticket_url: string;
ticket_status?: string;
playbook_url?: string;
providerId?: string;
group?: boolean;
note?: string;
isNoisy?: boolean;
enriched_fields: string[];
incident?: string;

// From AlertWithIncidentLinkMetadataDto
is_created_by_ai?: boolean;
// From AlertWithIncidentLinkMetadataDto
is_created_by_ai?: boolean;
}

interface Option {
readonly label: string;
readonly value: string;
readonly label: string;
readonly value: string;
}

export interface Tag {
Expand All @@ -90,40 +91,54 @@ export interface Preset {
}

export function getTabsFromPreset(preset: Preset): any[] {
const tabsOption = preset.options.find(option => option.label.toLowerCase() === "tabs");
return tabsOption && Array.isArray(tabsOption.value) ? tabsOption.value : [];
const tabsOption = preset.options.find(
(option) => option.label.toLowerCase() === "tabs"
);
return tabsOption && Array.isArray(tabsOption.value) ? tabsOption.value : [];
}

export interface AlertToWorkflowExecution {
workflow_id: string;
workflow_execution_id: string;
alert_fingerprint: string;
workflow_status: "timeout" | "in_progress" | "success" | "error" | "providers_not_configured";
workflow_started: Date;
workflow_id: string;
workflow_execution_id: string;
alert_fingerprint: string;
workflow_status:
| "timeout"
| "in_progress"
| "success"
| "error"
| "providers_not_configured";
workflow_started: Date;
}

export const AlertKnownKeys = [
"id",
"name",
"status",
"lastReceived",
"isDuplicate",
"duplicateReason",
"source",
"description",
"severity",
"pushed",
"url",
"event_id",
"ticket_url",
"playbook_url",
"ack_status",
"deleted",
"assignee",
"providerId",
"checkbox",
"alertMenu",
"group",
"extraPayload",
"note",
"id",
"name",
"status",
"lastReceived",
"isDuplicate",
"duplicateReason",
"source",
"description",
"severity",
"pushed",
"url",
"event_id",
"ticket_url",
"playbook_url",
"ack_status",
"deleted",
"assignee",
"providerId",
"checkbox",
"alertMenu",
"group",
"extraPayload",
"note",
];

export interface PaginatedAlertsDto {
limit: number;
offset: number;
count: number;
items: AlertDto[];
}
15 changes: 10 additions & 5 deletions keep-ui/utils/hooks/useAlerts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import { AlertDto } from "app/alerts/models";
import { AlertDto, PaginatedAlertsDto } from "app/alerts/models";
import { useSession } from "next-auth/react";
import useSWR, { SWRConfiguration } from "swr";
import { getApiURL } from "utils/apiUrl";
Expand Down Expand Up @@ -41,7 +41,7 @@ export const useAlerts = () => {
presetName: string,
options: SWRConfiguration = { revalidateOnFocus: false }
) => {
return useSWR<AlertDto[]>(
return useSWR<PaginatedAlertsDto>(
() =>
session && presetName ? `${apiUrl}/preset/${presetName}/alerts` : null,
(url) => fetcher(url, session?.accessToken),
Expand All @@ -58,15 +58,20 @@ export const useAlerts = () => {
);

const {
data: alertsFromEndpoint = [],
data: alertsFromEndpoint = {
limit: 0,
offset: 0,
count: 0,
items: [],
},
mutate,
isLoading,
} = useAllAlerts(presetName, options);

useEffect(() => {
if (alertsFromEndpoint.length) {
if (alertsFromEndpoint.items.length) {
const newAlertsMap = new Map<string, AlertDto>(
alertsFromEndpoint.map((alertFromEndpoint) => [
alertsFromEndpoint.items.map((alertFromEndpoint) => [
alertFromEndpoint.fingerprint,
{
...alertFromEndpoint,
Expand Down
1 change: 1 addition & 0 deletions keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ def get_last_alerts(

# Order by timestamp in descending order and limit the results
query = query.order_by(desc(Alert.timestamp)).limit(limit).offset(offset)

# Execute the query
alerts_with_start = query.all()
# Convert result to list of Alert objects and include "startedAt" information if needed
Expand Down
6 changes: 5 additions & 1 deletion keep/api/routes/alerts.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ def get_all_alerts(
"tenant_id": tenant_id,
},
)
db_alerts, total_count = get_last_alerts(tenant_id=tenant_id, limit=limit)
db_alerts, total_count = get_last_alerts(
tenant_id=tenant_id,
limit=limit,
offset=offset,
)
enriched_alerts_dto = convert_db_alerts_to_dto_alerts(db_alerts)
logger.info(
"Fetched alerts from DB",
Expand Down
21 changes: 12 additions & 9 deletions keep/api/routes/preset.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import json
import logging
import os
import uuid
from datetime import datetime
from typing import Optional

from fastapi import (
APIRouter,
BackgroundTasks,
Depends,
HTTPException,
Query,
Request,
Response,
Query
)
from pydantic import BaseModel
from sqlmodel import Session, select
Expand All @@ -24,7 +26,6 @@
update_provider_last_pull_time,
)
from keep.api.models.alert import AlertDto
from keep.api.models.time_stamp import TimeStampFilter
from keep.api.models.db.preset import (
Preset,
PresetDto,
Expand All @@ -33,15 +34,16 @@
Tag,
TagDto,
)
from keep.api.models.time_stamp import TimeStampFilter
from keep.api.tasks.process_event_task import process_event
from keep.api.tasks.process_topology_task import process_topology
from keep.api.utils.pagination import AlertPaginatedResultsDto
from keep.contextmanager.contextmanager import ContextManager
from keep.identitymanager.authenticatedentity import AuthenticatedEntity
from keep.identitymanager.identitymanagerfactory import IdentityManagerFactory
from keep.providers.base.base_provider import BaseTopologyProvider
from keep.providers.providers_factory import ProvidersFactory
from keep.searchengine.searchengine import SearchEngine
import json

router = APIRouter()
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -173,9 +175,7 @@ def pull_data_from_providers(


# Function to handle the time_stamp query parameter and parse it
def _get_time_stamp_filter(
time_stamp: Optional[str] = Query(None)
) -> TimeStampFilter:
def _get_time_stamp_filter(time_stamp: Optional[str] = Query(None)) -> TimeStampFilter:
if time_stamp:
try:
# Parse the JSON string
Expand All @@ -186,6 +186,7 @@ def _get_time_stamp_filter(
raise HTTPException(status_code=400, detail="Invalid time_stamp format")
return TimeStampFilter()


@router.get(
"",
description="Get all presets for tenant",
Expand All @@ -195,7 +196,7 @@ def get_presets(
IdentityManagerFactory.get_auth_verifier(["read:preset"])
),
session: Session = Depends(get_session),
time_stamp: TimeStampFilter = Depends(_get_time_stamp_filter)
time_stamp: TimeStampFilter = Depends(_get_time_stamp_filter),
) -> list[PresetDto]:
tenant_id = authenticated_entity.tenant_id
logger.info(f"Getting all presets {time_stamp}")
Expand Down Expand Up @@ -224,7 +225,9 @@ def get_presets(
# get the number of alerts + noisy alerts for each preset
search_engine = SearchEngine(tenant_id=tenant_id)
# get the preset metatada
presets_dto = search_engine.search_preset_alerts(presets=presets_dto, time_stamp=time_stamp)
presets_dto = search_engine.search_preset_alerts(
presets=presets_dto, time_stamp=time_stamp
)

return presets_dto

Expand Down Expand Up @@ -414,7 +417,7 @@ def get_preset_alerts(
authenticated_entity: AuthenticatedEntity = Depends(
IdentityManagerFactory.get_auth_verifier(["read:presets"])
),
) -> list:
) -> AlertPaginatedResultsDto:

# Gathering alerts may take a while and we don't care if it will finish before we return the response.
# In the worst case, gathered alerts will be pulled in the next request.
Expand Down
7 changes: 6 additions & 1 deletion keep/searchengine/searchengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,11 @@ def search_alerts_by_cel(
)

def _search_alerts_by_sql(
self, sql_query: dict, limit=1000, offset=0, timeframe: int = 0
self,
sql_query: dict,
limit=1000,
offset=0,
timeframe: int = 0,
) -> AlertPaginatedResultsDto:
"""Search for alerts based on a SQL query
Expand Down Expand Up @@ -206,6 +210,7 @@ def search_alerts(self, query: PresetSearchQuery) -> AlertPaginatedResultsDto:
limit=query.limit,
offset=query.offset,
timeframe=query.timeframe,
sorting=query.sorting,
)
else:
self.logger.error("Invalid search mode")
Expand Down

0 comments on commit ea290f8

Please sign in to comment.