mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
Alerting: Improve integration with dashboards (#80201)
* Add filtering by dashboard UID annotation * Update the inline doc for search * Add AlertRulesDrawer to the dashboards toolbar * Use DashboardPicker as a filter on the alert rules page * Fix accessibility errors * Update drawer subtitle * Display Alerting toolbar button only if there are linked alert rules * Change toolbar rendering method, prevent displaying when no linked rules * Improve text * Use React.lazy to load the Alert rule toolbar button and drawer when needed
This commit is contained in:
parent
75e1f7aa5e
commit
26e71953a4
@ -2102,13 +2102,6 @@ exports[`better eslint`] = {
|
||||
"public/app/features/alerting/unified/components/rules/RuleState.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rules/RulesFilter.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "2"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "3"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "4"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rules/RulesGroup.tsx:5381": [
|
||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||
|
@ -71,6 +71,7 @@ import { GrafanaJavascriptAgentBackend } from './core/services/echo/backends/gra
|
||||
import { KeybindingSrv } from './core/services/keybindingSrv';
|
||||
import { startMeasure, stopMeasure } from './core/utils/metrics';
|
||||
import { initDevFeatures } from './dev';
|
||||
import { initAlerting } from './features/alerting/unified/initAlerting';
|
||||
import { initAuthConfig } from './features/auth-config';
|
||||
import { getTimeSrv } from './features/dashboard/services/TimeSrv';
|
||||
import { EmbeddedDashboardLazy } from './features/dashboard-scene/embedding/EmbeddedDashboardLazy';
|
||||
@ -153,6 +154,8 @@ export class GrafanaApp {
|
||||
configureStore();
|
||||
initExtensions();
|
||||
|
||||
initAlerting();
|
||||
|
||||
standardEditorsRegistry.setInit(getAllOptionEditors);
|
||||
standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);
|
||||
standardTransformersRegistry.setInit(getStandardTransformers);
|
||||
|
@ -154,15 +154,15 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
||||
|
||||
prometheusRuleNamespaces: build.query<
|
||||
RuleNamespace[],
|
||||
{ ruleSourceName: string; namespace?: string; groupName?: string; ruleName?: string }
|
||||
{ ruleSourceName: string; namespace?: string; groupName?: string; ruleName?: string; dashboardUid?: string }
|
||||
>({
|
||||
query: ({ ruleSourceName, namespace, groupName, ruleName }) => {
|
||||
const queryParams: Record<string, string | undefined> = {};
|
||||
// if (isPrometheusRuleIdentifier(ruleIdentifier) || isCloudRuleIdentifier(ruleIdentifier)) {
|
||||
queryParams['file'] = namespace;
|
||||
queryParams['rule_group'] = groupName;
|
||||
queryParams['rule_name'] = ruleName;
|
||||
// }
|
||||
query: ({ ruleSourceName, namespace, groupName, ruleName, dashboardUid }) => {
|
||||
const queryParams: Record<string, string | undefined> = {
|
||||
file: namespace,
|
||||
rule_group: groupName,
|
||||
rule_name: ruleName,
|
||||
dashboard_uid: dashboardUid, // Supported only by Grafana managed rules
|
||||
};
|
||||
|
||||
return {
|
||||
url: `api/prometheus/${getDatasourceAPIUid(ruleSourceName)}/api/v1/rules`,
|
||||
|
@ -3,7 +3,8 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
import { DataSourceInstanceSettings, GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { Button, Field, Icon, Input, Label, RadioButtonGroup, Tooltip, useStyles2, Stack } from '@grafana/ui';
|
||||
import { Button, Field, Icon, Input, Label, RadioButtonGroup, Stack, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import { DashboardPicker } from 'app/core/components/Select/DashboardPicker';
|
||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||
import { PromAlertingRuleState, PromRuleType } from 'app/types/unified-alerting-dto';
|
||||
|
||||
@ -91,6 +92,10 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
setFilterKey((key) => key + 1);
|
||||
};
|
||||
|
||||
const handleDashboardChange = (dashboardUid: string | undefined) => {
|
||||
updateFilters({ ...filterState, dashboardUid });
|
||||
};
|
||||
|
||||
const clearDataSource = () => {
|
||||
updateFilters({ ...filterState, dataSourceNames: [] });
|
||||
setFilterKey((key) => key + 1);
|
||||
@ -99,7 +104,6 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
const handleAlertStateChange = (value: PromAlertingRuleState) => {
|
||||
logInfo(LogMessages.clickingAlertStateFilters);
|
||||
updateFilters({ ...filterState, ruleState: value });
|
||||
setFilterKey((key) => key + 1);
|
||||
};
|
||||
|
||||
const handleViewChange = (view: string) => {
|
||||
@ -108,12 +112,10 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
|
||||
const handleRuleTypeChange = (ruleType: PromRuleType) => {
|
||||
updateFilters({ ...filterState, ruleType });
|
||||
setFilterKey((key) => key + 1);
|
||||
};
|
||||
|
||||
const handleRuleHealthChange = (ruleHealth: RuleHealth) => {
|
||||
updateFilters({ ...filterState, ruleHealth });
|
||||
setFilterKey((key) => key + 1);
|
||||
};
|
||||
|
||||
const handleClearFiltersClick = () => {
|
||||
@ -127,7 +129,7 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Stack direction="column" gap={1}>
|
||||
<Stack direction="row" gap={1}>
|
||||
<Stack direction="row" gap={1} wrap="wrap">
|
||||
<Field
|
||||
className={styles.dsPickerContainer}
|
||||
label={
|
||||
@ -148,7 +150,7 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Icon name="info-circle" size="sm" />
|
||||
<Icon id="data-source-picker-inline-help" name="info-circle" size="sm" />
|
||||
</Tooltip>
|
||||
</Stack>
|
||||
</Label>
|
||||
@ -165,6 +167,22 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
className={styles.dashboardPickerContainer}
|
||||
label={<Label htmlFor="filters-dashboard-picker">Dashboard</Label>}
|
||||
>
|
||||
{/* The key prop is to clear the picker value */}
|
||||
{/* DashboardPicker doesn't do that itself when value is undefined */}
|
||||
<DashboardPicker
|
||||
inputId="filters-dashboard-picker"
|
||||
key={filterState.dashboardUid ? 'dashboard-defined' : 'dashboard-not-defined'}
|
||||
value={filterState.dashboardUid}
|
||||
onChange={(value) => handleDashboardChange(value?.uid)}
|
||||
isClearable
|
||||
cacheOptions
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div>
|
||||
<Label>State</Label>
|
||||
<RadioButtonGroup
|
||||
@ -246,18 +264,21 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) =>
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
container: css`
|
||||
margin-bottom: ${theme.spacing(1)};
|
||||
`,
|
||||
dsPickerContainer: css`
|
||||
width: 550px;
|
||||
flex-grow: 0;
|
||||
margin: 0;
|
||||
`,
|
||||
searchInput: css`
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
`,
|
||||
container: css({
|
||||
marginBottom: theme.spacing(1),
|
||||
}),
|
||||
dsPickerContainer: css({
|
||||
width: theme.spacing(60),
|
||||
flexGrow: 0,
|
||||
margin: 0,
|
||||
}),
|
||||
dashboardPickerContainer: css({
|
||||
minWidth: theme.spacing(50),
|
||||
}),
|
||||
searchInput: css({
|
||||
flex: 1,
|
||||
margin: 0,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@ -279,6 +300,7 @@ function SearchQueryHelp() {
|
||||
<HelpRow title="State" expr="state:firing|normal|pending" />
|
||||
<HelpRow title="Type" expr="type:alerting|recording" />
|
||||
<HelpRow title="Health" expr="health:ok|nodata|error" />
|
||||
<HelpRow title="Dashboard UID" expr="dashboard:eadde4c7-54e6-4964-85c0-484ab852fd04" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -296,16 +318,16 @@ function HelpRow({ title, expr }: { title: string; expr: string }) {
|
||||
}
|
||||
|
||||
const helpStyles = (theme: GrafanaTheme2) => ({
|
||||
grid: css`
|
||||
display: grid;
|
||||
grid-template-columns: max-content auto;
|
||||
gap: ${theme.spacing(1)};
|
||||
align-items: center;
|
||||
`,
|
||||
code: css`
|
||||
display: block;
|
||||
text-align: center;
|
||||
`,
|
||||
grid: css({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'max-content auto',
|
||||
gap: theme.spacing(1),
|
||||
alignItems: 'center',
|
||||
}),
|
||||
code: css({
|
||||
display: 'block',
|
||||
textAlign: 'center',
|
||||
}),
|
||||
});
|
||||
|
||||
export default RulesFilter;
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
mockRulerGrafanaRule,
|
||||
} from '../mocks';
|
||||
import { RuleHealth } from '../search/rulesSearchParser';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { getFilter } from '../utils/search';
|
||||
|
||||
import { filterRules } from './useFilteredRules';
|
||||
@ -230,4 +231,29 @@ describe('filterRules', function () {
|
||||
expect(filtered[0].groups[0].rules).toHaveLength(1);
|
||||
expect(filtered[0].groups[0].rules[0].name).toBe('Memory too low');
|
||||
});
|
||||
|
||||
it('should filter out rules by dashboard UID', () => {
|
||||
const rules = [
|
||||
mockCombinedRule({
|
||||
name: 'Memory too low',
|
||||
annotations: { [Annotation.dashboardUID]: 'dashboard-memory' },
|
||||
}),
|
||||
mockCombinedRule({
|
||||
name: 'CPU too high',
|
||||
annotations: { [Annotation.dashboardUID]: 'dashboard-cpu' },
|
||||
}),
|
||||
mockCombinedRule({
|
||||
name: 'Disk is dead',
|
||||
}),
|
||||
];
|
||||
|
||||
const ns = mockCombinedRuleNamespace({
|
||||
groups: [mockCombinedRuleGroup('Resources usage group', rules)],
|
||||
});
|
||||
|
||||
const filtered = filterRules([ns], getFilter({ dashboardUid: 'dashboard-cpu' }));
|
||||
|
||||
expect(filtered[0]?.groups[0]?.rules).toHaveLength(1);
|
||||
expect(filtered[0]?.groups[0]?.rules[0]?.name).toBe('CPU too high');
|
||||
});
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ import { isPromAlertingRuleState, PromRuleType, RulerGrafanaRuleDTO } from 'app/
|
||||
|
||||
import { applySearchFilterToQuery, getSearchFilterFromQuery, RulesFilter } from '../search/rulesSearchParser';
|
||||
import { labelsMatchMatchers, matcherToMatcherField, parseMatchers } from '../utils/alertmanager';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { isCloudRulesSource } from '../utils/datasource';
|
||||
import { parseMatcher } from '../utils/matchers';
|
||||
import { getRuleHealth, isAlertingRule, isGrafanaRulerRule, isPromRuleType } from '../utils/rules';
|
||||
@ -194,7 +195,7 @@ const reduceGroups = (filterState: RulesFilter) => {
|
||||
const matchesFilterFor = chain(filterState)
|
||||
// ⚠️ keep this list of properties we filter for here up-to-date ⚠️
|
||||
// We are ignoring predicates we've matched before we get here (like "freeFormWords")
|
||||
.pick(['ruleType', 'dataSourceNames', 'ruleHealth', 'labels', 'ruleState'])
|
||||
.pick(['ruleType', 'dataSourceNames', 'ruleHealth', 'labels', 'ruleState', 'dashboardUid'])
|
||||
.omitBy(isEmpty)
|
||||
.mapValues(() => false)
|
||||
.value();
|
||||
@ -253,6 +254,13 @@ const reduceGroups = (filterState: RulesFilter) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
'dashboardUid' in matchesFilterFor &&
|
||||
rule.annotations[Annotation.dashboardUID] === filterState.dashboardUid
|
||||
) {
|
||||
matchesFilterFor.dashboardUid = true;
|
||||
}
|
||||
|
||||
return Object.values(matchesFilterFor).every((match) => match === true);
|
||||
});
|
||||
|
||||
|
21
public/app/features/alerting/unified/initAlerting.tsx
Normal file
21
public/app/features/alerting/unified/initAlerting.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { addCustomRightAction } from '../../dashboard/components/DashNav/DashNav';
|
||||
|
||||
const AlertRulesToolbarButton = React.lazy(
|
||||
() => import(/* webpackChunkName: "alert-rules-toolbar-button" */ './integration/AlertRulesToolbarButton')
|
||||
);
|
||||
|
||||
export function initAlerting() {
|
||||
addCustomRightAction({
|
||||
show: () => config.unifiedAlertingEnabled,
|
||||
component: ({ dashboard }) => (
|
||||
<React.Suspense fallback={null}>
|
||||
{dashboard && <AlertRulesToolbarButton dashboardUid={dashboard.uid} />}
|
||||
</React.Suspense>
|
||||
),
|
||||
index: -2,
|
||||
});
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Drawer, LoadingPlaceholder, Stack, TextLink } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../../../core/internationalization';
|
||||
import { createUrl } from '../utils/url';
|
||||
|
||||
const AlertRulesDrawerContent = React.lazy(
|
||||
() => import(/* webpackChunkName: "alert-rules-drawer-content" */ './AlertRulesDrawerContent')
|
||||
);
|
||||
|
||||
interface Props {
|
||||
dashboardUid: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function AlertRulesDrawer({ dashboardUid, onClose }: Props) {
|
||||
return (
|
||||
<Drawer title="Alert rules" subtitle={<DrawerSubtitle dashboardUid={dashboardUid} />} onClose={onClose} size="lg">
|
||||
<React.Suspense fallback={<LoadingPlaceholder text="Loading alert rules" />}>
|
||||
<AlertRulesDrawerContent dashboardUid={dashboardUid} />
|
||||
</React.Suspense>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
function DrawerSubtitle({ dashboardUid }: { dashboardUid: string }) {
|
||||
const searchParams = new URLSearchParams({ search: `dashboard:${dashboardUid}` });
|
||||
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<div>{t('dashboard.toolbar.alert-rules.subtitle', 'Alert rules related to this dashboard')}</div>
|
||||
<TextLink href={createUrl(`/alerting/list/?${searchParams.toString()}`)}>
|
||||
{t('dashboard.toolbar.alert-rules.redirect-link', 'List in Grafana Alerting')}
|
||||
</TextLink>
|
||||
</Stack>
|
||||
);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { LoadingPlaceholder } from '@grafana/ui';
|
||||
import { useDispatch } from 'app/types';
|
||||
|
||||
import { RulesTable } from '../components/rules/RulesTable';
|
||||
import { useCombinedRuleNamespaces } from '../hooks/useCombinedRuleNamespaces';
|
||||
import { fetchPromAndRulerRulesAction } from '../state/actions';
|
||||
import { Annotation } from '../utils/constants';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
|
||||
interface Props {
|
||||
dashboardUid: string;
|
||||
}
|
||||
|
||||
export default function AlertRulesDrawerContent({ dashboardUid }: Props) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { loading } = useAsync(async () => {
|
||||
await dispatch(fetchPromAndRulerRulesAction({ rulesSourceName: GRAFANA_RULES_SOURCE_NAME }));
|
||||
}, [dispatch]);
|
||||
|
||||
const grafanaNamespaces = useCombinedRuleNamespaces(GRAFANA_RULES_SOURCE_NAME);
|
||||
const rules = grafanaNamespaces
|
||||
.flatMap((ns) => ns.groups)
|
||||
.flatMap((g) => g.rules)
|
||||
.filter((rule) => rule.annotations[Annotation.dashboardUID] === dashboardUid);
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading ? (
|
||||
<LoadingPlaceholder text="Loading alert rules" />
|
||||
) : (
|
||||
<RulesTable rules={rules} showNextEvaluationColumn={false} showGroupColumn={false} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { useToggle } from 'react-use';
|
||||
|
||||
import { ToolbarButton } from '@grafana/ui';
|
||||
|
||||
import { t } from '../../../../core/internationalization';
|
||||
import { alertRuleApi } from '../api/alertRuleApi';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
|
||||
import { AlertRulesDrawer } from './AlertRulesDrawer';
|
||||
|
||||
interface AlertRulesToolbarButtonProps {
|
||||
dashboardUid: string;
|
||||
}
|
||||
|
||||
export default function AlertRulesToolbarButton({ dashboardUid }: AlertRulesToolbarButtonProps) {
|
||||
const [showDrawer, toggleShowDrawer] = useToggle(false);
|
||||
|
||||
const { data: namespaces = [] } = alertRuleApi.endpoints.prometheusRuleNamespaces.useQuery({
|
||||
ruleSourceName: GRAFANA_RULES_SOURCE_NAME,
|
||||
dashboardUid: dashboardUid,
|
||||
});
|
||||
|
||||
if (namespaces.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToolbarButton
|
||||
tooltip={t('dashboard.toolbar.alert-rules', 'Alert rules')}
|
||||
icon="bell"
|
||||
onClick={toggleShowDrawer}
|
||||
key="button-alerting"
|
||||
/>
|
||||
{showDrawer && <AlertRulesDrawer dashboardUid={dashboardUid} onClose={toggleShowDrawer} />}
|
||||
</>
|
||||
);
|
||||
}
|
@ -20,6 +20,7 @@ export interface RulesFilter {
|
||||
dataSourceNames: string[];
|
||||
labels: string[];
|
||||
ruleHealth?: RuleHealth;
|
||||
dashboardUid?: string;
|
||||
}
|
||||
|
||||
const filterSupportedTerms: FilterSupportedTerm[] = [
|
||||
@ -31,6 +32,7 @@ const filterSupportedTerms: FilterSupportedTerm[] = [
|
||||
FilterSupportedTerm.state,
|
||||
FilterSupportedTerm.type,
|
||||
FilterSupportedTerm.health,
|
||||
FilterSupportedTerm.dashboard,
|
||||
];
|
||||
|
||||
export enum RuleHealth {
|
||||
@ -53,6 +55,7 @@ export function getSearchFilterFromQuery(query: string): RulesFilter {
|
||||
[terms.StateToken]: (value) => (filter.ruleState = parseStateToken(value)),
|
||||
[terms.TypeToken]: (value) => (isPromRuleType(value) ? (filter.ruleType = value) : undefined),
|
||||
[terms.HealthToken]: (value) => (filter.ruleHealth = getRuleHealth(value)),
|
||||
[terms.DashboardToken]: (value) => (filter.dashboardUid = value),
|
||||
[terms.FreeFormExpression]: (value) => filter.freeFormWords.push(value),
|
||||
};
|
||||
|
||||
@ -92,6 +95,9 @@ export function applySearchFilterToQuery(query: string, filter: RulesFilter): st
|
||||
if (filter.labels) {
|
||||
filterStateArray.push(...filter.labels.map((l) => ({ type: terms.LabelToken, value: l })));
|
||||
}
|
||||
if (filter.dashboardUid) {
|
||||
filterStateArray.push({ type: terms.DashboardToken, value: filter.dashboardUid });
|
||||
}
|
||||
if (filter.freeFormWords) {
|
||||
filterStateArray.push(...filter.freeFormWords.map((word) => ({ type: terms.FreeFormExpression, value: word })));
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
@top AlertRuleSearch { expression+ }
|
||||
|
||||
@dialects { dataSourceFilter, nameSpaceFilter, labelFilter, groupFilter, ruleFilter, stateFilter, typeFilter, healthFilter }
|
||||
@dialects { dataSourceFilter, nameSpaceFilter, labelFilter, groupFilter, ruleFilter, stateFilter, typeFilter, healthFilter, dashboardFilter }
|
||||
|
||||
expression { (FilterExpression | FreeFormExpression) expression }
|
||||
|
||||
@ -14,7 +14,8 @@ FilterExpression {
|
||||
filter<RuleToken> |
|
||||
filter<StateToken> |
|
||||
filter<TypeToken> |
|
||||
filter<HealthToken>
|
||||
filter<HealthToken> |
|
||||
filter<DashboardToken>
|
||||
}
|
||||
|
||||
filter<token> { token FilterValue }
|
||||
@ -41,6 +42,7 @@ filter<token> { token FilterValue }
|
||||
StateToken[@dialect=stateFilter] { filterToken<"state"> }
|
||||
TypeToken[@dialect=typeFilter] { filterToken<"type"> }
|
||||
HealthToken[@dialect=healthFilter] { filterToken<"health"> }
|
||||
DashboardToken[@dialect=dashboardFilter] { filterToken<"dashboard"> }
|
||||
|
||||
@precedence { DataSourceToken, word }
|
||||
@precedence { NameSpaceToken, word }
|
||||
@ -50,5 +52,6 @@ filter<token> { token FilterValue }
|
||||
@precedence { StateToken, word }
|
||||
@precedence { TypeToken, word }
|
||||
@precedence { HealthToken, word }
|
||||
@precedence { DashboardToken, word }
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -10,7 +10,8 @@ export const AlertRuleSearch = 1,
|
||||
StateToken = 9,
|
||||
TypeToken = 10,
|
||||
HealthToken = 11,
|
||||
FreeFormExpression = 12,
|
||||
DashboardToken = 12,
|
||||
FreeFormExpression = 13,
|
||||
Dialect_dataSourceFilter = 0,
|
||||
Dialect_nameSpaceFilter = 1,
|
||||
Dialect_labelFilter = 2,
|
||||
@ -18,4 +19,5 @@ export const AlertRuleSearch = 1,
|
||||
Dialect_ruleFilter = 4,
|
||||
Dialect_stateFilter = 5,
|
||||
Dialect_typeFilter = 6,
|
||||
Dialect_healthFilter = 7;
|
||||
Dialect_healthFilter = 7,
|
||||
Dialect_dashboardFilter = 8;
|
||||
|
@ -13,6 +13,7 @@ const filterTokenToTypeMap: Record<number, string> = {
|
||||
[terms.StateToken]: 'state',
|
||||
[terms.TypeToken]: 'type',
|
||||
[terms.HealthToken]: 'health',
|
||||
[terms.DashboardToken]: 'dashboard',
|
||||
};
|
||||
|
||||
// This enum allows to configure parser behavior
|
||||
@ -27,6 +28,7 @@ export enum FilterSupportedTerm {
|
||||
state = 'stateFilter',
|
||||
type = 'typeFilter',
|
||||
health = 'healthFilter',
|
||||
dashboard = 'dashboardFilter',
|
||||
}
|
||||
|
||||
export type QueryFilterMapper = Record<number, (filter: string) => void>;
|
||||
|
@ -189,7 +189,7 @@ export function fetchPromAndRulerRulesAction({
|
||||
limitAlerts?: number;
|
||||
matcher?: Matcher[];
|
||||
state?: string[];
|
||||
}): ThunkResult<void> {
|
||||
}): ThunkResult<Promise<void>> {
|
||||
return async (dispatch, getState) => {
|
||||
await dispatch(fetchRulesSourceBuildInfoAction({ rulesSourceName }));
|
||||
const dsConfig = getDataSourceConfig(getState, rulesSourceName);
|
||||
|
Loading…
Reference in New Issue
Block a user