diff --git a/public/app/features/alerting/unified/api/alertRuleApi.ts b/public/app/features/alerting/unified/api/alertRuleApi.ts
index 1b9f006ad8c..9127ae5a0af 100644
--- a/public/app/features/alerting/unified/api/alertRuleApi.ts
+++ b/public/app/features/alerting/unified/api/alertRuleApi.ts
@@ -329,6 +329,18 @@ export const alertRuleApi = alertingApi.injectEndpoints({
invalidatesTags: (result, _error, { namespace, payload, rulerConfig }) => {
const grafanaRulerRules = payload.rules.filter(rulerRuleType.grafana.rule);
+ const promTags: Array<{ type: 'GrafanaPrometheusGroups'; id: string }> = [];
+ if (rulerConfig.dataSourceUid === GRAFANA_RULES_SOURCE_NAME) {
+ promTags.push(
+ { type: 'GrafanaPrometheusGroups', id: `grafana/${namespace}/__any__/` },
+ { type: 'GrafanaPrometheusGroups', id: `grafana/${namespace}/${payload.name}/__any__` },
+ ...grafanaRulerRules.map((rule) => ({
+ type: 'GrafanaPrometheusGroups' as const,
+ id: `grafana/${namespace}/${payload.name}/${rule.grafana_alert.title}`,
+ }))
+ );
+ }
+
return [
{ type: 'RuleNamespace', id: `${rulerConfig.dataSourceUid}/${namespace}` },
{ type: 'RuleGroup', id: `${rulerConfig.dataSourceUid}/${namespace}/${payload.name}` },
@@ -336,6 +348,7 @@ export const alertRuleApi = alertingApi.injectEndpoints({
{ type: 'GrafanaRulerRule', id: rule.grafana_alert.uid } as const,
{ type: 'GrafanaRulerRuleVersion', id: rule.grafana_alert.uid } as const,
]),
+ ...promTags,
'DeletedRules',
];
},
diff --git a/public/app/features/alerting/unified/api/alertingApi.ts b/public/app/features/alerting/unified/api/alertingApi.ts
index e3c28894ea5..8c6852fb07a 100644
--- a/public/app/features/alerting/unified/api/alertingApi.ts
+++ b/public/app/features/alerting/unified/api/alertingApi.ts
@@ -130,6 +130,7 @@ export const alertingApi = createApi({
'ContactPointsStatus',
'Receiver',
'DeletedRules',
+ 'GrafanaPrometheusGroups',
],
endpoints: () => ({}),
});
diff --git a/public/app/features/alerting/unified/api/prometheusApi.ts b/public/app/features/alerting/unified/api/prometheusApi.ts
index 24641f6c4e6..3c2a8b50efe 100644
--- a/public/app/features/alerting/unified/api/prometheusApi.ts
+++ b/public/app/features/alerting/unified/api/prometheusApi.ts
@@ -1,3 +1,6 @@
+import { useCallback } from 'react';
+
+import { useDispatch } from 'app/types';
import { GrafanaPromRuleGroupDTO, PromRuleDTO, PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
@@ -79,6 +82,31 @@ export const prometheusApi = alertingApi.injectEndpoints({
group_next_token: groupNextToken,
},
}),
+ providesTags: (_result, _error, { folderUid, groupName, ruleName }) => {
+ const folderKey = folderUid ?? '__any__';
+ const groupKey = groupName ?? '__any__';
+ const ruleKey = ruleName ?? '__any__';
+ return [{ type: 'GrafanaPrometheusGroups', id: `grafana/${folderKey}/${groupKey}/${ruleKey}` }];
+ },
}),
}),
});
+
+export function usePopulateGrafanaPrometheusApiCache() {
+ const dispatch = useDispatch();
+
+ const populateGroupResponseCache = useCallback(
+ (group: GrafanaPromRuleGroupDTO) => {
+ dispatch(
+ prometheusApi.util.upsertQueryData(
+ 'getGrafanaGroups',
+ { folderUid: group.folderUid, groupName: group.name },
+ { data: { groups: [group] }, status: 'success' }
+ )
+ );
+ },
+ [dispatch]
+ );
+
+ return { populateGroupResponseCache };
+}
diff --git a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx
index 87194f42eb9..67a0b30542a 100644
--- a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx
+++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx
@@ -18,7 +18,6 @@ import {
trackRulesSearchComponentInteraction,
trackRulesSearchInputInteraction,
} from '../../../Analytics';
-import { shouldUseAlertingListViewV2 } from '../../../featureToggles';
import { useRulesFilter } from '../../../hooks/useFilteredRules';
import { useAlertingHomePageExtensions } from '../../../plugins/useAlertingHomePageExtensions';
import { RuleHealth } from '../../../search/rulesSearchParser';
@@ -43,10 +42,8 @@ const RuleHealthOptions: SelectableValue[] = [
// Contact point selector is not supported in Alerting ListView V2 yet
const canRenderContactPointSelector =
- (contextSrv.hasPermission(AccessControlAction.AlertingReceiversRead) &&
- config.featureToggles.alertingSimplifiedRouting &&
- shouldUseAlertingListViewV2() === false) ??
- false;
+ contextSrv.hasPermission(AccessControlAction.AlertingReceiversRead) &&
+ config.featureToggles.alertingSimplifiedRouting;
interface RulesFilerProps {
onClear?: () => void;
diff --git a/public/app/features/alerting/unified/mocks.ts b/public/app/features/alerting/unified/mocks.ts
index 7c8e5374a5d..de3b06d7a08 100644
--- a/public/app/features/alerting/unified/mocks.ts
+++ b/public/app/features/alerting/unified/mocks.ts
@@ -231,6 +231,7 @@ export const mockGrafanaPromAlertingRule = (
...mockPromAlertingRule(),
uid: 'mock-rule-uid-123',
folderUid: 'NAMESPACE_UID',
+ isPaused: false,
...partial,
};
};
diff --git a/public/app/features/alerting/unified/rule-list/FilterView.tsx b/public/app/features/alerting/unified/rule-list/FilterView.tsx
index 1e7253457b9..f83523e5a71 100644
--- a/public/app/features/alerting/unified/rule-list/FilterView.tsx
+++ b/public/app/features/alerting/unified/rule-list/FilterView.tsx
@@ -158,7 +158,7 @@ function FilterViewResults({ filterState }: FilterViewProps) {
return (
diff --git a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.test.tsx b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.test.tsx
index 565c5fb4884..ef76569b713 100644
--- a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.test.tsx
+++ b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.test.tsx
@@ -164,6 +164,7 @@ function rulerRuleToPromRule(rule: RulerGrafanaRuleDTO): GrafanaPromRuleDTO {
query: JSON.stringify(rule.grafana_alert.data),
uid: rule.grafana_alert.uid,
folderUid: rule.grafana_alert.namespace_uid,
+ isPaused: false,
health: 'ok',
state: PromAlertingRuleState.Inactive,
type: rulerRuleType.grafana.alertingRule(rule) ? PromRuleType.Alerting : PromRuleType.Recording,
diff --git a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx
index df87f6fcd15..bf7ea3dd702 100644
--- a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx
+++ b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx
@@ -5,6 +5,7 @@ import { t } from 'app/core/internationalization';
import { GrafanaRuleGroupIdentifier } from 'app/types/unified-alerting';
import { GrafanaPromRuleDTO, RulerGrafanaRuleDTO } from 'app/types/unified-alerting-dto';
+import { logWarning } from '../Analytics';
import { alertRuleApi } from '../api/alertRuleApi';
import { prometheusApi } from '../api/prometheusApi';
import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
@@ -154,5 +155,13 @@ export function matchRules(
matchingResult.promOnlyRules.push(...promRulesMap.values());
+ if (matchingResult.promOnlyRules.length > 0) {
+ // Grafana Prometheus rules should be strongly consistent now so each Ruler rule should have a matching Prometheus rule
+ // If not, log it as a warning
+ logWarning('Grafana Managed Rules: No matching Prometheus rule found for Ruler rule', {
+ promOnlyRulesCount: matchingResult.promOnlyRules.length.toString(),
+ });
+ }
+
return matchingResult;
}
diff --git a/public/app/features/alerting/unified/rule-list/GrafanaRuleLoader.tsx b/public/app/features/alerting/unified/rule-list/GrafanaRuleLoader.tsx
index 5cb1b7ebf83..08194fc65a4 100644
--- a/public/app/features/alerting/unified/rule-list/GrafanaRuleLoader.tsx
+++ b/public/app/features/alerting/unified/rule-list/GrafanaRuleLoader.tsx
@@ -1,9 +1,10 @@
import { Alert } from '@grafana/ui';
import { Trans, t } from 'app/core/internationalization';
-import { GrafanaRuleGroupIdentifier } from 'app/types/unified-alerting';
+import { GrafanaRuleGroupIdentifier, GrafanaRuleIdentifier } from 'app/types/unified-alerting';
import { GrafanaPromRuleDTO, PromRuleType, RulerGrafanaRuleDTO } from 'app/types/unified-alerting-dto';
import { alertRuleApi } from '../api/alertRuleApi';
+import { prometheusApi } from '../api/prometheusApi';
import { GrafanaRulesSource } from '../utils/datasource';
import { rulerRuleType } from '../utils/rules';
import { createRelativeUrl } from '../utils/url';
@@ -19,44 +20,55 @@ import { RuleActionsButtons } from './components/RuleActionsButtons.V2';
import { RuleOperation } from './components/RuleListIcon';
const { useGetGrafanaRulerGroupQuery } = alertRuleApi;
+const { useGetGrafanaGroupsQuery } = prometheusApi;
interface GrafanaRuleLoaderProps {
- rule: GrafanaPromRuleDTO;
-
+ ruleIdentifier: GrafanaRuleIdentifier;
groupIdentifier: GrafanaRuleGroupIdentifier;
namespaceName: string;
}
-export function GrafanaRuleLoader({ rule, groupIdentifier, namespaceName }: GrafanaRuleLoaderProps) {
+export function GrafanaRuleLoader({ ruleIdentifier, groupIdentifier, namespaceName }: GrafanaRuleLoaderProps) {
const {
data: rulerRuleGroup,
- isError,
- isLoading,
+ error: rulerRuleGroupError,
+ isLoading: isRulerRuleGroupLoading,
} = useGetGrafanaRulerGroupQuery({
folderUid: groupIdentifier.namespace.uid,
groupName: groupIdentifier.groupName,
});
+ const {
+ data: promRuleGroup,
+ error: promRuleGroupError,
+ isLoading: isPromRuleGroupLoading,
+ } = useGetGrafanaGroupsQuery({
+ folderUid: groupIdentifier.namespace.uid,
+ groupName: groupIdentifier.groupName,
+ });
- const rulerRule = rulerRuleGroup?.rules.find((rulerRule) => rulerRule.grafana_alert.uid === rule.uid);
+ const rulerRule = rulerRuleGroup?.rules.find((rulerRule) => rulerRule.grafana_alert.uid === ruleIdentifier.uid);
+ const promRule = promRuleGroup?.data.groups
+ .flatMap((group) => group.rules)
+ .find((promRule) => promRule.uid === ruleIdentifier.uid);
- if (isError) {
- return ;
+ if (rulerRuleGroupError || promRuleGroupError) {
+ return ;
}
- if (isLoading) {
+ if (isRulerRuleGroupLoading || isPromRuleGroupLoading) {
return ;
}
if (!rulerRule) {
return (
- Cannot find rule details for {{ uid: rule.uid ?? '' }}
+ Cannot find rule details for UID {{ uid: ruleIdentifier.uid ?? '' }}
);
@@ -64,7 +76,7 @@ export function GrafanaRuleLoader({ rule, groupIdentifier, namespaceName }: Graf
return (
,
};
diff --git a/public/app/features/alerting/unified/rule-list/components/AlertRuleListItemLoader.tsx b/public/app/features/alerting/unified/rule-list/components/AlertRuleListItemLoader.tsx
index c74bd088ca6..e5a179231bc 100644
--- a/public/app/features/alerting/unified/rule-list/components/AlertRuleListItemLoader.tsx
+++ b/public/app/features/alerting/unified/rule-list/components/AlertRuleListItemLoader.tsx
@@ -1,7 +1,9 @@
import Skeleton from 'react-loading-skeleton';
import { t } from 'app/core/internationalization';
-import { PromRuleDTO } from 'app/types/unified-alerting-dto';
+import { GrafanaRuleIdentifier } from 'app/types/unified-alerting';
+
+import { stringifyErrorLike } from '../../utils/misc';
import { ListItem } from './ListItem';
import { RuleActionsSkeleton } from './RuleActionsSkeleton';
@@ -19,12 +21,16 @@ export function AlertRuleListItemSkeleton() {
);
}
-export function RulerRuleLoadingError({ rule }: { rule: PromRuleDTO }) {
- return (
-
- );
+export function RulerRuleLoadingError({
+ ruleIdentifier,
+ error,
+}: {
+ ruleIdentifier: GrafanaRuleIdentifier;
+ error?: unknown;
+}) {
+ const errorMessage = error
+ ? stringifyErrorLike(error)
+ : t('alerting.rule-list.rulerrule-loading-error', 'Failed to load the rule');
+
+ return ;
}
diff --git a/public/app/features/alerting/unified/rule-list/hooks/filters.ts b/public/app/features/alerting/unified/rule-list/hooks/filters.ts
index 89eb4c96f6b..e66ddb559a9 100644
--- a/public/app/features/alerting/unified/rule-list/hooks/filters.ts
+++ b/public/app/features/alerting/unified/rule-list/hooks/filters.ts
@@ -85,6 +85,20 @@ export function ruleFilter(rule: PromRuleDTO, filterState: RulesFilter) {
return false;
}
+ if (filterState.contactPoint) {
+ if (!prometheusRuleType.grafana.alertingRule(rule)) {
+ return false;
+ }
+
+ if (!rule.notificationSettings) {
+ return false;
+ }
+
+ if (filterState.contactPoint !== rule.notificationSettings.receiver) {
+ return false;
+ }
+ }
+
// Dashboard UID filter
if (filterState.dashboardUid) {
if (!prometheusRuleType.alertingRule(rule)) {
diff --git a/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts b/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts
index 50be4d9054d..0b7fc4159a6 100644
--- a/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts
+++ b/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts
@@ -15,6 +15,7 @@ import {
PromRuleGroupDTO,
} from 'app/types/unified-alerting-dto';
+import { usePopulateGrafanaPrometheusApiCache } from '../../api/prometheusApi';
import { RulesFilter } from '../../search/rulesSearchParser';
import {
getDataSourceByUid,
@@ -51,6 +52,7 @@ interface GetIteratorResult {
}
export function useFilteredRulesIteratorProvider() {
+ const { populateGroupResponseCache } = usePopulateGrafanaPrometheusApiCache();
const allExternalRulesSources = getExternalRulesSources();
const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
@@ -68,7 +70,10 @@ export function useFilteredRulesIteratorProvider() {
concatMap((groups) =>
groups
.filter((group) => groupFilter(group, normalizedFilterState))
- .flatMap((group) => group.rules.map((rule) => [group, rule] as const))
+ .flatMap((group) => {
+ populateGroupResponseCache(group);
+ return group.rules.map((rule) => [group, rule] as const);
+ })
.filter(([, rule]) => ruleFilter(rule, normalizedFilterState))
.map(([group, rule]) => mapGrafanaRuleToRuleWithOrigin(group, rule))
),
diff --git a/public/app/features/browse-dashboards/fixtures/alertRules.fixture.ts b/public/app/features/browse-dashboards/fixtures/alertRules.fixture.ts
index be447f4fa6c..1363b10ad9a 100644
--- a/public/app/features/browse-dashboards/fixtures/alertRules.fixture.ts
+++ b/public/app/features/browse-dashboards/fixtures/alertRules.fixture.ts
@@ -80,6 +80,7 @@ export function getPrometheusRulesResponse(
query:
'[{"refId":"A","queryType":"","relativeTimeRange":{"from":600,"to":0},"datasourceUid":"gdev-testdata","model":{"hide":false,"intervalMs":1000,"maxDataPoints":43200,"refId":"A"}},{"refId":"B","queryType":"","relativeTimeRange":{"from":0,"to":0},"datasourceUid":"__expr__","model":{"conditions":[{"evaluator":{"params":[0,0],"type":"gt"},"operator":{"type":"and"},"query":{"params":[]},"reducer":{"params":[],"type":"avg"},"type":"query"}],"datasource":{"name":"Expression","type":"__expr__","uid":"__expr__"},"expression":"A","intervalMs":1000,"maxDataPoints":43200,"refId":"B","type":"threshold"}}]',
duration: 300,
+ isPaused: false,
health: 'ok',
type: PromRuleType.Alerting,
lastEvaluation: '0001-01-01T00:00:00Z',
diff --git a/public/app/types/unified-alerting-dto.ts b/public/app/types/unified-alerting-dto.ts
index c96d35b3736..b3d66445587 100644
--- a/public/app/types/unified-alerting-dto.ts
+++ b/public/app/types/unified-alerting-dto.ts
@@ -129,6 +129,7 @@ interface PromRuleDTOBase {
interface GrafanaPromRuleDTOBase extends PromRuleDTOBase {
uid: string;
folderUid: string;
+ isPaused: boolean;
queriedDatasourceUIDs?: string[];
}
@@ -145,6 +146,7 @@ export interface PromAlertingRuleDTO extends PromRuleDTOBase {
duration?: number; // for
state: PromAlertingRuleState;
type: PromRuleType.Alerting;
+ notificationSettings?: GrafanaNotificationSettings;
}
export interface PromRecordingRuleDTO extends PromRuleDTOBase {
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 2e2bd544668..68200392e56 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -2026,8 +2026,8 @@
"title-inspect-alert-rule": "Inspect Alert rule"
},
"rule-list": {
- "cannot-find-rule-details-for": "Cannot find rule details for {{uid}}",
- "cannot-load-rule-details-for": "Cannot load rule details for {{name}}",
+ "cannot-find-rule-details-for": "Cannot find rule details for UID {{uid}}",
+ "cannot-load-rule-details-for": "Cannot load rule details for UID {{uid}}",
"configure-datasource": "Configure",
"draft-new-rule": "Draft a new rule",
"ds-error-boundary": {