Skip to content

Commit b600218

Browse files
authored
fix: topology not showing alert count and a few more (#4524)
1 parent 452e160 commit b600218

File tree

13 files changed

+79
-21
lines changed

13 files changed

+79
-21
lines changed

keep-ui/app/(keep)/topology/model/models.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface TopologyService {
2929
// Added on client to optimize rendering
3030
applications: TopologyApplicationMinimal[];
3131
incidents?: number;
32+
alerts?: number;
3233
is_manual: boolean;
3334
}
3435

@@ -38,7 +39,10 @@ export interface TopologyServiceWithMutator extends TopologyService {
3839

3940
// We need to convert interface to type because only types are allowed in @xyflow/react
4041
// https://github.com/xyflow/web/issues/486
41-
export type ServiceNodeType = Node<InterfaceToType<TopologyServiceWithMutator>, string>;
42+
export type ServiceNodeType = Node<
43+
InterfaceToType<TopologyServiceWithMutator>,
44+
string
45+
>;
4246

4347
export type TopologyNode = ServiceNodeType | Node;
4448

keep-ui/app/(keep)/topology/ui/map/getNodesAndEdgesFromTopologyData.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ import {
1313
} from "@/app/(keep)/topology/ui/map/styles";
1414
import { IncidentDto } from "@/entities/incidents/model";
1515
import { KeyedMutator } from "swr";
16+
import { AlertDto } from "@/entities/alerts/model";
1617

1718
export function getNodesAndEdgesFromTopologyData(
1819
topologyData: TopologyService[],
1920
applicationsMap: Map<string, TopologyApplication>,
2021
allIncidents: IncidentDto[],
22+
allAlerts: AlertDto[],
2123
topologyMutator: KeyedMutator<TopologyService[]>
2224
) {
2325
const nodeMap = new Map<string, TopologyNode>();
@@ -36,6 +38,8 @@ export function getNodesAndEdgesFromTopologyData(
3638
data: {
3739
...service,
3840
incidents: numIncidentsToService.length,
41+
alerts: allAlerts.filter((alert) => alert.service === service.service)
42+
.length,
3943
topologyMutator,
4044
},
4145
position: { x: 0, y: 0 }, // Dagre will handle the actual positioning

keep-ui/app/(keep)/topology/ui/map/service-node.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,17 @@ export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
9999
setIsTooltipReady(true);
100100
}, [showDetails]);
101101

102-
const handleClick = () => {
102+
const handleIncidentClick = () => {
103103
router.push(`/incidents?services=${encodeURIComponent(data.display_name)}`);
104104
};
105105

106+
const handleAlertClick = () => {
107+
const cel = `service=="${data.display_name}"`;
108+
router.push(`/alerts/feed?cel=${encodeURIComponent(cel)}`);
109+
};
110+
106111
const incidentsCount = data.incidents ?? 0;
112+
const alertsCount = data.alerts ?? 0;
107113
const badgeColor =
108114
incidentsCount < THRESHOLD ? "bg-orange-500" : "bg-red-500";
109115

@@ -130,13 +136,22 @@ export function ServiceNode({ data, selected }: NodeProps<ServiceNodeType>) {
130136
</div>
131137
)}
132138
<strong className="text-lg">{data.display_name || data.service}</strong>
133-
{incidentsCount > 0 && (
139+
{incidentsCount > 0 ? (
140+
<span
141+
className={`absolute top-[-17px] right-[-20px] mt-2 mr-2 px-2 py-1 text-white text-[7px] leading-[7px] font-bold rounded-full ${badgeColor} hover:cursor-pointer`}
142+
onClick={handleIncidentClick}
143+
>
144+
{incidentsCount} {incidentsCount === 1 ? "incident" : "incidents"}
145+
</span>
146+
) : alertsCount > 0 ? (
134147
<span
135-
className={`absolute top-[-20px] right-[-20px] mt-2 mr-2 px-2 py-1 text-white text-xs font-bold rounded-full ${badgeColor} hover:cursor-pointer`}
136-
onClick={handleClick}
148+
className={`absolute top-[-17px] right-[-20px] mt-2 mr-2 px-2 py-1 text-white text-[7px] leading-[7px] font-bold rounded-full ${badgeColor} hover:cursor-pointer`}
149+
onClick={handleAlertClick}
137150
>
138-
{incidentsCount}
151+
{alertsCount} {alertsCount === 1 ? "alert" : "alerts"}
139152
</span>
153+
) : (
154+
<></>
140155
)}
141156
<div className="flex flex-wrap gap-1">
142157
{data?.applications?.map((app) => {

keep-ui/app/(keep)/topology/ui/map/topology-map.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import {
7272
import { downloadFileFromString } from "@/shared/lib/downloadFileFromString";
7373
import { PlusIcon } from "@heroicons/react/20/solid";
7474
import { TbTopologyRing } from "react-icons/tb";
75+
import { useAlerts } from "@/entities/alerts/model";
7576

7677
const defaultFitViewOptions: FitViewOptions = {
7778
padding: 0.1,
@@ -451,7 +452,10 @@ export function TopologyMap({
451452

452453
const previousNodesIds = useRef<Set<string>>(new Set());
453454

454-
const { data: allIncidents } = useIncidents();
455+
const { useLastAlerts } = useAlerts();
456+
457+
const { data: allIncidents } = useIncidents(false, false);
458+
const { data: allAlerts } = useLastAlerts(undefined);
455459

456460
useEffect(
457461
function createAndSetLayoutedNodesAndEdges() {
@@ -463,6 +467,7 @@ export function TopologyMap({
463467
topologyData,
464468
applicationMap,
465469
allIncidents?.items ?? [],
470+
allAlerts ?? [],
466471
mutateTopologyData
467472
);
468473

keep-ui/entities/alerts/model/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ export const AlertKnownKeys = [
101101
"ack_status",
102102
"deleted",
103103
"assignee",
104-
"providerId",
105104
"checkbox",
106105
"alertMenu",
107106
"group",

keep-ui/entities/workflows/model/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const V2StepAlertTriggerSchema = z.object({
3434
alert: AlertTriggerValueSchema,
3535
source: z.string().optional(),
3636
}),
37+
only_on_change: z.array(z.string()).optional(),
3738
});
3839

3940
export const IncidentEventEnum = z.enum(["created", "updated", "deleted"]);

keep-ui/widgets/alerts-table/lib/alert-table-utils.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
ColumnRenameMapping,
5151
getColumnDisplayName,
5252
} from "@/widgets/alerts-table/ui/alert-table-column-rename";
53+
import { useProviders } from "@/utils/hooks/useProviders";
5354

5455
export const DEFAULT_COLS = [
5556
"severity",
@@ -221,6 +222,7 @@ export const useAlertTableCols = (
221222
{}
222223
);
223224
const { data: incidents } = useIncidents();
225+
const { data: providersData } = useProviders();
224226
const { isRowExpanded } = useExpandedRows(presetName);
225227
const [columnListFormats, setColumnListFormats] = useLocalStorage<
226228
Record<string, ListFormatOption>
@@ -311,7 +313,12 @@ export const useAlertTableCols = (
311313
</span>
312314
);
313315
}
314-
316+
if (context.column.id === "providerId") {
317+
const provider = providersData?.installed_providers?.find(
318+
(provider) => provider.id === value
319+
);
320+
return provider?.details?.name || value;
321+
}
315322
if (context.column.id === "incident") {
316323
const incidentString = String(value || "");
317324
const incidentSplit = incidentString.split(",");

keep-ui/widgets/alerts-table/ui/ColumnSelection.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ export default function ColumnSelection({
4040
.filter((col) => col.getIsPinned() === false)
4141
.map((col) => col.id);
4242

43-
const selectedColumns = tableColumns
44-
.filter((col) => col.getIsVisible() && col.getIsPinned() === false)
45-
.map((col) => col.id);
46-
4743
const filteredColumns = columnsOptions.filter((column) =>
4844
column.toLowerCase().includes(searchTerm.toLowerCase())
4945
);

keep-ui/widgets/alerts-table/ui/SettingsSelection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function SettingsSelection({
3939
});
4040

4141
return (
42-
<Popover>
42+
<Popover as="div" className="flex items-center">
4343
{({ close }) => (
4444
<>
4545
<Popover.Button

keep-ui/widgets/alerts-table/ui/TitleAndFilters.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,8 @@ export const TitleAndFilters = ({
112112
hasZoomOut={false}
113113
enableYearNavigation
114114
/>
115-
<div className="flex items-center">
116-
<SettingsSelection table={table} presetName={presetName} />
117-
</div>
115+
116+
<SettingsSelection table={table} presetName={presetName} />
118117
</div>
119118
</div>
120119
);

keep/api/models/incident.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,14 @@ def alerts(self) -> List:
123123
from keep.api.core.db import get_incident_alerts_by_incident_id
124124
from keep.api.utils.enrichment_helpers import convert_db_alerts_to_dto_alerts
125125

126-
if not self._tenant_id:
126+
try:
127+
if not self._tenant_id:
128+
return []
129+
except Exception:
130+
logging.getLogger(__name__).error(
131+
"Tenant ID is not set in incident",
132+
extra={"incident_id": self.id},
133+
)
127134
return []
128135
alerts, _ = get_incident_alerts_by_incident_id(self._tenant_id, str(self.id))
129136
return convert_db_alerts_to_dto_alerts(alerts)
@@ -135,7 +142,7 @@ def set_default_values(cls, values: Dict[str, Any]) -> Dict[str, Any]:
135142
try:
136143
values["status"] = IncidentStatus(status)
137144
except ValueError:
138-
logging.warning(
145+
logging.getLogger(__name__).warning(
139146
f"Invalid status value: {status}, setting default.",
140147
extra={"event": values},
141148
)

keep/api/tasks/process_incident_task.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ def process_incident(
119119
"No alerts to add to incident, probably deduplicated",
120120
extra=extra,
121121
)
122+
else:
123+
logger.info(
124+
"No alerts to add to incident",
125+
extra={
126+
**extra,
127+
"incident_id": incident_from_db.id,
128+
"incident_name": incident_from_db.name,
129+
"fingerprint": incident.fingerprint,
130+
},
131+
)
122132
except Exception:
123133
logger.exception("Error adding incident alerts", extra=extra)
124134
logger.info("Processed incident", extra=extra)

keep/providers/pagerduty_provider/pagerduty_provider.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,11 @@ def _get_specific_incident(self, incident_id: str):
849849

850850
def __get_all_incidents_or_alerts(self, incident_id: str = None):
851851
self.logger.info(
852-
"Getting incidents or alerts", extra={"incident_id": incident_id}
852+
"Getting incidents or alerts",
853+
extra={
854+
"incident_id": incident_id,
855+
"tenant_id": self.context_manager.tenant_id,
856+
},
853857
)
854858
paginated_response = []
855859
offset = 0
@@ -879,7 +883,13 @@ def __get_all_incidents_or_alerts(self, incident_id: str = None):
879883
response.raise_for_status()
880884
response = response.json()
881885
except Exception:
882-
self.logger.exception("Failed to get incidents or alerts")
886+
self.logger.exception(
887+
"Failed to get incidents or alerts",
888+
extra={
889+
"incident_id": incident_id,
890+
"tenant_id": self.context_manager.tenant_id,
891+
},
892+
)
883893
raise
884894
offset = response.get("offset", 0)
885895
paginated_response.extend(response.get(resource, []))
@@ -903,6 +913,7 @@ def __get_all_incidents_or_alerts(self, incident_id: str = None):
903913
"Fetched all incidents or alerts",
904914
extra={
905915
"count": len(paginated_response),
916+
"incident_id": incident_id,
906917
"tenant_id": self.context_manager.tenant_id,
907918
},
908919
)

0 commit comments

Comments
 (0)