mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Display correct results when using different filters on alerting panels (#70482)
* Trigger separate rules request for each alerting panel in a dashboard * Add RTK method to fetch prom rules * Use RTKQuery to get prom rules in UnifiedAlertList * Fix lint * Mock promRules call * Address PR comments * Fix tests
This commit is contained in:
parent
cbf4fe9d23
commit
f17c49e632
@ -1,10 +1,26 @@
|
|||||||
import { RelativeTimeRange } from '@grafana/data';
|
import { RelativeTimeRange } from '@grafana/data';
|
||||||
import { AlertQuery, Annotations, GrafanaAlertStateDecision, Labels } from 'app/types/unified-alerting-dto';
|
import { Matcher } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
import { RuleIdentifier, RuleNamespace } from 'app/types/unified-alerting';
|
||||||
|
import {
|
||||||
|
AlertQuery,
|
||||||
|
Annotations,
|
||||||
|
GrafanaAlertStateDecision,
|
||||||
|
Labels,
|
||||||
|
PromRulesResponse,
|
||||||
|
} from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { Folder } from '../components/rule-editor/RuleFolderPicker';
|
import { Folder } from '../components/rule-editor/RuleFolderPicker';
|
||||||
|
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
import { arrayKeyValuesToObject } from '../utils/labels';
|
import { arrayKeyValuesToObject } from '../utils/labels';
|
||||||
|
import { isCloudRuleIdentifier, isPrometheusRuleIdentifier } from '../utils/rules';
|
||||||
|
|
||||||
import { alertingApi } from './alertingApi';
|
import { alertingApi } from './alertingApi';
|
||||||
|
import {
|
||||||
|
FetchPromRulesFilter,
|
||||||
|
groupRulesByFileName,
|
||||||
|
paramsWithMatcherAndState,
|
||||||
|
prepareRulesFilterQueryParams,
|
||||||
|
} from './prometheus';
|
||||||
|
|
||||||
export type ResponseLabels = {
|
export type ResponseLabels = {
|
||||||
labels: AlertInstances[];
|
labels: AlertInstances[];
|
||||||
@ -17,6 +33,8 @@ export interface Datasource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const PREVIEW_URL = '/api/v1/rule/test/grafana';
|
export const PREVIEW_URL = '/api/v1/rule/test/grafana';
|
||||||
|
export const PROM_RULES_URL = 'api/prometheus/grafana/api/v1/rules';
|
||||||
|
|
||||||
export interface Data {
|
export interface Data {
|
||||||
refId: string;
|
refId: string;
|
||||||
relativeTimeRange: RelativeTimeRange;
|
relativeTimeRange: RelativeTimeRange;
|
||||||
@ -76,5 +94,38 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
prometheusRulesByNamespace: build.query<
|
||||||
|
RuleNamespace[],
|
||||||
|
{
|
||||||
|
limitAlerts?: number;
|
||||||
|
identifier?: RuleIdentifier;
|
||||||
|
filter?: FetchPromRulesFilter;
|
||||||
|
state?: string[];
|
||||||
|
matcher?: Matcher[];
|
||||||
|
}
|
||||||
|
>({
|
||||||
|
query: ({ limitAlerts, identifier, filter, state, matcher }) => {
|
||||||
|
const searchParams = new URLSearchParams();
|
||||||
|
|
||||||
|
// if we're fetching for Grafana managed rules, we should add a limit to the number of alert instances
|
||||||
|
// we do this because the response is large otherwise and we don't show all of them in the UI anyway.
|
||||||
|
if (limitAlerts) {
|
||||||
|
searchParams.set('limit_alerts', String(limitAlerts));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifier && (isPrometheusRuleIdentifier(identifier) || isCloudRuleIdentifier(identifier))) {
|
||||||
|
searchParams.set('file', identifier.namespace);
|
||||||
|
searchParams.set('rule_group', identifier.groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = prepareRulesFilterQueryParams(searchParams, filter);
|
||||||
|
|
||||||
|
return { url: PROM_RULES_URL, params: paramsWithMatcherAndState(params, state, matcher) };
|
||||||
|
},
|
||||||
|
transformResponse: (response: PromRulesResponse): RuleNamespace[] => {
|
||||||
|
return groupRulesByFileName(response.data.groups, GRAFANA_RULES_SOURCE_NAME);
|
||||||
|
},
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@ import { lastValueFrom } from 'rxjs';
|
|||||||
import { getBackendSrv } from '@grafana/runtime';
|
import { getBackendSrv } from '@grafana/runtime';
|
||||||
import { Matcher } from 'app/plugins/datasource/alertmanager/types';
|
import { Matcher } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { RuleIdentifier, RuleNamespace } from 'app/types/unified-alerting';
|
import { RuleIdentifier, RuleNamespace } from 'app/types/unified-alerting';
|
||||||
import { PromRulesResponse } from 'app/types/unified-alerting-dto';
|
import { PromRuleGroupDTO, PromRulesResponse } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { getDatasourceAPIUid, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { getDatasourceAPIUid, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
import { isCloudRuleIdentifier, isPrometheusRuleIdentifier } from '../utils/rules';
|
import { isCloudRuleIdentifier, isPrometheusRuleIdentifier } from '../utils/rules';
|
||||||
@ -83,6 +83,26 @@ export function paramsWithMatcherAndState(
|
|||||||
return paramsResult;
|
return paramsResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const groupRulesByFileName = (groups: PromRuleGroupDTO[], dataSourceName: string) => {
|
||||||
|
const nsMap: { [key: string]: RuleNamespace } = {};
|
||||||
|
groups.forEach((group) => {
|
||||||
|
group.rules.forEach((rule) => {
|
||||||
|
rule.query = rule.query || '';
|
||||||
|
});
|
||||||
|
if (!nsMap[group.file]) {
|
||||||
|
nsMap[group.file] = {
|
||||||
|
dataSourceName,
|
||||||
|
name: group.file,
|
||||||
|
groups: [group],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
nsMap[group.file].groups.push(group);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Object.values(nsMap);
|
||||||
|
};
|
||||||
|
|
||||||
export async function fetchRules(
|
export async function fetchRules(
|
||||||
dataSourceName: string,
|
dataSourceName: string,
|
||||||
filter?: FetchPromRulesFilter,
|
filter?: FetchPromRulesFilter,
|
||||||
@ -116,21 +136,5 @@ export async function fetchRules(
|
|||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
const nsMap: { [key: string]: RuleNamespace } = {};
|
return groupRulesByFileName(response.data.data.groups, dataSourceName);
|
||||||
response.data.data.groups.forEach((group) => {
|
|
||||||
group.rules.forEach((rule) => {
|
|
||||||
rule.query = rule.query || '';
|
|
||||||
});
|
|
||||||
if (!nsMap[group.file]) {
|
|
||||||
nsMap[group.file] = {
|
|
||||||
dataSourceName,
|
|
||||||
name: group.file,
|
|
||||||
groups: [group],
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
nsMap[group.file].groups.push(group);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return Object.values(nsMap);
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
getAllRulesSources,
|
getAllRulesSources,
|
||||||
getRulesSourceByName,
|
getRulesSourceByName,
|
||||||
|
GRAFANA_RULES_SOURCE_NAME,
|
||||||
isCloudRulesSource,
|
isCloudRulesSource,
|
||||||
isGrafanaRulesSource,
|
isGrafanaRulesSource,
|
||||||
} from '../utils/datasource';
|
} from '../utils/datasource';
|
||||||
@ -45,7 +46,10 @@ interface CacheValue {
|
|||||||
|
|
||||||
// this little monster combines prometheus rules and ruler rules to produce a unified data structure
|
// this little monster combines prometheus rules and ruler rules to produce a unified data structure
|
||||||
// can limit to a single rules source
|
// can limit to a single rules source
|
||||||
export function useCombinedRuleNamespaces(rulesSourceName?: string): CombinedRuleNamespace[] {
|
export function useCombinedRuleNamespaces(
|
||||||
|
rulesSourceName?: string,
|
||||||
|
grafanaPromRuleNamespaces?: RuleNamespace[]
|
||||||
|
): CombinedRuleNamespace[] {
|
||||||
const promRulesResponses = useUnifiedAlertingSelector((state) => state.promRules);
|
const promRulesResponses = useUnifiedAlertingSelector((state) => state.promRules);
|
||||||
const rulerRulesResponses = useUnifiedAlertingSelector((state) => state.rulerRules);
|
const rulerRulesResponses = useUnifiedAlertingSelector((state) => state.rulerRules);
|
||||||
|
|
||||||
@ -67,9 +71,13 @@ export function useCombinedRuleNamespaces(rulesSourceName?: string): CombinedRul
|
|||||||
return rulesSources
|
return rulesSources
|
||||||
.map((rulesSource): CombinedRuleNamespace[] => {
|
.map((rulesSource): CombinedRuleNamespace[] => {
|
||||||
const rulesSourceName = isCloudRulesSource(rulesSource) ? rulesSource.name : rulesSource;
|
const rulesSourceName = isCloudRulesSource(rulesSource) ? rulesSource.name : rulesSource;
|
||||||
const promRules = promRulesResponses[rulesSourceName]?.result;
|
|
||||||
const rulerRules = rulerRulesResponses[rulesSourceName]?.result;
|
const rulerRules = rulerRulesResponses[rulesSourceName]?.result;
|
||||||
|
|
||||||
|
let promRules = promRulesResponses[rulesSourceName]?.result;
|
||||||
|
if (rulesSourceName === GRAFANA_RULES_SOURCE_NAME && grafanaPromRuleNamespaces) {
|
||||||
|
promRules = grafanaPromRuleNamespaces;
|
||||||
|
}
|
||||||
|
|
||||||
const cached = cache.current[rulesSourceName];
|
const cached = cache.current[rulesSourceName];
|
||||||
if (cached && cached.promRules === promRules && cached.rulerRules === rulerRules) {
|
if (cached && cached.promRules === promRules && cached.rulerRules === rulerRules) {
|
||||||
return cached.result;
|
return cached.result;
|
||||||
@ -104,7 +112,7 @@ export function useCombinedRuleNamespaces(rulesSourceName?: string): CombinedRul
|
|||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
.flat();
|
.flat();
|
||||||
}, [promRulesResponses, rulerRulesResponses, rulesSources]);
|
}, [promRulesResponses, rulerRulesResponses, rulesSources, grafanaPromRuleNamespaces]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge all groups in case of grafana managed, essentially treating namespaces (folders) as groups
|
// merge all groups in case of grafana managed, essentially treating namespaces (folders) as groups
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { rest } from 'msw';
|
import { rest } from 'msw';
|
||||||
import { SetupServer } from 'msw/node';
|
import { SetupServer } from 'msw/node';
|
||||||
|
|
||||||
import { PreviewResponse, PREVIEW_URL } from '../api/alertRuleApi';
|
import { PromRulesResponse } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
|
import { PreviewResponse, PREVIEW_URL, PROM_RULES_URL } from '../api/alertRuleApi';
|
||||||
|
|
||||||
export function mockPreviewApiResponse(server: SetupServer, result: PreviewResponse) {
|
export function mockPreviewApiResponse(server: SetupServer, result: PreviewResponse) {
|
||||||
server.use(rest.post(PREVIEW_URL, (req, res, ctx) => res(ctx.json<PreviewResponse>(result))));
|
server.use(rest.post(PREVIEW_URL, (req, res, ctx) => res(ctx.json<PreviewResponse>(result))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function mockPromRulesApiResponse(server: SetupServer, result: PromRulesResponse) {
|
||||||
|
server.use(rest.get(PROM_RULES_URL, (req, res, ctx) => res(ctx.json<PromRulesResponse>(result))));
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
import alertDef from 'app/features/alerting/state/alertDef';
|
import alertDef from 'app/features/alerting/state/alertDef';
|
||||||
|
import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
|
||||||
import { INSTANCES_DISPLAY_LIMIT } from 'app/features/alerting/unified/components/rules/RuleDetails';
|
import { INSTANCES_DISPLAY_LIMIT } from 'app/features/alerting/unified/components/rules/RuleDetails';
|
||||||
import { useCombinedRuleNamespaces } from 'app/features/alerting/unified/hooks/useCombinedRuleNamespaces';
|
import { useCombinedRuleNamespaces } from 'app/features/alerting/unified/hooks/useCombinedRuleNamespaces';
|
||||||
import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector';
|
import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector';
|
||||||
@ -60,6 +61,8 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
|||||||
const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []);
|
const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []);
|
||||||
const [limitInstances, toggleLimit] = useToggle(true);
|
const [limitInstances, toggleLimit] = useToggle(true);
|
||||||
|
|
||||||
|
const { usePrometheusRulesByNamespaceQuery } = alertRuleApi;
|
||||||
|
|
||||||
// backwards compat for "Inactive" state filter
|
// backwards compat for "Inactive" state filter
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.options.stateFilter.inactive === true) {
|
if (props.options.stateFilter.inactive === true) {
|
||||||
@ -153,9 +156,19 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
|||||||
|
|
||||||
const promRulesRequests = useUnifiedAlertingSelector((state) => state.promRules);
|
const promRulesRequests = useUnifiedAlertingSelector((state) => state.promRules);
|
||||||
const rulerRulesRequests = useUnifiedAlertingSelector((state) => state.rulerRules);
|
const rulerRulesRequests = useUnifiedAlertingSelector((state) => state.rulerRules);
|
||||||
const combinedRules = useCombinedRuleNamespaces();
|
|
||||||
|
|
||||||
const somePromRulesDispatched = rulesDataSourceNames.some((name) => promRulesRequests[name]?.dispatched);
|
const somePromRulesDispatched = rulesDataSourceNames.some((name) => promRulesRequests[name]?.dispatched);
|
||||||
|
|
||||||
|
//For grafana managed rules, get the result using RTK Query to avoid the need of using the redux store
|
||||||
|
//See https://github.com/grafana/grafana/pull/70482
|
||||||
|
const { currentData: promRules = [], isLoading: grafanaRulesLoading } = usePrometheusRulesByNamespaceQuery({
|
||||||
|
limitAlerts: limitInstances ? INSTANCES_DISPLAY_LIMIT : undefined,
|
||||||
|
matcher: matcherList,
|
||||||
|
state: stateList,
|
||||||
|
});
|
||||||
|
|
||||||
|
const combinedRules = useCombinedRuleNamespaces(undefined, promRules);
|
||||||
|
|
||||||
const someRulerRulesDispatched = rulesDataSourceNames.some((name) => rulerRulesRequests[name]?.dispatched);
|
const someRulerRulesDispatched = rulesDataSourceNames.some((name) => rulerRulesRequests[name]?.dispatched);
|
||||||
const dispatched = somePromRulesDispatched || someRulerRulesDispatched;
|
const dispatched = somePromRulesDispatched || someRulerRulesDispatched;
|
||||||
|
|
||||||
@ -183,7 +196,7 @@ export function UnifiedAlertList(props: PanelProps<UnifiedAlertListOptions>) {
|
|||||||
return (
|
return (
|
||||||
<CustomScrollbar autoHeightMin="100%" autoHeightMax="100%">
|
<CustomScrollbar autoHeightMin="100%" autoHeightMax="100%">
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{dispatched && loading && !haveResults && <LoadingPlaceholder text="Loading..." />}
|
{(grafanaRulesLoading || (dispatched && loading && !haveResults)) && <LoadingPlaceholder text="Loading..." />}
|
||||||
{noAlertsMessage && <div className={styles.noAlertsMessage}>{noAlertsMessage}</div>}
|
{noAlertsMessage && <div className={styles.noAlertsMessage}>{noAlertsMessage}</div>}
|
||||||
<section>
|
<section>
|
||||||
{props.options.viewMode === ViewMode.Stat && haveResults && (
|
{props.options.viewMode === ViewMode.Stat && haveResults && (
|
||||||
|
@ -2,11 +2,17 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
import { act } from 'react-test-renderer';
|
||||||
import { byRole, byText } from 'testing-library-selector';
|
import { byRole, byText } from 'testing-library-selector';
|
||||||
|
|
||||||
import { FieldConfigSource, getDefaultTimeRange, LoadingState, PanelProps } from '@grafana/data';
|
import { FieldConfigSource, getDefaultTimeRange, LoadingState, PanelProps } from '@grafana/data';
|
||||||
import { TimeRangeUpdatedEvent } from '@grafana/runtime';
|
import { TimeRangeUpdatedEvent } from '@grafana/runtime';
|
||||||
|
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
|
||||||
|
import { mockPromRulesApiResponse } from 'app/features/alerting/unified/mocks/alertRuleApi';
|
||||||
|
import { mockRulerRulesApiResponse } from 'app/features/alerting/unified/mocks/rulerApi';
|
||||||
|
import { Annotation } from 'app/features/alerting/unified/utils/constants';
|
||||||
import { DashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
import { DashboardSrv, setDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||||
|
import { PromRuleGroupDTO, PromRulesResponse, RulerGrafanaRuleDTO } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { contextSrv } from '../../../core/services/context_srv';
|
import { contextSrv } from '../../../core/services/context_srv';
|
||||||
import {
|
import {
|
||||||
@ -14,6 +20,7 @@ import {
|
|||||||
mockPromAlertingRule,
|
mockPromAlertingRule,
|
||||||
mockPromRuleGroup,
|
mockPromRuleGroup,
|
||||||
mockPromRuleNamespace,
|
mockPromRuleNamespace,
|
||||||
|
mockRulerGrafanaRule,
|
||||||
mockUnifiedAlertingStore,
|
mockUnifiedAlertingStore,
|
||||||
} from '../../../features/alerting/unified/mocks';
|
} from '../../../features/alerting/unified/mocks';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../../features/alerting/unified/utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../../../features/alerting/unified/utils/datasource';
|
||||||
@ -22,8 +29,55 @@ import { UnifiedAlertList } from './UnifiedAlertList';
|
|||||||
import { GroupMode, SortOrder, UnifiedAlertListOptions, ViewMode } from './types';
|
import { GroupMode, SortOrder, UnifiedAlertListOptions, ViewMode } from './types';
|
||||||
import * as utils from './util';
|
import * as utils from './util';
|
||||||
|
|
||||||
|
const grafanaRuleMock = {
|
||||||
|
promRules: {
|
||||||
|
grafana: {
|
||||||
|
loading: false,
|
||||||
|
dispatched: true,
|
||||||
|
result: [
|
||||||
|
mockPromRuleNamespace({
|
||||||
|
name: 'ns1',
|
||||||
|
groups: [
|
||||||
|
mockPromRuleGroup({
|
||||||
|
name: 'group1',
|
||||||
|
rules: [
|
||||||
|
mockPromAlertingRule({
|
||||||
|
name: 'rule1',
|
||||||
|
alerts: [mockPromAlert({ labels: { severity: 'critical' } })],
|
||||||
|
totals: { alerting: 1 },
|
||||||
|
totalsFiltered: { alerting: 1 },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock('app/features/alerting/unified/api/alertmanager');
|
jest.mock('app/features/alerting/unified/api/alertmanager');
|
||||||
|
|
||||||
|
const fakeResponse: PromRulesResponse = {
|
||||||
|
data: { groups: grafanaRuleMock.promRules.grafana.result[0].groups as PromRuleGroupDTO[] },
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
|
||||||
|
const server = setupMswServer();
|
||||||
|
|
||||||
|
mockPromRulesApiResponse(server, fakeResponse);
|
||||||
|
const originRule: RulerGrafanaRuleDTO = mockRulerGrafanaRule(
|
||||||
|
{
|
||||||
|
for: '1m',
|
||||||
|
labels: { severity: 'critical', region: 'nasa' },
|
||||||
|
annotations: { [Annotation.summary]: 'This is a very important alert rule' },
|
||||||
|
},
|
||||||
|
{ uid: 'grafana-rule-1', title: 'First Grafana Rule', data: [] }
|
||||||
|
);
|
||||||
|
mockRulerRulesApiResponse(server, 'grafana', {
|
||||||
|
'folder-one': [{ name: 'group1', interval: '20s', rules: [originRule] }],
|
||||||
|
});
|
||||||
|
|
||||||
const defaultOptions: UnifiedAlertListOptions = {
|
const defaultOptions: UnifiedAlertListOptions = {
|
||||||
maxItems: 2,
|
maxItems: 2,
|
||||||
sortOrder: SortOrder.AlphaAsc,
|
sortOrder: SortOrder.AlphaAsc,
|
||||||
@ -73,32 +127,7 @@ const dashboard = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const renderPanel = (options: Partial<UnifiedAlertListOptions> = defaultOptions) => {
|
const renderPanel = (options: Partial<UnifiedAlertListOptions> = defaultOptions) => {
|
||||||
const store = mockUnifiedAlertingStore({
|
const store = mockUnifiedAlertingStore(grafanaRuleMock);
|
||||||
promRules: {
|
|
||||||
grafana: {
|
|
||||||
loading: false,
|
|
||||||
dispatched: true,
|
|
||||||
result: [
|
|
||||||
mockPromRuleNamespace({
|
|
||||||
name: 'ns1',
|
|
||||||
groups: [
|
|
||||||
mockPromRuleGroup({
|
|
||||||
name: 'group1',
|
|
||||||
rules: [
|
|
||||||
mockPromAlertingRule({
|
|
||||||
name: 'rule1',
|
|
||||||
alerts: [mockPromAlert({ labels: { severity: 'critical' } })],
|
|
||||||
totals: { alerting: 1 },
|
|
||||||
totalsFiltered: { alerting: 1 },
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const dashSrv: unknown = { getCurrent: () => dashboard };
|
const dashSrv: unknown = { getCurrent: () => dashboard };
|
||||||
setDashboardSrv(dashSrv as DashboardSrv);
|
setDashboardSrv(dashSrv as DashboardSrv);
|
||||||
@ -115,12 +144,19 @@ const renderPanel = (options: Partial<UnifiedAlertListOptions> = defaultOptions)
|
|||||||
describe('UnifiedAlertList', () => {
|
describe('UnifiedAlertList', () => {
|
||||||
it('subscribes to the dashboard refresh interval', async () => {
|
it('subscribes to the dashboard refresh interval', async () => {
|
||||||
jest.spyOn(defaultProps, 'replaceVariables').mockReturnValue('severity=critical');
|
jest.spyOn(defaultProps, 'replaceVariables').mockReturnValue('severity=critical');
|
||||||
await renderPanel();
|
|
||||||
|
await act(async () => {
|
||||||
|
renderPanel();
|
||||||
|
});
|
||||||
|
|
||||||
expect(dashboard.events.subscribe).toHaveBeenCalledTimes(1);
|
expect(dashboard.events.subscribe).toHaveBeenCalledTimes(1);
|
||||||
expect(dashboard.events.subscribe.mock.calls[0][0]).toEqual(TimeRangeUpdatedEvent);
|
expect(dashboard.events.subscribe.mock.calls[0][0]).toEqual(TimeRangeUpdatedEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace option variables before filtering', async () => {
|
it('should replace option variables before filtering', async () => {
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
|
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
|
||||||
const filterAlertsSpy = jest.spyOn(utils, 'filterAlerts');
|
const filterAlertsSpy = jest.spyOn(utils, 'filterAlerts');
|
||||||
|
|
||||||
@ -128,12 +164,18 @@ describe('UnifiedAlertList', () => {
|
|||||||
|
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
||||||
await renderPanel({
|
await act(async () => {
|
||||||
alertInstanceLabelFilter: '$label',
|
renderPanel({
|
||||||
dashboardAlerts: false,
|
alertInstanceLabelFilter: '$label',
|
||||||
alertName: '',
|
dashboardAlerts: false,
|
||||||
datasource: GRAFANA_RULES_SOURCE_NAME,
|
alertName: '',
|
||||||
folder: undefined,
|
datasource: GRAFANA_RULES_SOURCE_NAME,
|
||||||
|
folder: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(byText('rule1').get()).toBeInTheDocument();
|
expect(byText('rule1').get()).toBeInTheDocument();
|
||||||
|
Loading…
Reference in New Issue
Block a user