Alerting: Add rule_type filter to the rules endpoint (#113701)

Alerting: Add type filter parameter to the rules endpoint
This commit is contained in:
Alexander Akhmetov
2025-11-14 13:03:15 +01:00
committed by GitHub
parent bfa7ce9d78
commit 6d7ce16883
5 changed files with 27 additions and 5 deletions
@@ -481,6 +481,16 @@ func PrepareRuleGroupStatusesV2(log log.Logger, store ListAlertRulesStoreV2, opt
receiverName := opts.Query.Get("receiver_name")
title := opts.Query.Get("search.rule_name")
var ruleType ngmodels.RuleTypeFilter
switch ngmodels.RuleType(opts.Query.Get("rule_type")) {
case ngmodels.RuleTypeAlerting:
ruleType = ngmodels.RuleTypeFilterAlerting
case ngmodels.RuleTypeRecording:
ruleType = ngmodels.RuleTypeFilterRecording
default:
ruleType = ngmodels.RuleTypeFilterAll
}
maxGroups := getInt64WithDefault(opts.Query, "group_limit", -1)
nextToken := opts.Query.Get("group_next_token")
@@ -498,6 +508,7 @@ func PrepareRuleGroupStatusesV2(log log.Logger, store ListAlertRulesStoreV2, opt
ReceiverName: receiverName,
SearchTitle: title,
},
RuleType: ruleType,
Limit: maxGroups,
ContinueToken: nextToken,
}
@@ -43,6 +43,7 @@ type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'namespace'
health?: RuleHealth[];
state?: PromAlertingRuleState[];
title?: string;
type?: 'alerting' | 'recording';
};
export const prometheusApi = alertingApi.injectEndpoints({
@@ -89,6 +90,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
contactPoint,
health,
state,
type,
groupLimit,
limitAlerts,
groupNextToken,
@@ -102,6 +104,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
receiver_name: contactPoint,
health: health,
state: state,
rule_type: type,
limit_alerts: limitAlerts,
group_limit: groupLimit?.toFixed(0),
group_next_token: groupNextToken,
@@ -47,6 +47,7 @@ interface GrafanaPromApiFilter {
health?: RuleHealth[];
contactPoint?: string;
title?: string;
type?: 'alerting' | 'recording';
}
interface GrafanaFetchGroupsOptions extends FetchGroupsOptions {
@@ -1,4 +1,5 @@
import { config } from '@grafana/runtime';
import { PromRuleType } from 'app/types/unified-alerting-dto';
import { RuleSource } from '../../search/rulesSearchParser';
import { getFilter } from '../../utils/search';
@@ -21,9 +22,10 @@ describe('hasClientSideFilters', () => {
config.featureToggles.alertingUIUseBackendFilters = true;
});
it('should return false for title search filters (backend-supported)', () => {
it('should return false for backend-supported filters (title and type)', () => {
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(false);
expect(hasClientSideFilters(getFilter({ ruleName: 'test' }))).toBe(false);
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
});
it('should return true for client-side only filters', () => {
@@ -44,9 +46,10 @@ describe('hasClientSideFilters', () => {
config.featureToggles.alertingUIUseBackendFilters = false;
});
it('should return true for title search filters (client-side fallback)', () => {
it('should return true for title and type filters (client-side fallback)', () => {
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
expect(hasClientSideFilters(getFilter({ ruleName: 'test' }))).toBe(true);
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(true);
});
it('should return true for client-side only filters', () => {
@@ -67,10 +70,11 @@ describe('hasClientSideFilters', () => {
config.featureToggles.alertingUIUseBackendFilters = undefined;
});
it('should return true for title search filters (backward compatibility)', () => {
it('should return true for title and type filters (backward compatibility)', () => {
// Default behavior should be client-side filtering
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
expect(hasClientSideFilters(getFilter({ ruleName: 'test' }))).toBe(true);
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(true);
});
});
});
@@ -83,6 +83,7 @@ export function useFilteredRulesIteratorProvider() {
const useBackendFilters = shouldUseBackendFilters();
const titleSearch = useBackendFilters ? buildTitleSearch(filterState) : undefined;
const ruleType = useBackendFilters ? filterState.ruleType : undefined;
const grafanaRulesGenerator: AsyncIterableX<RuleWithOrigin> = from(
grafanaGroupsGenerator(groupLimit, {
@@ -90,6 +91,7 @@ export function useFilteredRulesIteratorProvider() {
health: filterState.ruleHealth ? [filterState.ruleHealth] : [],
state: filterState.ruleState ? [filterState.ruleState] : [],
title: titleSearch,
type: ruleType,
})
).pipe(
withAbort(abortController.signal),
@@ -158,8 +160,9 @@ export function hasClientSideFilters(filterState: RulesFilter): boolean {
const useBackendFilters = shouldUseBackendFilters();
return (
// When backend filters are disabled, title search needs client-side filtering
(!useBackendFilters && (filterState.freeFormWords.length > 0 || Boolean(filterState.ruleName))) ||
// When backend filters are disabled, title search and type filter need client-side filtering
(!useBackendFilters &&
(filterState.freeFormWords.length > 0 || Boolean(filterState.ruleName) || Boolean(filterState.ruleType))) ||
// Client-side only filters:
Boolean(filterState.namespace) ||
filterState.dataSourceNames.length > 0 ||