Alerting: Add label and state filters to alert instance components (#42550)

pull/43025/head
Matthew Jacobson 4 years ago committed by GitHub
parent 1442052c37
commit 62c20ebd77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButton.tsx
  2. 13
      packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup.tsx
  3. 7
      public/app/features/alerting/unified/components/alert-groups/MatcherFilter.tsx
  4. 32
      public/app/features/alerting/unified/components/rules/AlertInstanceStateFilter.tsx
  5. 11
      public/app/features/alerting/unified/components/rules/AlertInstancesTable.tsx
  6. 88
      public/app/features/alerting/unified/components/rules/RuleDetailsMatchingInstances.tsx
  7. 7
      public/app/features/alerting/unified/utils/alertmanager.test.ts
  8. 2
      public/app/features/alerting/unified/utils/alertmanager.ts
  9. 70
      public/app/features/alerting/unified/utils/misc.test.ts
  10. 40
      public/app/features/alerting/unified/utils/misc.ts
  11. 81
      public/app/plugins/panel/alertlist/AlertInstances.tsx
  12. 2
      public/app/plugins/panel/alertlist/UnifiedAlertList.tsx
  13. 49
      public/app/plugins/panel/alertlist/module.tsx
  14. 6
      public/app/plugins/panel/alertlist/types.ts

@ -16,6 +16,7 @@ export interface RadioButtonProps {
active: boolean;
id: string;
onChange: () => void;
onClick: () => void;
fullWidth?: boolean;
'aria-label'?: StringSelector;
children?: React.ReactNode;
@ -29,6 +30,7 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>(
disabled = false,
size = 'md',
onChange,
onClick,
id,
name = undefined,
description,
@ -46,6 +48,7 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>(
type="radio"
className={styles.radio}
onChange={onChange}
onClick={onClick}
disabled={disabled}
id={id}
checked={active}

@ -13,6 +13,7 @@ export interface RadioButtonGroupProps<T> {
disabledOptions?: T[];
options: Array<SelectableValue<T>>;
onChange?: (value: T) => void;
onClick?: (value: T) => void;
size?: RadioButtonSize;
fullWidth?: boolean;
className?: string;
@ -23,6 +24,7 @@ export function RadioButtonGroup<T>({
options,
value,
onChange,
onClick,
disabled,
disabledOptions,
size = 'md',
@ -40,6 +42,16 @@ export function RadioButtonGroup<T>({
},
[onChange]
);
const handleOnClick = useCallback(
(option: SelectableValue) => {
return () => {
if (onClick) {
onClick(option.value);
}
};
},
[onClick]
);
const id = uniqueId('radiogroup-');
const groupName = useRef(id);
const styles = useStyles2(getStyles);
@ -63,6 +75,7 @@ export function RadioButtonGroup<T>({
key={`o.label-${i}`}
aria-label={o.ariaLabel}
onChange={handleOnChange(o)}
onClick={handleOnClick(o)}
id={`option-${o.value}-${id}`}
name={groupName.current}
description={o.description}

@ -15,6 +15,7 @@ export const MatcherFilter = ({ className, onFilterChange, queryString }: Props)
const target = e.target as HTMLInputElement;
onFilterChange(target.value);
};
const searchIcon = <Icon name={'search'} />;
return (
<div className={className}>
<Label>
@ -35,6 +36,8 @@ export const MatcherFilter = ({ className, onFilterChange, queryString }: Props)
defaultValue={queryString}
onChange={handleSearchChange}
data-testid="search-query-input"
prefix={searchIcon}
className={styles.inputWidth}
/>
</div>
);
@ -44,4 +47,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
icon: css`
margin-right: ${theme.spacing(0.5)};
`,
inputWidth: css`
width: 340px;
flex-grow: 0;
`,
});

@ -0,0 +1,32 @@
import React from 'react';
import { RadioButtonGroup, Label } from '@grafana/ui';
import { GrafanaAlertState } from 'app/types/unified-alerting-dto';
interface Props {
className?: string;
stateFilter?: GrafanaAlertState;
onStateFilterChange: (value: GrafanaAlertState | undefined) => void;
}
export const AlertInstanceStateFilter = ({ className, onStateFilterChange, stateFilter }: Props) => {
const stateOptions = Object.values(GrafanaAlertState).map((value) => ({
label: value,
value,
}));
return (
<div className={className}>
<Label>State</Label>
<RadioButtonGroup
options={stateOptions}
value={stateFilter}
onChange={onStateFilterChange}
onClick={(v) => {
if (v === stateFilter) {
onStateFilterChange(undefined);
}
}}
/>
</div>
);
};

@ -16,15 +16,12 @@ type AlertTableColumnProps = DynamicTableColumnProps<Alert>;
type AlertTableItemProps = DynamicTableItemProps<Alert>;
export const AlertInstancesTable: FC<Props> = ({ instances }) => {
// add key & sort instance. API returns instances in random order, different every time.
const items = useMemo(
(): AlertTableItemProps[] =>
instances
.map((instance) => ({
data: instance,
id: alertInstanceKey(instance),
}))
.sort((a, b) => a.id.localeCompare(b.id)),
instances.map((instance) => ({
data: instance,
id: alertInstanceKey(instance),
})),
[instances]
);

@ -1,8 +1,17 @@
import { Rule } from 'app/types/unified-alerting';
import React from 'react';
import { Alert, Rule } from 'app/types/unified-alerting';
import React, { useMemo, useState } from 'react';
import { isAlertingRule } from '../../utils/rules';
import { DetailsField } from '../DetailsField';
import { AlertInstancesTable } from './AlertInstancesTable';
import { SortOrder } from 'app/plugins/panel/alertlist/types';
import { GrafanaAlertState } from 'app/types/unified-alerting-dto';
import { GrafanaTheme } from '@grafana/data';
import { useStyles } from '@grafana/ui';
import { css, cx } from '@emotion/css';
import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
import { sortAlerts } from 'app/features/alerting/unified/utils/misc';
import { MatcherFilter } from 'app/features/alerting/unified/components/alert-groups/MatcherFilter';
import { AlertInstanceStateFilter } from 'app/features/alerting/unified/components/rules/AlertInstanceStateFilter';
type Props = {
promRule?: Rule;
@ -11,13 +20,84 @@ type Props = {
export function RuleDetailsMatchingInstances(props: Props): JSX.Element | null {
const { promRule } = props;
if (!isAlertingRule(promRule) || !promRule.alerts?.length) {
const [queryString, setQueryString] = useState<string>();
const [alertState, setAlertState] = useState<GrafanaAlertState>();
// This key is used to force a rerender on the inputs when the filters are cleared
const [filterKey] = useState<number>(Math.floor(Math.random() * 100));
const queryStringKey = `queryString-${filterKey}`;
const styles = useStyles(getStyles);
const alerts = useMemo(
(): Alert[] =>
isAlertingRule(promRule) && promRule.alerts?.length
? filterAlerts(queryString, alertState, sortAlerts(SortOrder.Importance, promRule.alerts))
: [],
[promRule, alertState, queryString]
);
if (!isAlertingRule(promRule)) {
return null;
}
return (
<DetailsField label="Matching instances" horizontal={true}>
<AlertInstancesTable instances={promRule.alerts} />
<div className={cx(styles.flexRow, styles.spaceBetween)}>
<div className={styles.flexRow}>
<MatcherFilter
className={styles.rowChild}
key={queryStringKey}
queryString={queryString}
onFilterChange={(value) => setQueryString(value)}
/>
<AlertInstanceStateFilter
className={styles.rowChild}
stateFilter={alertState}
onStateFilterChange={setAlertState}
/>
</div>
</div>
<AlertInstancesTable instances={alerts} />
</DetailsField>
);
}
function filterAlerts(
alertInstanceLabel: string | undefined,
alertInstanceState: GrafanaAlertState | undefined,
alerts: Alert[]
): Alert[] {
let filteredAlerts = [...alerts];
if (alertInstanceLabel) {
const matchers = parseMatchers(alertInstanceLabel || '');
filteredAlerts = filteredAlerts.filter(({ labels }) => labelsMatchMatchers(labels, matchers));
}
if (alertInstanceState) {
filteredAlerts = filteredAlerts.filter((alert) => {
return alert.state === alertInstanceState;
});
}
return filteredAlerts;
}
const getStyles = (theme: GrafanaTheme) => {
return {
flexRow: css`
display: flex;
flex-direction: row;
align-items: flex-end;
width: 100%;
flex-wrap: wrap;
margin-bottom: ${theme.spacing.sm};
`,
spaceBetween: css`
justify-content: space-between;
`,
rowChild: css`
margin-right: ${theme.spacing.sm};
`,
};
};

@ -88,6 +88,13 @@ describe('Alertmanager utils', () => {
{ name: 'bar', value: 'bazz', isEqual: true, isRegex: false },
]);
});
it('should parse matchers for key with special characters', () => {
expect(parseMatchers('foo.bar-baz="bar",baz-bar.foo=bazz')).toEqual<Matcher[]>([
{ name: 'foo.bar-baz', value: 'bar', isRegex: false, isEqual: true },
{ name: 'baz-bar.foo', value: 'bazz', isEqual: true, isRegex: false },
]);
});
});
describe('labelsMatchMatchers', () => {

@ -136,7 +136,7 @@ export function parseMatcher(matcher: string): Matcher {
}
export function parseMatchers(matcherQueryString: string): Matcher[] {
const matcherRegExp = /\b(\w+)(=~|!=|!~|=(?="?\w))"?([^"\n,]*)"?/g;
const matcherRegExp = /\b([\w.-]+)(=~|!=|!~|=(?="?\w))"?([^"\n,]*)"?/g;
const matchers: Matcher[] = [];
matcherQueryString.replace(matcherRegExp, (_, key, operator, value) => {

@ -0,0 +1,70 @@
import { sortAlerts } from 'app/features/alerting/unified/utils/misc';
import { GrafanaAlertState } from 'app/types/unified-alerting-dto';
import { Alert } from 'app/types/unified-alerting';
import { SortOrder } from 'app/plugins/panel/alertlist/types';
function withState(state: GrafanaAlertState, labels?: {}): Alert {
return { activeAt: '', annotations: {}, labels: labels || {}, state: state, value: '' };
}
function withDate(activeAt?: string, labels?: {}): Alert {
return {
activeAt: activeAt || '',
annotations: {},
labels: labels || {},
state: GrafanaAlertState.Alerting,
value: '',
};
}
function permute(inputArray: any[]): any[] {
return inputArray.reduce(function permute(res, item, key, arr) {
return res.concat(
(arr.length > 1 &&
arr
.slice(0, key)
.concat(arr.slice(key + 1))
.reduce(permute, [])
.map(function (perm: any) {
return [item].concat(perm);
})) ||
item
);
}, []);
}
describe('Unified Altering misc', () => {
describe('sortAlerts', () => {
describe('when using any sortOrder with a list of alert instances', () => {
it.each`
alerts | sortOrder | expected
${[withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.Alerting), withState(GrafanaAlertState.Normal)]} | ${SortOrder.Importance} | ${[withState(GrafanaAlertState.Alerting), withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.Normal)]}
${[withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.Alerting), withState(GrafanaAlertState.NoData)]} | ${SortOrder.Importance} | ${[withState(GrafanaAlertState.Alerting), withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.NoData)]}
${[withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.Error), withState(GrafanaAlertState.Normal)]} | ${SortOrder.Importance} | ${[withState(GrafanaAlertState.Error), withState(GrafanaAlertState.Pending), withState(GrafanaAlertState.Normal)]}
${[withDate('2021-11-29T14:10:07-05:00'), withDate('2021-11-29T15:10:07-05:00'), withDate('2021-11-29T13:10:07-05:00')]} | ${SortOrder.TimeAsc} | ${[withDate('2021-11-29T13:10:07-05:00'), withDate('2021-11-29T14:10:07-05:00'), withDate('2021-11-29T15:10:07-05:00')]}
${[withDate('2021-11-29T14:10:07-05:00'), withDate('2021-11-29T15:10:07-05:00'), withDate('2021-11-29T13:10:07-05:00')]} | ${SortOrder.TimeDesc} | ${[withDate('2021-11-29T15:10:07-05:00'), withDate('2021-11-29T14:10:07-05:00'), withDate('2021-11-29T13:10:07-05:00')]}
${[withDate('', { mno: 'pqr' }), withDate('', { abc: 'def' }), withDate('', { ghi: 'jkl' })]} | ${SortOrder.AlphaAsc} | ${[withDate('', { abc: 'def' }), withDate('', { ghi: 'jkl' }), withDate('', { mno: 'pqr' })]}
${[withDate('', { mno: 'pqr' }), withDate('', { abc: 'def' }), withDate('', { ghi: 'jkl' })]} | ${SortOrder.AlphaDesc} | ${[withDate('', { mno: 'pqr' }), withDate('', { ghi: 'jkl' }), withDate('', { abc: 'def' })]}
`('then it should sort the alerts correctly', ({ alerts, sortOrder, expected }) => {
const result = sortAlerts(sortOrder, alerts);
expect(result).toEqual(expected);
});
});
describe('when sorting ties', () => {
it.each`
alerts | sortOrder
${[withState(GrafanaAlertState.Alerting, { ghi: 'jkl' }), withState(GrafanaAlertState.Alerting, { abc: 'def' }), withState(GrafanaAlertState.Alerting)]} | ${SortOrder.Importance}
${[withDate('2021-11-29T13:10:07-05:00', { ghi: 'jkl' }), withDate('2021-11-29T13:10:07-05:00'), withDate('2021-11-29T13:10:07-05:00', { abc: 'def' })]} | ${SortOrder.TimeAsc}
${[withDate('2021-11-29T13:10:07-05:00', { ghi: 'jkl' }), withDate('2021-11-29T13:10:07-05:00'), withDate('2021-11-29T13:10:07-05:00', { abc: 'def' })]} | ${SortOrder.TimeDesc}
`('then tie order should be deterministic', ({ alerts, sortOrder }) => {
// All input permutations should result in the same sorted order
const sortedPermutations = permute(alerts).map((a) => sortAlerts(sortOrder, a));
sortedPermutations.forEach((p) => {
expect(p).toEqual(sortedPermutations[0]);
});
});
});
});
});

@ -1,9 +1,13 @@
import { urlUtil, UrlQueryMap } from '@grafana/data';
import { config } from '@grafana/runtime';
import { CombinedRule, FilterState, RulesSource, SilenceFilterState } from 'app/types/unified-alerting';
import { Alert, CombinedRule, FilterState, RulesSource, SilenceFilterState } from 'app/types/unified-alerting';
import { ALERTMANAGER_NAME_QUERY_KEY } from './constants';
import { getRulesSourceName } from './datasource';
import * as ruleId from './rule-id';
import { SortOrder } from 'app/plugins/panel/alertlist/types';
import { alertInstanceKey } from 'app/features/alerting/unified/utils/rules';
import { sortBy } from 'lodash';
import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto';
export function createViewLink(ruleSource: RulesSource, rule: CombinedRule, returnTo: string): string {
const sourceName = getRulesSourceName(ruleSource);
@ -83,3 +87,37 @@ export function retryWhile<T, E = Error>(
});
return makeAttempt();
}
const alertStateSortScore = {
[GrafanaAlertState.Alerting]: 1,
[PromAlertingRuleState.Firing]: 1,
[GrafanaAlertState.Error]: 1,
[GrafanaAlertState.Pending]: 2,
[PromAlertingRuleState.Pending]: 2,
[PromAlertingRuleState.Inactive]: 2,
[GrafanaAlertState.NoData]: 3,
[GrafanaAlertState.Normal]: 4,
};
export function sortAlerts(sortOrder: SortOrder, alerts: Alert[]): Alert[] {
// Make sure to handle tie-breaks because API returns alert instances in random order every time
if (sortOrder === SortOrder.Importance) {
return sortBy(alerts, (alert) => [alertStateSortScore[alert.state], alertInstanceKey(alert).toLocaleLowerCase()]);
} else if (sortOrder === SortOrder.TimeAsc) {
return sortBy(alerts, (alert) => [
new Date(alert.activeAt) || new Date(),
alertInstanceKey(alert).toLocaleLowerCase(),
]);
} else if (sortOrder === SortOrder.TimeDesc) {
return sortBy(alerts, (alert) => [
new Date(alert.activeAt) || new Date(),
alertInstanceKey(alert).toLocaleLowerCase(),
]).reverse();
}
const result = sortBy(alerts, (alert) => alertInstanceKey(alert).toLocaleLowerCase());
if (sortOrder === SortOrder.AlphaDesc) {
result.reverse();
}
return result;
}

@ -2,35 +2,31 @@ import React, { useEffect, useMemo, useState } from 'react';
import pluralize from 'pluralize';
import { Icon, useStyles2 } from '@grafana/ui';
import { Alert, PromRuleWithLocation } from 'app/types/unified-alerting';
import { AlertLabels } from 'app/features/alerting/unified/components/AlertLabels';
import { AlertStateTag } from 'app/features/alerting/unified/components/rules/AlertStateTag';
import { dateTime, GrafanaTheme2 } from '@grafana/data';
import { GrafanaTheme2, PanelProps } from '@grafana/data';
import { css } from '@emotion/css';
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { omit } from 'lodash';
import { alertInstanceKey } from 'app/features/alerting/unified/utils/rules';
import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { UnifiedAlertListOptions } from './types';
import { AlertInstancesTable } from 'app/features/alerting/unified/components/rules/AlertInstancesTable';
import { sortAlerts } from 'app/features/alerting/unified/utils/misc';
import { labelsMatchMatchers, parseMatchers } from 'app/features/alerting/unified/utils/alertmanager';
interface Props {
ruleWithLocation: PromRuleWithLocation;
showInstances: boolean;
options: PanelProps<UnifiedAlertListOptions>['options'];
}
export const AlertInstances = ({ ruleWithLocation, showInstances }: Props) => {
export const AlertInstances = ({ ruleWithLocation, options }: Props) => {
const { rule } = ruleWithLocation;
const [displayInstances, setDisplayInstances] = useState<boolean>(showInstances);
const [displayInstances, setDisplayInstances] = useState<boolean>(options.showInstances);
const styles = useStyles2(getStyles);
useEffect(() => {
setDisplayInstances(showInstances);
}, [showInstances]);
setDisplayInstances(options.showInstances);
}, [options.showInstances]);
// sort instances, because API returns them in random order every time
const sortedAlerts = useMemo(
(): Alert[] =>
displayInstances
? rule.alerts.slice().sort((a, b) => alertInstanceKey(a).localeCompare(alertInstanceKey(b)))
: [],
[rule, displayInstances]
const alerts = useMemo(
(): Alert[] => (displayInstances ? filterAlerts(options, sortAlerts(options.sortOrder, rule.alerts)) : []),
[rule, options, displayInstances]
);
return (
@ -42,37 +38,34 @@ export const AlertInstances = ({ ruleWithLocation, showInstances }: Props) => {
</div>
)}
{!!sortedAlerts.length && (
<ol className={styles.list}>
{sortedAlerts.map((alert, index) => {
return (
<li className={styles.listItem} key={`${alert.activeAt}-${index}`}>
<div>
<AlertStateTag state={alert.state} />
<span className={styles.date}>{dateTime(alert.activeAt).format('YYYY-MM-DD HH:mm:ss')}</span>
</div>
<AlertLabels labels={omit(alert.labels, 'alertname')} />
</li>
);
})}
</ol>
)}
{!!alerts.length && <AlertInstancesTable instances={alerts} />}
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
function filterAlerts(options: PanelProps<UnifiedAlertListOptions>['options'], alerts: Alert[]): Alert[] {
let filteredAlerts = [...alerts];
if (options.alertInstanceLabelFilter) {
const matchers = parseMatchers(options.alertInstanceLabelFilter || '');
filteredAlerts = filteredAlerts.filter(({ labels }) => labelsMatchMatchers(labels, matchers));
}
if (Object.values(options.alertInstanceStateFilter).some((value) => value)) {
filteredAlerts = filteredAlerts.filter((alert) => {
return (
(options.alertInstanceStateFilter.Alerting && alert.state === GrafanaAlertState.Alerting) ||
(options.alertInstanceStateFilter.Pending && alert.state === GrafanaAlertState.Pending) ||
(options.alertInstanceStateFilter.NoData && alert.state === GrafanaAlertState.NoData) ||
(options.alertInstanceStateFilter.Normal && alert.state === GrafanaAlertState.Normal) ||
(options.alertInstanceStateFilter.Error && alert.state === GrafanaAlertState.Error)
);
});
}
return filteredAlerts;
}
const getStyles = (_: GrafanaTheme2) => ({
instance: css`
cursor: pointer;
`,
list: css`
list-style-type: none;
`,
listItem: css`
margin-top: ${theme.spacing(1)};
`,
date: css`
font-size: ${theme.typography.bodySmall.fontSize};
padding-left: ${theme.spacing(0.5)};
`,
});

@ -100,7 +100,7 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
)}
</div>
</div>
<AlertInstances ruleWithLocation={ruleWithLocation} showInstances={props.options.showInstances} />
<AlertInstances ruleWithLocation={ruleWithLocation} options={props.options} />
</div>
</li>
);

@ -154,12 +154,14 @@ const unifiedAlertList = new PanelPlugin<UnifiedAlertListOptions>(UnifiedAlertLi
.addNumberInput({
name: 'Max items',
path: 'maxItems',
description: 'Maximum alerts to display',
defaultValue: 20,
category: ['Options'],
})
.addSelect({
name: 'Sort order',
path: 'sortOrder',
description: 'Sort order of alerts and alert instances',
settings: {
options: [
{ label: 'Alphabetical (asc)', value: SortOrder.AlphaAsc },
@ -175,24 +177,35 @@ const unifiedAlertList = new PanelPlugin<UnifiedAlertListOptions>(UnifiedAlertLi
.addBooleanSwitch({
path: 'dashboardAlerts',
name: 'Alerts from this dashboard',
description: 'Show alerts from this dashboard',
defaultValue: false,
category: ['Options'],
})
.addBooleanSwitch({
path: 'showInstances',
name: 'Show alert instances',
description: 'Show individual alert instances for multi-dimensional rules',
defaultValue: false,
category: ['Options'],
})
.addTextInput({
path: 'alertName',
name: 'Alert name',
description: 'Filter for alerts containing this text',
defaultValue: '',
category: ['Filter'],
})
.addTextInput({
path: 'alertInstanceLabelFilter',
name: 'Alert instance label',
description: 'Filter alert instances using label querying, ex: {severity="critical", instance=~"cluster-us-.+"}',
defaultValue: '',
category: ['Filter'],
})
.addCustomEditor({
path: 'folder',
name: 'Folder',
description: 'Filter for alerts in the selected folder',
id: 'folder',
defaultValue: null,
editor: function RenderFolderPicker(props) {
@ -212,19 +225,49 @@ const unifiedAlertList = new PanelPlugin<UnifiedAlertListOptions>(UnifiedAlertLi
path: 'stateFilter.firing',
name: 'Alerting',
defaultValue: true,
category: ['State filter'],
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.pending',
name: 'Pending',
defaultValue: true,
category: ['State filter'],
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'stateFilter.inactive',
name: 'Inactive',
defaultValue: false,
category: ['State filter'],
category: ['Alert state filter'],
})
.addBooleanSwitch({
path: 'alertInstanceStateFilter.Alerting',
name: 'Alerting',
defaultValue: true,
category: ['Alert instance state filter'],
})
.addBooleanSwitch({
path: 'alertInstanceStateFilter.Pending',
name: 'Pending',
defaultValue: true,
category: ['Alert instance state filter'],
})
.addBooleanSwitch({
path: 'alertInstanceStateFilter.NoData',
name: 'No Data',
defaultValue: false,
category: ['Alert instance state filter'],
})
.addBooleanSwitch({
path: 'alertInstanceStateFilter.Normal',
name: 'Normal',
defaultValue: false,
category: ['Alert instance state filter'],
})
.addBooleanSwitch({
path: 'alertInstanceStateFilter.Error',
name: 'Error',
defaultValue: true,
category: ['Alert instance state filter'],
});
});

@ -1,4 +1,4 @@
import { PromAlertingRuleState } from 'app/types/unified-alerting-dto';
import { GrafanaAlertState, PromAlertingRuleState } from 'app/types/unified-alerting-dto';
export enum SortOrder {
AlphaAsc = 1,
@ -42,4 +42,8 @@ export interface UnifiedAlertListOptions {
stateFilter: {
[K in PromAlertingRuleState]: boolean;
};
alertInstanceLabelFilter: string;
alertInstanceStateFilter: {
[K in GrafanaAlertState]: boolean;
};
}

Loading…
Cancel
Save