mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
chore: link to docs, QB flavour for expr and update options (#9246)
This commit is contained in:
parent
f9a70a3a69
commit
1b1aa4915b
@ -4,8 +4,10 @@ import '../EvaluationSettings/styles.scss';
|
|||||||
import { Button, Select, Tooltip, Typography } from 'antd';
|
import { Button, Select, Tooltip, Typography } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import getRandomColor from 'lib/getRandomColor';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { useCreateAlertState } from '../context';
|
import { useCreateAlertState } from '../context';
|
||||||
import {
|
import {
|
||||||
@ -68,11 +70,15 @@ function AlertThreshold({
|
|||||||
const addThreshold = (): void => {
|
const addThreshold = (): void => {
|
||||||
let newThreshold;
|
let newThreshold;
|
||||||
if (thresholdState.thresholds.length === 1) {
|
if (thresholdState.thresholds.length === 1) {
|
||||||
newThreshold = INITIAL_WARNING_THRESHOLD;
|
newThreshold = { ...INITIAL_WARNING_THRESHOLD, id: v4() };
|
||||||
} else if (thresholdState.thresholds.length === 2) {
|
} else if (thresholdState.thresholds.length === 2) {
|
||||||
newThreshold = INITIAL_INFO_THRESHOLD;
|
newThreshold = { ...INITIAL_INFO_THRESHOLD, id: v4() };
|
||||||
} else {
|
} else {
|
||||||
newThreshold = INITIAL_RANDOM_THRESHOLD;
|
newThreshold = {
|
||||||
|
...INITIAL_RANDOM_THRESHOLD,
|
||||||
|
id: v4(),
|
||||||
|
color: getRandomColor(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
setThresholdState({
|
setThresholdState({
|
||||||
type: 'SET_THRESHOLDS',
|
type: 'SET_THRESHOLDS',
|
||||||
|
|||||||
@ -141,7 +141,7 @@ describe('Footer utils', () => {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: [],
|
alertStates: [],
|
||||||
},
|
},
|
||||||
usePolicy: false,
|
usePolicy: false,
|
||||||
@ -154,7 +154,7 @@ describe('Footer utils', () => {
|
|||||||
...INITIAL_NOTIFICATION_SETTINGS_STATE,
|
...INITIAL_NOTIFICATION_SETTINGS_STATE,
|
||||||
reNotification: {
|
reNotification: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
value: 1,
|
value: 30,
|
||||||
unit: UniversalYAxisUnit.MINUTES,
|
unit: UniversalYAxisUnit.MINUTES,
|
||||||
conditions: ['firing'],
|
conditions: ['firing'],
|
||||||
},
|
},
|
||||||
@ -165,7 +165,7 @@ describe('Footer utils', () => {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: ['firing'],
|
alertStates: ['firing'],
|
||||||
},
|
},
|
||||||
usePolicy: false,
|
usePolicy: false,
|
||||||
@ -183,7 +183,7 @@ describe('Footer utils', () => {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: [],
|
alertStates: [],
|
||||||
},
|
},
|
||||||
usePolicy: true,
|
usePolicy: true,
|
||||||
@ -201,7 +201,7 @@ describe('Footer utils', () => {
|
|||||||
groupBy: ['test group'],
|
groupBy: ['test group'],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: [],
|
alertStates: [],
|
||||||
},
|
},
|
||||||
usePolicy: false,
|
usePolicy: false,
|
||||||
@ -495,7 +495,7 @@ describe('Footer utils', () => {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: [],
|
alertStates: [],
|
||||||
},
|
},
|
||||||
usePolicy: false,
|
usePolicy: false,
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { Input, Select, Typography } from 'antd';
|
|||||||
|
|
||||||
import { useCreateAlertState } from '../context';
|
import { useCreateAlertState } from '../context';
|
||||||
import {
|
import {
|
||||||
ADVANCED_OPTIONS_TIME_UNIT_OPTIONS as RE_NOTIFICATION_UNIT_OPTIONS,
|
|
||||||
RE_NOTIFICATION_CONDITION_OPTIONS,
|
RE_NOTIFICATION_CONDITION_OPTIONS,
|
||||||
|
RE_NOTIFICATION_TIME_UNIT_OPTIONS,
|
||||||
} from '../context/constants';
|
} from '../context/constants';
|
||||||
import AdvancedOptionItem from '../EvaluationSettings/AdvancedOptionItem';
|
import AdvancedOptionItem from '../EvaluationSettings/AdvancedOptionItem';
|
||||||
import Stepper from '../Stepper';
|
import Stepper from '../Stepper';
|
||||||
@ -45,7 +45,7 @@ function NotificationSettings(): JSX.Element {
|
|||||||
value={notificationSettings.reNotification.unit || null}
|
value={notificationSettings.reNotification.unit || null}
|
||||||
placeholder="Select unit"
|
placeholder="Select unit"
|
||||||
disabled={!notificationSettings.reNotification.enabled}
|
disabled={!notificationSettings.reNotification.enabled}
|
||||||
options={RE_NOTIFICATION_UNIT_OPTIONS}
|
options={RE_NOTIFICATION_TIME_UNIT_OPTIONS}
|
||||||
onChange={(value): void => {
|
onChange={(value): void => {
|
||||||
setNotificationSettings({
|
setNotificationSettings({
|
||||||
type: 'SET_RE_NOTIFICATION',
|
type: 'SET_RE_NOTIFICATION',
|
||||||
|
|||||||
@ -51,7 +51,6 @@ function ChartPreview({ alertDef }: ChartPreviewProps): JSX.Element {
|
|||||||
yAxisUnit={yAxisUnit || ''}
|
yAxisUnit={yAxisUnit || ''}
|
||||||
graphType={panelType || PANEL_TYPES.TIME_SERIES}
|
graphType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||||
setQueryStatus={setQueryStatus}
|
setQueryStatus={setQueryStatus}
|
||||||
showSideLegend
|
|
||||||
additionalThresholds={thresholdState.thresholds}
|
additionalThresholds={thresholdState.thresholds}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -66,7 +65,6 @@ function ChartPreview({ alertDef }: ChartPreviewProps): JSX.Element {
|
|||||||
yAxisUnit={yAxisUnit || ''}
|
yAxisUnit={yAxisUnit || ''}
|
||||||
graphType={panelType || PANEL_TYPES.TIME_SERIES}
|
graphType={panelType || PANEL_TYPES.TIME_SERIES}
|
||||||
setQueryStatus={setQueryStatus}
|
setQueryStatus={setQueryStatus}
|
||||||
showSideLegend
|
|
||||||
additionalThresholds={thresholdState.thresholds}
|
additionalThresholds={thresholdState.thresholds}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -216,7 +216,7 @@ describe('CreateAlertV2 utils', () => {
|
|||||||
multipleNotifications: ['email'],
|
multipleNotifications: ['email'],
|
||||||
reNotification: {
|
reNotification: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
value: 1,
|
value: 30,
|
||||||
unit: UniversalYAxisUnit.MINUTES,
|
unit: UniversalYAxisUnit.MINUTES,
|
||||||
conditions: [],
|
conditions: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const defaultNotificationSettings: PostableAlertRuleV2['notificationSettings'] =
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
renotify: {
|
renotify: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
interval: '1m',
|
interval: '30m',
|
||||||
alertStates: [],
|
alertStates: [],
|
||||||
},
|
},
|
||||||
usePolicy: false,
|
usePolicy: false,
|
||||||
|
|||||||
@ -172,6 +172,11 @@ export const ADVANCED_OPTIONS_TIME_UNIT_OPTIONS = [
|
|||||||
{ value: UniversalYAxisUnit.HOURS, label: 'Hours' },
|
{ value: UniversalYAxisUnit.HOURS, label: 'Hours' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const RE_NOTIFICATION_TIME_UNIT_OPTIONS = [
|
||||||
|
{ value: UniversalYAxisUnit.MINUTES, label: 'Minutes' },
|
||||||
|
{ value: UniversalYAxisUnit.HOURS, label: 'Hours' },
|
||||||
|
];
|
||||||
|
|
||||||
export const NOTIFICATION_MESSAGE_PLACEHOLDER =
|
export const NOTIFICATION_MESSAGE_PLACEHOLDER =
|
||||||
'This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})';
|
'This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})';
|
||||||
|
|
||||||
@ -184,7 +189,7 @@ export const INITIAL_NOTIFICATION_SETTINGS_STATE: NotificationSettingsState = {
|
|||||||
multipleNotifications: [],
|
multipleNotifications: [],
|
||||||
reNotification: {
|
reNotification: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
value: 1,
|
value: 30,
|
||||||
unit: UniversalYAxisUnit.MINUTES,
|
unit: UniversalYAxisUnit.MINUTES,
|
||||||
conditions: [],
|
conditions: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -198,10 +198,10 @@ export function getNotificationSettingsStateFromAlertDef(
|
|||||||
(state) => state as 'firing' | 'nodata',
|
(state) => state as 'firing' | 'nodata',
|
||||||
) || [];
|
) || [];
|
||||||
const reNotificationValue = alertDef.notificationSettings?.renotify
|
const reNotificationValue = alertDef.notificationSettings?.renotify
|
||||||
? parseGoTime(alertDef.notificationSettings.renotify.interval || '1m').time
|
? parseGoTime(alertDef.notificationSettings.renotify.interval || '30m').time
|
||||||
: 1;
|
: 30;
|
||||||
const reNotificationUnit = alertDef.notificationSettings?.renotify
|
const reNotificationUnit = alertDef.notificationSettings?.renotify
|
||||||
? parseGoTime(alertDef.notificationSettings.renotify.interval || '1m').unit
|
? parseGoTime(alertDef.notificationSettings.renotify.interval || '30m').unit
|
||||||
: UniversalYAxisUnit.MINUTES;
|
: UniversalYAxisUnit.MINUTES;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import { Button, Dropdown, Flex, Input, MenuProps, Typography } from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
Dropdown,
|
||||||
|
Flex,
|
||||||
|
Input,
|
||||||
|
MenuProps,
|
||||||
|
Tag,
|
||||||
|
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';
|
||||||
@ -118,12 +126,16 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
|
|||||||
const newAlertMenuItems: MenuProps['items'] = [
|
const newAlertMenuItems: MenuProps['items'] = [
|
||||||
{
|
{
|
||||||
key: 'new',
|
key: 'new',
|
||||||
label: 'Try the new experience',
|
label: (
|
||||||
|
<span>
|
||||||
|
Try the new experience <Tag color="blue">Beta</Tag>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
onClick: onClickNewAlertV2Handler,
|
onClick: onClickNewAlertV2Handler,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'classic',
|
key: 'classic',
|
||||||
label: 'Continue with the current experience',
|
label: 'Continue with the classic experience',
|
||||||
onClick: onClickNewClassicAlertHandler,
|
onClick: onClickNewClassicAlertHandler,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -88,6 +88,7 @@ function RoutingPolicies(): JSX.Element {
|
|||||||
isRoutingPoliciesError={isErrorRoutingPolicies}
|
isRoutingPoliciesError={isErrorRoutingPolicies}
|
||||||
handlePolicyDetailsModalOpen={handlePolicyDetailsModalOpen}
|
handlePolicyDetailsModalOpen={handlePolicyDetailsModalOpen}
|
||||||
handleDeleteModalOpen={handleDeleteModalOpen}
|
handleDeleteModalOpen={handleDeleteModalOpen}
|
||||||
|
hasSearchTerm={(searchTerm?.length ?? 0) > 0}
|
||||||
/>
|
/>
|
||||||
{policyDetailsModalState.isOpen && (
|
{policyDetailsModalState.isOpen && (
|
||||||
<RoutingPolicyDetails
|
<RoutingPolicyDetails
|
||||||
|
|||||||
@ -10,6 +10,7 @@ function RoutingPolicyList({
|
|||||||
isRoutingPoliciesError,
|
isRoutingPoliciesError,
|
||||||
handlePolicyDetailsModalOpen,
|
handlePolicyDetailsModalOpen,
|
||||||
handleDeleteModalOpen,
|
handleDeleteModalOpen,
|
||||||
|
hasSearchTerm,
|
||||||
}: RoutingPolicyListProps): JSX.Element {
|
}: RoutingPolicyListProps): JSX.Element {
|
||||||
const columns: TableProps<RoutingPolicy>['columns'] = [
|
const columns: TableProps<RoutingPolicy>['columns'] = [
|
||||||
{
|
{
|
||||||
@ -25,6 +26,7 @@ function RoutingPolicyList({
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
const localeEmptyState = useMemo(
|
const localeEmptyState = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<div className="no-routing-policies-message-container">
|
<div className="no-routing-policies-message-container">
|
||||||
@ -41,12 +43,23 @@ function RoutingPolicyList({
|
|||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
Something went wrong while fetching routing policies.
|
Something went wrong while fetching routing policies.
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
) : hasSearchTerm ? (
|
||||||
|
<Typography.Text>No matching routing policies found.</Typography.Text>
|
||||||
) : (
|
) : (
|
||||||
<Typography.Text>No routing policies found.</Typography.Text>
|
<Typography.Text>
|
||||||
|
No routing policies yet,{' '}
|
||||||
|
<a
|
||||||
|
href="https://signoz.io/docs/alerts-management/routing-policy"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
Learn more here
|
||||||
|
</a>
|
||||||
|
</Typography.Text>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
[isRoutingPoliciesError],
|
[isRoutingPoliciesError, hasSearchTerm],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -28,6 +28,7 @@ describe('RoutingPoliciesList', () => {
|
|||||||
isRoutingPoliciesError={useRoutingPolicesMockData.isErrorRoutingPolicies}
|
isRoutingPoliciesError={useRoutingPolicesMockData.isErrorRoutingPolicies}
|
||||||
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
||||||
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
||||||
|
hasSearchTerm={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ describe('RoutingPoliciesList', () => {
|
|||||||
isRoutingPoliciesError={false}
|
isRoutingPoliciesError={false}
|
||||||
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
||||||
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
||||||
|
hasSearchTerm={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
// Check for loading spinner by class name
|
// Check for loading spinner by class name
|
||||||
@ -67,6 +69,7 @@ describe('RoutingPoliciesList', () => {
|
|||||||
isRoutingPoliciesError
|
isRoutingPoliciesError
|
||||||
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
||||||
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
||||||
|
hasSearchTerm={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
@ -82,8 +85,9 @@ describe('RoutingPoliciesList', () => {
|
|||||||
isRoutingPoliciesError={false}
|
isRoutingPoliciesError={false}
|
||||||
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
handlePolicyDetailsModalOpen={mockHandlePolicyDetailsModalOpen}
|
||||||
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
handleDeleteModalOpen={mockHandleDeleteModalOpen}
|
||||||
|
hasSearchTerm={false}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
expect(screen.getByText('No routing policies found.')).toBeInTheDocument();
|
expect(screen.getByText('No routing policies yet,')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export interface RoutingPolicyListProps {
|
|||||||
isRoutingPoliciesError: boolean;
|
isRoutingPoliciesError: boolean;
|
||||||
handlePolicyDetailsModalOpen: HandlePolicyDetailsModalOpen;
|
handlePolicyDetailsModalOpen: HandlePolicyDetailsModalOpen;
|
||||||
handleDeleteModalOpen: HandleDeleteModalOpen;
|
handleDeleteModalOpen: HandleDeleteModalOpen;
|
||||||
|
hasSearchTerm: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoutingPolicyListItemProps {
|
export interface RoutingPolicyListItemProps {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -338,3 +338,5 @@ require (
|
|||||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/expr-lang/expr => github.com/SigNoz/expr v1.17.7-beta
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -102,6 +102,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4
|
|||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/SigNoz/expr v1.17.7-beta h1:FyZkleM5dTQ0O6muQfwGpoH5A2ohmN/XTasRCO72gAA=
|
||||||
|
github.com/SigNoz/expr v1.17.7-beta/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
||||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8=
|
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkbj57eGXx8H3ZJ4zhmQXBnrW523ktj8=
|
||||||
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc=
|
github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc=
|
||||||
github.com/SigNoz/signoz-otel-collector v0.129.4 h1:DGDu9y1I1FU+HX4eECPGmfhnXE4ys4yr7LL6znbf6to=
|
github.com/SigNoz/signoz-otel-collector v0.129.4 h1:DGDu9y1I1FU+HX4eECPGmfhnXE4ys4yr7LL6znbf6to=
|
||||||
@ -248,8 +250,6 @@ github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||||
github.com/expr-lang/expr v1.17.5 h1:i1WrMvcdLF249nSNlpQZN1S6NXuW9WaOfF5tPi3aw3k=
|
|
||||||
github.com/expr-lang/expr v1.17.5/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
|
|
||||||
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM=
|
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM=
|
||||||
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc=
|
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
|||||||
@ -295,6 +295,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "simple equality check - match",
|
||||||
|
expression: `threshold.name = 'auth' AND ruleId = 'rule1'`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"threshold.name": "auth",
|
||||||
|
"ruleId": "rule1",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "simple equality check - no match",
|
name: "simple equality check - no match",
|
||||||
expression: `service == "payment"`,
|
expression: `service == "payment"`,
|
||||||
@ -304,6 +313,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "simple equality check - no match",
|
||||||
|
expression: `service = "payment"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
"env": "production",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple conditions with AND - both match",
|
name: "multiple conditions with AND - both match",
|
||||||
expression: `service == "auth" && env == "production"`,
|
expression: `service == "auth" && env == "production"`,
|
||||||
@ -313,6 +331,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple conditions with AND - both match",
|
||||||
|
expression: `service = "auth" AND env = "production"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
"env": "production",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple conditions with AND - one doesn't match",
|
name: "multiple conditions with AND - one doesn't match",
|
||||||
expression: `service == "auth" && env == "staging"`,
|
expression: `service == "auth" && env == "staging"`,
|
||||||
@ -322,6 +349,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple conditions with AND - one doesn't match",
|
||||||
|
expression: `service = "auth" AND env = "staging"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
"env": "production",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple conditions with OR - one matches",
|
name: "multiple conditions with OR - one matches",
|
||||||
expression: `service == "payment" || env == "production"`,
|
expression: `service == "payment" || env == "production"`,
|
||||||
@ -331,6 +367,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple conditions with OR - one matches",
|
||||||
|
expression: `service = "payment" OR env = "production"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
"env": "production",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "multiple conditions with OR - none match",
|
name: "multiple conditions with OR - none match",
|
||||||
expression: `service == "payment" || env == "staging"`,
|
expression: `service == "payment" || env == "staging"`,
|
||||||
@ -340,6 +385,15 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "multiple conditions with OR - none match",
|
||||||
|
expression: `service = "payment" OR env = "staging"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
"env": "production",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "in operator - value in list",
|
name: "in operator - value in list",
|
||||||
expression: `service in ["auth", "payment", "notification"]`,
|
expression: `service in ["auth", "payment", "notification"]`,
|
||||||
@ -348,6 +402,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "in operator - value in list",
|
||||||
|
expression: `service IN ["auth", "payment", "notification"]`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "in operator - value not in list",
|
name: "in operator - value not in list",
|
||||||
expression: `service in ["payment", "notification"]`,
|
expression: `service in ["payment", "notification"]`,
|
||||||
@ -356,6 +418,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "in operator - value not in list",
|
||||||
|
expression: `service IN ["payment", "notification"]`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "contains operator - substring match",
|
name: "contains operator - substring match",
|
||||||
expression: `host contains "prod"`,
|
expression: `host contains "prod"`,
|
||||||
@ -364,6 +434,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "contains operator - substring match",
|
||||||
|
expression: `host CONTAINS "prod"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"host": "prod-server-01",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "contains operator - no substring match",
|
name: "contains operator - no substring match",
|
||||||
expression: `host contains "staging"`,
|
expression: `host contains "staging"`,
|
||||||
@ -372,6 +450,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "contains operator - no substring match",
|
||||||
|
expression: `host CONTAINS "staging"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"host": "prod-server-01",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "complex expression with parentheses",
|
name: "complex expression with parentheses",
|
||||||
expression: `(service == "auth" && env == "production") || critical == "true"`,
|
expression: `(service == "auth" && env == "production") || critical == "true"`,
|
||||||
@ -382,6 +468,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "complex expression with parentheses",
|
||||||
|
expression: `(service = "auth" AND env = "production") OR critical = "true"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "payment",
|
||||||
|
"env": "staging",
|
||||||
|
"critical": "true",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "missing label key",
|
name: "missing label key",
|
||||||
expression: `"missing_key" == "value"`,
|
expression: `"missing_key" == "value"`,
|
||||||
@ -390,6 +486,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "missing label key",
|
||||||
|
expression: `"missing_key" = "value"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth",
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "rule-based expression with threshold name and ruleId",
|
name: "rule-based expression with threshold name and ruleId",
|
||||||
expression: `'threshold.name' == "high-cpu" && ruleId == "rule-123"`,
|
expression: `'threshold.name' == "high-cpu" && ruleId == "rule-123"`,
|
||||||
@ -400,6 +504,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false, //no commas
|
expected: false, //no commas
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "rule-based expression with threshold name and ruleId",
|
||||||
|
expression: `'threshold.name' = "high-cpu" AND ruleId == "rule-123"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"threshold.name": "high-cpu",
|
||||||
|
"ruleId": "rule-123",
|
||||||
|
"service": "auth",
|
||||||
|
},
|
||||||
|
expected: false, //no commas
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "alertname and ruleId combination",
|
name: "alertname and ruleId combination",
|
||||||
expression: `alertname == "HighCPUUsage" && ruleId == "cpu-alert-001"`,
|
expression: `alertname == "HighCPUUsage" && ruleId == "cpu-alert-001"`,
|
||||||
@ -410,6 +524,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "alertname and ruleId combination",
|
||||||
|
expression: `alertname = "HighCPUUsage" AND ruleId = "cpu-alert-001"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"alertname": "HighCPUUsage",
|
||||||
|
"ruleId": "cpu-alert-001",
|
||||||
|
"severity": "critical",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "kubernetes namespace filtering",
|
name: "kubernetes namespace filtering",
|
||||||
expression: `k8s.namespace.name == "auth" && service in ["auth", "payment"]`,
|
expression: `k8s.namespace.name == "auth" && service in ["auth", "payment"]`,
|
||||||
@ -420,6 +544,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "kubernetes namespace filtering",
|
||||||
|
expression: `k8s.namespace.name = "auth" && service IN ["auth", "payment"]`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"k8s.namespace.name": "auth",
|
||||||
|
"service": "auth",
|
||||||
|
"host": "k8s-node-1",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "migration expression format from SQL migration",
|
name: "migration expression format from SQL migration",
|
||||||
expression: `threshold.name == "HighCPUUsage" && ruleId == "rule-uuid-123"`,
|
expression: `threshold.name == "HighCPUUsage" && ruleId == "rule-uuid-123"`,
|
||||||
@ -430,6 +564,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "migration expression format from SQL migration",
|
||||||
|
expression: `threshold.name = "HighCPUUsage" && ruleId = "rule-uuid-123"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"threshold.name": "HighCPUUsage",
|
||||||
|
"ruleId": "rule-uuid-123",
|
||||||
|
"severity": "warning",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "case sensitive matching",
|
name: "case sensitive matching",
|
||||||
expression: `service == "Auth"`, // capital A
|
expression: `service == "Auth"`, // capital A
|
||||||
@ -438,6 +582,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "case sensitive matching",
|
||||||
|
expression: `service = "Auth"`, // capital A
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth", // lowercase a
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "numeric comparison as strings",
|
name: "numeric comparison as strings",
|
||||||
expression: `port == "8080"`,
|
expression: `port == "8080"`,
|
||||||
@ -446,6 +598,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "numeric comparison as strings",
|
||||||
|
expression: `port = "8080"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"port": "8080",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "quoted string with special characters",
|
name: "quoted string with special characters",
|
||||||
expression: `service == "auth-service-v2"`,
|
expression: `service == "auth-service-v2"`,
|
||||||
@ -454,6 +614,14 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "quoted string with special characters",
|
||||||
|
expression: `service = "auth-service-v2"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "auth-service-v2",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "boolean operators precedence",
|
name: "boolean operators precedence",
|
||||||
expression: `service == "auth" && env == "prod" || critical == "true"`,
|
expression: `service == "auth" && env == "prod" || critical == "true"`,
|
||||||
@ -464,6 +632,16 @@ func TestProvider_EvaluateExpression(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "boolean operators precedence",
|
||||||
|
expression: `service = "auth" AND env = "prod" OR critical = "true"`,
|
||||||
|
labelSet: model.LabelSet{
|
||||||
|
"service": "payment",
|
||||||
|
"env": "staging",
|
||||||
|
"critical": "true",
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -554,6 +732,21 @@ func TestProvider_CreateRoute(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid route qb format",
|
||||||
|
orgID: "test-org-123",
|
||||||
|
route: &alertmanagertypes.RoutePolicy{
|
||||||
|
Identifiable: types.Identifiable{ID: valuer.GenerateUUID()},
|
||||||
|
Expression: `service = "auth"`,
|
||||||
|
ExpressionKind: alertmanagertypes.PolicyBasedExpression,
|
||||||
|
Name: "auth-service-route",
|
||||||
|
Description: "Route for auth service alerts",
|
||||||
|
Enabled: true,
|
||||||
|
OrgID: "test-org-123",
|
||||||
|
Channels: []string{"slack-channel"},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "nil route",
|
name: "nil route",
|
||||||
orgID: "test-org-123",
|
orgID: "test-org-123",
|
||||||
@ -582,6 +775,17 @@ func TestProvider_CreateRoute(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid route - missing name",
|
||||||
|
orgID: "test-org-123",
|
||||||
|
route: &alertmanagertypes.RoutePolicy{
|
||||||
|
Expression: `service = "auth"`,
|
||||||
|
ExpressionKind: alertmanagertypes.PolicyBasedExpression,
|
||||||
|
Name: "", // empty name
|
||||||
|
OrgID: "test-org-123",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user