diff --git a/pkg/services/ngalert/api/prometheus/api_prometheus.go b/pkg/services/ngalert/api/prometheus/api_prometheus.go index a1884b56e98..2cfedbc81c4 100644 --- a/pkg/services/ngalert/api/prometheus/api_prometheus.go +++ b/pkg/services/ngalert/api/prometheus/api_prometheus.go @@ -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, } diff --git a/public/app/features/alerting/unified/api/prometheusApi.ts b/public/app/features/alerting/unified/api/prometheusApi.ts index 8aafa13a8b3..d4a23770b01 100644 --- a/public/app/features/alerting/unified/api/prometheusApi.ts +++ b/public/app/features/alerting/unified/api/prometheusApi.ts @@ -43,6 +43,7 @@ type GrafanaPromRulesOptions = Omit { 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); }); }); }); 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 12cbcfb7a73..3e225ed9b7a 100644 --- a/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts +++ b/public/app/features/alerting/unified/rule-list/hooks/useFilteredRulesIterator.ts @@ -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 = 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 ||