Alerting: List V2 - Use backend filters for GMA rules (#106897)

* Use state, health and receiver_name query params for server-side filtering

* Remove Unknown option from state filters
This commit is contained in:
Konrad Lalik
2025-06-18 15:47:53 +02:00
committed by GitHub
parent c7e338342a
commit 94fb519a2b
4 changed files with 60 additions and 13 deletions
@@ -1,7 +1,13 @@
import { useCallback } from 'react';
import { useDispatch } from 'app/types';
import { GrafanaPromRuleGroupDTO, PromRuleDTO, PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { RuleHealth } from 'app/types/unified-alerting';
import {
GrafanaPromRuleGroupDTO,
PromAlertingRuleState,
PromRuleDTO,
PromRuleGroupDTO,
} from 'app/types/unified-alerting-dto';
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
@@ -33,6 +39,9 @@ type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace'
dashboardUid?: string;
panelId?: number;
limitAlerts?: number;
contactPoint?: string;
health?: RuleHealth[];
state?: PromAlertingRuleState[];
};
export const prometheusApi = alertingApi.injectEndpoints({
@@ -72,12 +81,25 @@ export const prometheusApi = alertingApi.injectEndpoints({
},
}),
getGrafanaGroups: build.query<PromRulesResponse<GrafanaPromRuleGroupDTO>, GrafanaPromRulesOptions>({
query: ({ folderUid, groupName, ruleName, groupLimit, limitAlerts, groupNextToken }) => ({
query: ({
folderUid,
groupName,
ruleName,
contactPoint,
health,
state,
groupLimit,
limitAlerts,
groupNextToken,
}) => ({
url: `api/prometheus/grafana/api/v1/rules`,
params: {
folder_uid: folderUid,
rule_group: groupName,
rule_name: ruleName,
receiver_name: contactPoint,
health: health,
state: state,
limit_alerts: limitAlerts,
group_limit: groupLimit?.toFixed(0),
group_next_token: groupNextToken,
@@ -48,10 +48,12 @@ interface RulesFilerProps {
onViewModeChange?: (viewMode: SupportedView) => void;
}
const RuleStateOptions = Object.entries(PromAlertingRuleState).map(([key, value]) => ({
label: alertStateToReadable(value),
value,
}));
const RuleStateOptions = Object.entries(PromAlertingRuleState)
.filter(([key, value]) => value !== PromAlertingRuleState.Unknown) // Exclude Unknown state from filter options
.map(([key, value]) => ({
label: alertStateToReadable(value),
value,
}));
const RulesFilter = ({ onClear = () => undefined, viewMode, onViewModeChange }: RulesFilerProps) => {
const styles = useStyles2(getStyles);
@@ -1,8 +1,8 @@
import { useCallback } from 'react';
import { useDispatch } from 'app/types/store';
import { DataSourceRulesSourceIdentifier } from 'app/types/unified-alerting';
import { PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { DataSourceRulesSourceIdentifier, RuleHealth } from 'app/types/unified-alerting';
import { PromAlertingRuleState, PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
import { alertRuleApi } from '../../api/alertRuleApi';
import { PromRulesResponse, prometheusApi } from '../../api/prometheusApi';
@@ -57,13 +57,27 @@ export function usePrometheusGroupsGenerator(hookOptions: UseGeneratorHookOption
);
}
interface GrafanaPromApiFilter {
state?: PromAlertingRuleState[];
health?: RuleHealth[];
contactPoint?: string;
}
interface GrafanaFetchGroupsOptions extends FetchGroupsOptions {
filter?: GrafanaPromApiFilter;
}
export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions = {}) {
const dispatch = useDispatch();
const [getGrafanaGroups] = useLazyGetGrafanaGroupsQuery();
const getGroupsAndProvideCache = useCallback(
async (fetchOptions: FetchGroupsOptions) => {
const response = await getGrafanaGroups({ ...fetchOptions, limitAlerts: hookOptions.limitAlerts }).unwrap();
async (fetchOptions: GrafanaFetchGroupsOptions) => {
const response = await getGrafanaGroups({
...fetchOptions,
limitAlerts: hookOptions.limitAlerts,
...fetchOptions.filter,
}).unwrap();
// This is not mandatory to preload ruler rules, but it improves the UX
// Because the user waits a bit longer for the initial load but doesn't need to wait for each group to be loaded
@@ -94,8 +108,11 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
);
return useCallback(
async function* (groupLimit: number) {
yield* genericGroupsGenerator(getGroupsAndProvideCache, groupLimit);
async function* (groupLimit: number, filter?: GrafanaPromApiFilter) {
yield* genericGroupsGenerator(
(fetchOptions) => getGroupsAndProvideCache({ ...fetchOptions, filter }),
groupLimit
);
},
[getGroupsAndProvideCache]
);
@@ -65,7 +65,13 @@ export function useFilteredRulesIteratorProvider() {
const normalizedFilterState = normalizeFilterState(filterState);
const hasDataSourceFilterActive = Boolean(filterState.dataSourceNames.length);
const grafanaRulesGenerator = from(grafanaGroupsGenerator(groupLimit)).pipe(
const grafanaRulesGenerator = from(
grafanaGroupsGenerator(groupLimit, {
contactPoint: filterState.contactPoint ?? undefined,
health: filterState.ruleHealth ? [filterState.ruleHealth] : [],
state: filterState.ruleState ? [filterState.ruleState] : [],
})
).pipe(
withAbort(abortController.signal),
concatMap((groups) =>
groups