Skip to content

Commit 784701b

Browse files
authored
DEV-48338 Silences alert not eval right away in grafana 10 (#108)
1 parent 20db01f commit 784701b

File tree

2 files changed

+66
-36
lines changed

2 files changed

+66
-36
lines changed

public/app/features/alerting/unified/components/silences/SilencedInstancesPreview.tsx

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
// LOGZ.IO CHANGES :: DEV-48338 , file taken from https://github.com/grafana/grafana/pull/87320
12
import { css } from '@emotion/css';
2-
import React from 'react';
3+
import React, { useState } from 'react';
4+
import { useDebounce, useDeepCompareEffect } from 'react-use';
35

46
import { dateTime, GrafanaTheme2 } from '@grafana/data';
57
import { Alert, Badge, LoadingPlaceholder, useStyles2 } from '@grafana/ui';
6-
import { AlertmanagerAlert, Matcher } from 'app/plugins/datasource/alertmanager/types';
8+
import { MatcherFieldValue } from 'app/features/alerting/unified/types/silence-form';
9+
import { matcherFieldToMatcher } from 'app/features/alerting/unified/utils/alertmanager';
10+
import { AlertmanagerAlert, Matcher, MatcherOperator } from 'app/plugins/datasource/alertmanager/types';
711

812
import { alertmanagerApi } from '../../api/alertmanagerApi';
913
import { isNullDate } from '../../utils/time';
@@ -12,27 +16,52 @@ import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '..
1216

1317
import { AmAlertStateTag } from './AmAlertStateTag';
1418

19+
const MATCHER_ALERT_RULE_UID = '__alert_rule_uid__';
1520
interface Props {
1621
amSourceName: string;
17-
matchers: Matcher[];
22+
matchers: MatcherFieldValue[];
23+
ruleUid?: string;
1824
}
1925

20-
export const SilencedInstancesPreview = ({ amSourceName, matchers }: Props) => {
21-
const { useGetAlertmanagerAlertsQuery } = alertmanagerApi;
26+
/**
27+
* Performs a deep equality check on the dependencies, and debounces the callback
28+
*/
29+
const useDebouncedDeepCompare = (cb: () => void, debounceMs: number, dependencies: unknown[]) => {
30+
const [state, setState] = useState<unknown[]>();
31+
32+
useDebounce(cb, debounceMs, [state]);
33+
34+
useDeepCompareEffect(() => {
35+
setState(dependencies);
36+
}, [dependencies]);
37+
};
38+
39+
export const SilencedInstancesPreview = ({ amSourceName, matchers: inputMatchers, ruleUid }: Props) => {
40+
const matchers: Matcher[] = [
41+
...(ruleUid ? [{ name: MATCHER_ALERT_RULE_UID, value: ruleUid, operator: MatcherOperator.equal }] : []),
42+
...inputMatchers,
43+
].map(matcherFieldToMatcher);
44+
const useLazyQuery = alertmanagerApi.endpoints.getAlertmanagerAlerts.useLazyQuery;
2245
const styles = useStyles2(getStyles);
2346
const columns = useColumns();
2447

2548
// By default the form contains an empty matcher - with empty name and value and = operator
2649
// We don't want to fetch previews for empty matchers as it results in all alerts returned
27-
const hasValidMatchers = matchers.some((matcher) => matcher.value && matcher.name);
28-
29-
const {
30-
currentData: alerts = [],
31-
isFetching,
32-
isError,
33-
} = useGetAlertmanagerAlertsQuery(
34-
{ amSourceName, filter: { matchers } },
35-
{ skip: !hasValidMatchers, refetchOnMountOrArgChange: true }
50+
const hasValidMatchers = ruleUid || inputMatchers.some((matcher) => matcher.value && matcher.name);
51+
52+
const [getAlertmanagerAlerts, { currentData: alerts = [], isFetching, isError }] = useLazyQuery();
53+
54+
// We need to deep compare the matchers, as otherwise the preview API call is triggered on every render
55+
// of the component. This is because between react-hook-form's useFieldArray, and our parsing of the matchers,
56+
// we end up otherwise triggering the call too frequently
57+
useDebouncedDeepCompare(
58+
() => {
59+
if (hasValidMatchers) {
60+
getAlertmanagerAlerts({ amSourceName, filter: { matchers } });
61+
}
62+
},
63+
500,
64+
[amSourceName, matchers]
3665
);
3766

3867
const tableItemAlerts = alerts.map<DynamicTableItemProps<AlertmanagerAlert>>((alert) => ({
@@ -43,18 +72,18 @@ export const SilencedInstancesPreview = ({ amSourceName, matchers }: Props) => {
4372
return (
4473
<div>
4574
<h4 className={styles.title}>
46-
Affected alert instances
75+
Affected alert rule instances
4776
{tableItemAlerts.length > 0 ? (
4877
<Badge className={styles.badge} color="blue" text={tableItemAlerts.length} />
4978
) : null}
5079
</h4>
5180
{!hasValidMatchers && <span>Add a valid matcher to see affected alerts</span>}
5281
{isError && (
5382
<Alert title="Preview not available" severity="error">
54-
Error occured when generating affected alerts preview. Are you matchers valid?
83+
Error occured when generating preview of affected alerts. Are your matchers valid?
5584
</Alert>
5685
)}
57-
{isFetching && <LoadingPlaceholder text="Loading..." />}
86+
{isFetching && <LoadingPlaceholder text="Loading affected alert rule instances..." />}
5887
{!isFetching && !isError && hasValidMatchers && (
5988
<div className={styles.table}>
6089
{tableItemAlerts.length > 0 ? (
@@ -65,7 +94,7 @@ export const SilencedInstancesPreview = ({ amSourceName, matchers }: Props) => {
6594
pagination={{ itemsPerPage: 10 }}
6695
/>
6796
) : (
68-
<span>No matching alert instances found</span>
97+
<span>No firing alert instances found</span>
6998
)}
7099
</div>
71100
)}
@@ -106,21 +135,21 @@ function useColumns(): Array<DynamicTableColumnProps<AlertmanagerAlert>> {
106135
}
107136

108137
const getStyles = (theme: GrafanaTheme2) => ({
109-
table: css`
110-
max-width: ${theme.breakpoints.values.lg}px;
111-
`,
112-
moreMatches: css`
113-
margin-top: ${theme.spacing(1)};
114-
`,
115-
title: css`
116-
display: flex;
117-
align-items: center;
118-
`,
119-
badge: css`
120-
margin-left: ${theme.spacing(1)};
121-
`,
122-
stateColumn: css`
123-
display: flex;
124-
align-items: center;
125-
`,
138+
table: css({
139+
maxWidth: `${theme.breakpoints.values.lg}px`,
140+
}),
141+
moreMatches: css({
142+
marginTop: theme.spacing(1),
143+
}),
144+
title: css({
145+
display: 'flex',
146+
alignItems: 'center',
147+
}),
148+
badge: css({
149+
marginLeft: theme.spacing(1),
150+
}),
151+
stateColumn: css({
152+
display: 'flex',
153+
alignItems: 'center',
154+
}),
126155
});

public/app/features/alerting/unified/components/silences/SilencesEditor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
183183

184184
const userLogged = Boolean(config.bootData.user.isSignedIn && config.bootData.user.name);
185185

186+
// LOGZ.IO CHANGES :: DEV-48338 , line 242
186187
return (
187188
<FormProvider {...formAPI}>
188189
<form onSubmit={handleSubmit(onSubmit)}>
@@ -238,7 +239,7 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
238239
/>
239240
</Field>
240241
)}
241-
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matchersForPreview} />
242+
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matcherFields} />
242243
</FieldSet>
243244
<div className={styles.flexRow}>
244245
{loading && (

0 commit comments

Comments
 (0)