Alerting: Enable server-side folder search for GMA rules (#116201)
* Alerting: Support backend filtering for folder search Updates the Grafana managed rules API and filter logic to support server-side filtering by folder (namespace). Changes: - Add `searchFolder` parameter to `getGrafanaGroups` API endpoint - Map filter state `namespace` to `searchFolder` in backend filter - Disable client-side namespace filtering when backend filtering is enabled - Update tests to verify correct behavior for folder search with backend filters * Add missing property in filter options * Update tests
This commit is contained in:
@@ -46,6 +46,7 @@ export type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'nam
|
||||
state?: PromAlertingRuleState[];
|
||||
title?: string;
|
||||
searchGroupName?: string;
|
||||
searchFolder?: string;
|
||||
type?: 'alerting' | 'recording';
|
||||
ruleMatchers?: string[];
|
||||
plugins?: 'hide' | 'only';
|
||||
@@ -103,6 +104,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
|
||||
title,
|
||||
datasources,
|
||||
searchGroupName,
|
||||
searchFolder,
|
||||
dashboardUid,
|
||||
ruleMatchers,
|
||||
plugins,
|
||||
@@ -123,6 +125,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
|
||||
datasource_uid: datasources,
|
||||
'search.rule_name': title,
|
||||
'search.rule_group': searchGroupName,
|
||||
'search.folder': searchFolder,
|
||||
dashboard_uid: dashboardUid,
|
||||
rule_matcher: ruleMatchers,
|
||||
plugins: plugins,
|
||||
|
||||
@@ -455,6 +455,25 @@ describe('grafana-managed rules', () => {
|
||||
expect(frontendFilter.ruleMatches(regularRule)).toBe(true);
|
||||
expect(frontendFilter.ruleMatches(pluginRule)).toBe(true);
|
||||
});
|
||||
|
||||
it('should include searchFolder in backend filter when namespace is provided', () => {
|
||||
const { backendFilter } = getGrafanaFilter(getFilter({ namespace: 'my-folder' }));
|
||||
|
||||
expect(backendFilter.searchFolder).toBe('my-folder');
|
||||
});
|
||||
|
||||
it('should skip namespace filtering on frontend when backend filtering is enabled', () => {
|
||||
const group: PromRuleGroupDTO = {
|
||||
name: 'Test Group',
|
||||
file: 'production/alerts',
|
||||
rules: [],
|
||||
interval: 60,
|
||||
};
|
||||
|
||||
const { frontendFilter } = getGrafanaFilter(getFilter({ namespace: 'staging' }));
|
||||
// Should return true because namespace filter is null (handled by backend)
|
||||
expect(frontendFilter.groupMatches(group)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when alertingUIUseBackendFilters is disabled', () => {
|
||||
@@ -537,6 +556,12 @@ describe('grafana-managed rules', () => {
|
||||
expect(backendFilter.searchGroupName).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not include searchFolder in backend filter', () => {
|
||||
const { backendFilter } = getGrafanaFilter(getFilter({ namespace: 'my-folder' }));
|
||||
|
||||
expect(backendFilter.searchFolder).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should perform groupName filtering on frontend', () => {
|
||||
const group: PromRuleGroupDTO = {
|
||||
name: 'CPU Usage Alerts',
|
||||
@@ -706,8 +731,8 @@ describe('grafana-managed rules', () => {
|
||||
expect(frontendFilter.groupMatches(group)).toBe(true);
|
||||
});
|
||||
|
||||
it('should still apply always-frontend filters (namespace)', () => {
|
||||
// Namespace filter should still work
|
||||
it('should skip namespace filtering on frontend', () => {
|
||||
// Namespace filter should be handled by backend
|
||||
const group: PromRuleGroupDTO = {
|
||||
name: 'Test Group',
|
||||
file: 'production/alerts',
|
||||
@@ -719,7 +744,7 @@ describe('grafana-managed rules', () => {
|
||||
expect(nsFilter.groupMatches(group)).toBe(true);
|
||||
|
||||
const { frontendFilter: nsFilter2 } = getGrafanaFilter(getFilter({ namespace: 'staging' }));
|
||||
expect(nsFilter2.groupMatches(group)).toBe(false);
|
||||
expect(nsFilter2.groupMatches(group)).toBe(true);
|
||||
});
|
||||
|
||||
it('should skip dataSourceNames filtering on frontend (handled by backend)', () => {
|
||||
@@ -807,8 +832,8 @@ describe('grafana-managed rules', () => {
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for client-side only filters', () => {
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
it('should return false for namespace filter (handled by backend)', () => {
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for plugins filter (handled by backend when feature toggle is enabled)', () => {
|
||||
@@ -862,8 +887,8 @@ describe('grafana-managed rules', () => {
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
|
||||
// Should return true for: always-frontend filters only (namespace)
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
// Should return false for: namespace (handled by backend)
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(false);
|
||||
|
||||
// plugins is backend-handled when both feature toggles are enabled
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ plugins: 'hide' }))).toBe(false);
|
||||
|
||||
@@ -96,6 +96,7 @@ export function getGrafanaFilter(filterState: Partial<RulesFilter>) {
|
||||
datasources: ruleFilterConfig.dataSourceNames ? undefined : datasourceUids,
|
||||
ruleMatchers: ruleMatchersBackendFilter,
|
||||
plugins: ruleFilterConfig.plugins ? undefined : normalizedFilterState.plugins,
|
||||
searchFolder: groupFilterConfig.namespace ? undefined : normalizedFilterState.namespace,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -134,7 +135,7 @@ function buildGrafanaFilterConfigs() {
|
||||
};
|
||||
|
||||
const groupFilterConfig: GroupFilterConfig = {
|
||||
namespace: namespaceFilter,
|
||||
namespace: useBackendFilters ? null : namespaceFilter,
|
||||
groupName: useBackendFilters ? null : groupNameFilter,
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ interface GrafanaPromApiFilter {
|
||||
contactPoint?: string;
|
||||
title?: string;
|
||||
searchGroupName?: string;
|
||||
searchFolder?: string;
|
||||
type?: 'alerting' | 'recording';
|
||||
dashboardUid?: string;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ describe('paginationLimits', () => {
|
||||
{ contactPoint: 'slack' },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
{ namespace: 'production' },
|
||||
])(
|
||||
'should return rule limit for grafana + large limit for datasource when only backend filters are used: %p',
|
||||
(filterState) => {
|
||||
@@ -84,16 +85,6 @@ describe('paginationLimits', () => {
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ namespace: 'production' },
|
||||
{ ruleState: PromAlertingRuleState.Firing, namespace: 'production' },
|
||||
])('should return large limits for both when frontend filters are used: %p', (filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when alertingUIUseFullyCompatBackendFilters is enabled', () => {
|
||||
@@ -158,6 +149,7 @@ describe('paginationLimits', () => {
|
||||
{ contactPoint: 'slack' },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
{ namespace: 'production' },
|
||||
])(
|
||||
'should return rule limit for grafana + large limit for datasource when only backend filters are used: %p',
|
||||
(filterState) => {
|
||||
@@ -167,16 +159,6 @@ describe('paginationLimits', () => {
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
|
||||
it.each<Partial<RulesFilter>>([{ namespace: 'production' }])(
|
||||
'should return large limits for both when frontend filters are used: %p',
|
||||
(filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user