mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 20:38:59 +08:00
Revert "feat: improved the alert rules list search functionality" (#8085)
* Revert "feat: improved the alert rules list search functionality (#8075)" This reverts commit bec52c3d3e3b95dc8ca72bda5c237b3e7b2d02dc. * feat: added search capability for labels * feat: added test cases
This commit is contained in:
parent
1cb01e8dd2
commit
f487f088bd
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Flex, Input, Tooltip, Typography } from 'antd';
|
import { Flex, Input, Typography } from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table/interface';
|
import type { ColumnsType } from 'antd/es/table/interface';
|
||||||
import saveAlertApi from 'api/alerts/save';
|
import saveAlertApi from 'api/alerts/save';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
@ -175,41 +175,6 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
setData(filteredData);
|
setData(filteredData);
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchTooltipContent = (
|
|
||||||
<div style={{ maxWidth: 400 }}>
|
|
||||||
<div style={{ marginBottom: 8, fontWeight: 'bold' }}>Search Options:</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <strong>Plain text:</strong> Search across all fields
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <strong>Key:value pairs:</strong> Specific field matching
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <strong>Multiple terms:</strong> All terms must match (AND logic)
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 8 }}>
|
|
||||||
• <strong>Status mapping:</strong> Use "ok" for inactive alerts
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 8, fontWeight: 'bold' }}>Examples:</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <code>cpu warning</code> - Find alerts with both "cpu" and
|
|
||||||
"warning"
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <code>status:ok</code> - Find alerts with OK status
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <code>severity:critical</code> - Find critical alerts
|
|
||||||
</div>
|
|
||||||
<div style={{ marginBottom: 4 }}>
|
|
||||||
• <code>cluster:prod</code> - Find alerts with cluster=prod label
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
• <code>status:ok cpu</code> - Find OK alerts containing "cpu"
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const dynamicColumns: ColumnsType<GettableAlert> = [
|
const dynamicColumns: ColumnsType<GettableAlert> = [
|
||||||
{
|
{
|
||||||
title: 'Created At',
|
title: 'Created At',
|
||||||
@ -379,22 +344,11 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchContainer>
|
<SearchContainer>
|
||||||
<div className="search-container">
|
|
||||||
<Search
|
<Search
|
||||||
placeholder="Search by name, status, severity, labels or key:value or chaining (e.g. 'status:ok cpu warning')"
|
placeholder="Search by Alert Name, Severity and Labels"
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
defaultValue={searchString}
|
defaultValue={searchString}
|
||||||
prefix={
|
|
||||||
<Flex align="center" gap={8}>
|
|
||||||
<Tooltip title={searchTooltipContent} placement="bottomRight">
|
|
||||||
<InfoCircleOutlined className="search-tooltip" />
|
|
||||||
</Tooltip>
|
|
||||||
<div className="search-divider" />
|
|
||||||
</Flex>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Flex gap={12}>
|
<Flex gap={12}>
|
||||||
{addNewAlert && (
|
{addNewAlert && (
|
||||||
<Button
|
<Button
|
||||||
|
131
frontend/src/container/ListAlertRules/__test__/utils.test.ts
Normal file
131
frontend/src/container/ListAlertRules/__test__/utils.test.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/* eslint-disable sonarjs/no-duplicate-string */
|
||||||
|
import { GettableAlert } from 'types/api/alerts/get';
|
||||||
|
|
||||||
|
import { filterAlerts } from '../utils';
|
||||||
|
|
||||||
|
describe('filterAlerts', () => {
|
||||||
|
const mockAlertBase: Partial<GettableAlert> = {
|
||||||
|
state: 'active',
|
||||||
|
disabled: false,
|
||||||
|
createAt: '2024-01-01T00:00:00Z',
|
||||||
|
createBy: 'test-user',
|
||||||
|
updateAt: '2024-01-01T00:00:00Z',
|
||||||
|
updateBy: 'test-user',
|
||||||
|
version: '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockAlerts: GettableAlert[] = [
|
||||||
|
{
|
||||||
|
...mockAlertBase,
|
||||||
|
id: '1',
|
||||||
|
alert: 'High CPU Usage',
|
||||||
|
alertType: 'metrics',
|
||||||
|
labels: {
|
||||||
|
severity: 'warning',
|
||||||
|
status: 'ok',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
} as GettableAlert,
|
||||||
|
{
|
||||||
|
...mockAlertBase,
|
||||||
|
id: '2',
|
||||||
|
alert: 'Memory Leak Detected',
|
||||||
|
alertType: 'metrics',
|
||||||
|
labels: {
|
||||||
|
severity: 'critical',
|
||||||
|
status: 'firing',
|
||||||
|
environment: 'staging',
|
||||||
|
},
|
||||||
|
} as GettableAlert,
|
||||||
|
{
|
||||||
|
...mockAlertBase,
|
||||||
|
id: '3',
|
||||||
|
alert: 'Database Connection Error',
|
||||||
|
alertType: 'metrics',
|
||||||
|
labels: {
|
||||||
|
severity: 'error',
|
||||||
|
status: 'pending',
|
||||||
|
environment: 'production',
|
||||||
|
},
|
||||||
|
} as GettableAlert,
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should return all alerts when filter is empty', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, '');
|
||||||
|
expect(result).toEqual(mockAlerts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return all alerts when filter is only whitespace', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, ' ');
|
||||||
|
expect(result).toEqual(mockAlerts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter alerts by alert name', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'CPU');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].alert).toBe('High CPU Usage');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter alerts by severity', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'warning');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].labels?.severity).toBe('warning');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter alerts by label key', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'environment');
|
||||||
|
expect(result).toHaveLength(3); // All alerts have environment label
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filter alerts by label value', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'production');
|
||||||
|
expect(result).toHaveLength(2);
|
||||||
|
expect(
|
||||||
|
result.every((alert) => alert.labels?.environment === 'production'),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be case insensitive', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'cpu');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].alert).toBe('High CPU Usage');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle partial matches', () => {
|
||||||
|
const result = filterAlerts(mockAlerts, 'mem');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].alert).toBe('Memory Leak Detected');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle alerts with missing labels', () => {
|
||||||
|
const alertsWithMissingLabels: GettableAlert[] = [
|
||||||
|
{
|
||||||
|
...mockAlertBase,
|
||||||
|
id: '4',
|
||||||
|
alert: 'Test Alert',
|
||||||
|
alertType: 'metrics',
|
||||||
|
labels: undefined,
|
||||||
|
} as GettableAlert,
|
||||||
|
];
|
||||||
|
const result = filterAlerts(alertsWithMissingLabels, 'test');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].alert).toBe('Test Alert');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle alerts with missing alert name', () => {
|
||||||
|
const alertsWithMissingName: GettableAlert[] = [
|
||||||
|
{
|
||||||
|
...mockAlertBase,
|
||||||
|
id: '5',
|
||||||
|
alert: '',
|
||||||
|
alertType: 'metrics',
|
||||||
|
labels: {
|
||||||
|
severity: 'warning',
|
||||||
|
},
|
||||||
|
} as GettableAlert,
|
||||||
|
];
|
||||||
|
const result = filterAlerts(alertsWithMissingName, 'warning');
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].labels?.severity).toBe('warning');
|
||||||
|
});
|
||||||
|
});
|
@ -8,24 +8,6 @@ export const SearchContainer = styled.div`
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2rem;
|
gap: 2rem;
|
||||||
}
|
}
|
||||||
.search-container {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
|
|
||||||
.search-tooltip {
|
|
||||||
color: var(--bg-robin-500);
|
|
||||||
cursor: help;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-divider {
|
|
||||||
height: 16px;
|
|
||||||
margin: 0;
|
|
||||||
border-left: 1px solid var(--bg-slate-100);
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Button = styled(ButtonComponent)`
|
export const Button = styled(ButtonComponent)`
|
||||||
|
@ -1,162 +0,0 @@
|
|||||||
/* eslint-disable sonarjs/no-duplicate-string */
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
|
||||||
import { GettableAlert } from 'types/api/alerts/get';
|
|
||||||
import { EQueryType } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
import { filterAlerts } from './utils';
|
|
||||||
|
|
||||||
const testLabels = { severity: 'warning', cluster: 'prod', test: 'value' };
|
|
||||||
|
|
||||||
const baseAlert: GettableAlert = {
|
|
||||||
id: '1',
|
|
||||||
alert: 'CPU Usage High',
|
|
||||||
state: 'inactive',
|
|
||||||
disabled: false,
|
|
||||||
createAt: '',
|
|
||||||
createBy: '',
|
|
||||||
updateAt: '',
|
|
||||||
updateBy: '',
|
|
||||||
alertType: 'type',
|
|
||||||
ruleType: 'rule',
|
|
||||||
frequency: '1m',
|
|
||||||
condition: {
|
|
||||||
compositeQuery: {
|
|
||||||
builderQueries: {},
|
|
||||||
promQueries: {},
|
|
||||||
chQueries: {},
|
|
||||||
queryType: EQueryType.QUERY_BUILDER,
|
|
||||||
panelType: PANEL_TYPES.TABLE,
|
|
||||||
unit: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
labels: testLabels,
|
|
||||||
annotations: {},
|
|
||||||
evalWindow: '',
|
|
||||||
source: '',
|
|
||||||
preferredChannels: [],
|
|
||||||
broadcastToAll: false,
|
|
||||||
version: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const alerts: GettableAlert[] = [
|
|
||||||
{
|
|
||||||
...baseAlert,
|
|
||||||
id: '1',
|
|
||||||
alert: 'CPU Usage High',
|
|
||||||
state: 'inactive',
|
|
||||||
labels: testLabels,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...baseAlert,
|
|
||||||
id: '2',
|
|
||||||
alert: 'Memory Usage',
|
|
||||||
state: 'firing',
|
|
||||||
labels: { severity: 'critical', cluster: 'dev', test: 'other' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...baseAlert,
|
|
||||||
id: '3',
|
|
||||||
alert: 'Disk IO',
|
|
||||||
state: 'pending',
|
|
||||||
labels: testLabels,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...baseAlert,
|
|
||||||
id: '4',
|
|
||||||
alert: 'Network Latency',
|
|
||||||
state: 'disabled',
|
|
||||||
labels: { severity: 'info', cluster: 'qa', test: 'value' },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('filterAlerts', () => {
|
|
||||||
it('returns all alerts if filter is empty', () => {
|
|
||||||
expect(filterAlerts(alerts, '')).toHaveLength(alerts.length);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by alert name (case-insensitive)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'cpu usage');
|
|
||||||
expect(result).toHaveLength(1);
|
|
||||||
expect(result[0].alert).toBe('CPU Usage High');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by severity', () => {
|
|
||||||
const result = filterAlerts(alerts, 'warning');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by label key or value', () => {
|
|
||||||
const result = filterAlerts(alerts, 'prod');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by multi-word AND search', () => {
|
|
||||||
const result = filterAlerts(alerts, 'cpu prod');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (label)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'test:value');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3', '4']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key: value (label, with space)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'test: value');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3', '4']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (severity)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'severity:warning');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (status:ok)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'status:ok');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (status:inactive)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'status:inactive');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (status:firing)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'status:firing');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['2']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (status:pending)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'status:pending');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (status:disabled)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'status:disabled');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['4']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (cluster:prod)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'cluster:prod');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (cluster:dev)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'cluster:dev');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['2']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by key:value (case-insensitive)', () => {
|
|
||||||
const result = filterAlerts(alerts, 'CLUSTER:PROD');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1', '3']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('matches by combination of word and key:value', () => {
|
|
||||||
const result = filterAlerts(alerts, 'cpu status:ok');
|
|
||||||
expect(result.map((a) => a.id)).toEqual(['1']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns empty if no match', () => {
|
|
||||||
const result = filterAlerts(alerts, 'notfound');
|
|
||||||
expect(result).toHaveLength(0);
|
|
||||||
});
|
|
||||||
});
|
|
@ -3,108 +3,29 @@ import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
|||||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||||
import { GettableAlert } from 'types/api/alerts/get';
|
import { GettableAlert } from 'types/api/alerts/get';
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses key:value pairs from the filter string, allowing optional whitespace after the colon.
|
|
||||||
*/
|
|
||||||
function parseKeyValuePairs(
|
|
||||||
filter: string,
|
|
||||||
): { keyValuePairs: Record<string, string>; filterCopy: string } {
|
|
||||||
// Allow optional whitespace after colon, and support more flexible values
|
|
||||||
const keyValueRegex = /([\w-]+):\s*([^\s]+)/g;
|
|
||||||
const keyValuePairs: Record<string, string> = {};
|
|
||||||
let filterCopy = filter.toLowerCase();
|
|
||||||
const matches = Array.from(filterCopy.matchAll(keyValueRegex));
|
|
||||||
matches.forEach((match) => {
|
|
||||||
const [, key, value] = match;
|
|
||||||
keyValuePairs[key] = value.trim();
|
|
||||||
filterCopy = filterCopy.replace(match[0], '');
|
|
||||||
});
|
|
||||||
return { keyValuePairs, filterCopy };
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusMap: Record<string, string> = {
|
|
||||||
ok: 'inactive',
|
|
||||||
inactive: 'inactive',
|
|
||||||
pending: 'pending',
|
|
||||||
firing: 'firing',
|
|
||||||
disabled: 'disabled',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the alert matches the search words and key-value pairs.
|
|
||||||
*/
|
|
||||||
function alertMatches(alert: GettableAlert, searchWords: string[]): boolean {
|
|
||||||
const alertName = alert.alert?.toLowerCase() || '';
|
|
||||||
const severity = alert.labels?.severity?.toLowerCase() || '';
|
|
||||||
const status = alert.state?.toLowerCase() || '';
|
|
||||||
const labelKeys = Object.keys(alert.labels || {})
|
|
||||||
.filter((e) => e !== 'severity')
|
|
||||||
.map((k) => k.toLowerCase());
|
|
||||||
const labelValues = Object.values(alert.labels || {}).map((v) =>
|
|
||||||
typeof v === 'string' ? v.toLowerCase() : '',
|
|
||||||
);
|
|
||||||
|
|
||||||
const searchable = [
|
|
||||||
alertName,
|
|
||||||
severity,
|
|
||||||
status,
|
|
||||||
...labelKeys,
|
|
||||||
...labelValues,
|
|
||||||
].join(' ');
|
|
||||||
|
|
||||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
||||||
return searchWords.every((word) => {
|
|
||||||
const plainTextMatch = searchable.includes(word);
|
|
||||||
|
|
||||||
// Check if this word is a key:value pair
|
|
||||||
const isKeyValue = word.includes(':');
|
|
||||||
if (isKeyValue) {
|
|
||||||
// For key:value pairs, check if the key:value logic matches
|
|
||||||
const [key, value] = word.split(':');
|
|
||||||
const keyValueMatch = ((): boolean => {
|
|
||||||
if (key === 'severity') {
|
|
||||||
return severity === value;
|
|
||||||
}
|
|
||||||
if (key === 'status') {
|
|
||||||
const mappedStatus = statusMap[value] || value;
|
|
||||||
const labelVal =
|
|
||||||
alert.labels && key in alert.labels ? alert.labels[key] : undefined;
|
|
||||||
return (
|
|
||||||
status === mappedStatus ||
|
|
||||||
(typeof labelVal === 'string' && labelVal.toLowerCase() === value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (alert.labels && key in alert.labels) {
|
|
||||||
const labelVal = alert.labels[key];
|
|
||||||
return typeof labelVal === 'string' && labelVal.toLowerCase() === value;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})();
|
|
||||||
|
|
||||||
// For key:value pairs, match if EITHER plain text OR key:value logic matches
|
|
||||||
return plainTextMatch || keyValueMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For regular words, only plain text matching is required
|
|
||||||
return plainTextMatch;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const filterAlerts = (
|
export const filterAlerts = (
|
||||||
allAlertRules: GettableAlert[],
|
allAlertRules: GettableAlert[],
|
||||||
filter: string,
|
filter: string,
|
||||||
): GettableAlert[] => {
|
): GettableAlert[] => {
|
||||||
if (!filter.trim()) return allAlertRules;
|
if (!filter.trim()) return allAlertRules;
|
||||||
|
|
||||||
const { keyValuePairs, filterCopy } = parseKeyValuePairs(filter);
|
const value = filter.trim().toLowerCase();
|
||||||
// Include both the remaining words AND the original key:value strings as search words
|
return allAlertRules.filter((alert) => {
|
||||||
const remainingWords = filterCopy.split(/\s+/).filter(Boolean);
|
const alertName = alert.alert?.toLowerCase();
|
||||||
const keyValueStrings = Object.entries(keyValuePairs).map(
|
const severity = alert.labels?.severity?.toLowerCase();
|
||||||
([key, value]) => `${key}:${value}`,
|
|
||||||
);
|
|
||||||
const searchWords = [...remainingWords, ...keyValueStrings];
|
|
||||||
|
|
||||||
return allAlertRules.filter((alert) => alertMatches(alert, searchWords));
|
// Create a string of all label keys and values for searching
|
||||||
|
const labelSearchString = Object.entries(alert.labels || {})
|
||||||
|
.map(([key, val]) => `${key} ${val}`)
|
||||||
|
.join(' ')
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
alertName?.includes(value) ||
|
||||||
|
severity?.includes(value) ||
|
||||||
|
labelSearchString.includes(value)
|
||||||
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const alertActionLogEvent = (
|
export const alertActionLogEvent = (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user