Alerting: Add rule_limits to rule list requests (#114176)
* Alerting: Add rule_limits to rule list requests * Unify pagination limits calculation for GMA and DMA rules * Fix limits, add tests * Alerting: Rename filter functions and limit properties for clarity - hasClientSideFilters → hasGrafanaClientSideFilters - hasDatasourceFilters → hasDatasourceClientSideFilters - gmaLimit → grafanaManagedLimit - dmaLimit → datasourceManagedLimit --------- Co-authored-by: Konrad Lalik <konradlalik@gmail.com>
This commit is contained in:
co-authored by
Konrad Lalik
parent
8e73cc2f70
commit
42d3673d04
@@ -39,6 +39,7 @@ export type GrafanaPromRulesOptions = Omit<PromRulesOptions, 'ruleSource' | 'nam
|
||||
dashboardUid?: string;
|
||||
panelId?: number;
|
||||
limitAlerts?: number;
|
||||
ruleLimit?: number;
|
||||
contactPoint?: string;
|
||||
health?: RuleHealth[];
|
||||
state?: PromAlertingRuleState[];
|
||||
@@ -93,6 +94,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
|
||||
state,
|
||||
type,
|
||||
groupLimit,
|
||||
ruleLimit,
|
||||
limitAlerts,
|
||||
groupNextToken,
|
||||
title,
|
||||
@@ -109,6 +111,7 @@ export const prometheusApi = alertingApi.injectEndpoints({
|
||||
state: state,
|
||||
rule_type: type,
|
||||
limit_alerts: limitAlerts,
|
||||
rule_limit: ruleLimit?.toFixed(0),
|
||||
group_limit: groupLimit?.toFixed(0),
|
||||
group_next_token: groupNextToken,
|
||||
'search.rule_name': title,
|
||||
|
||||
@@ -16,14 +16,13 @@ import { GrafanaRuleListItem } from './GrafanaRuleListItem';
|
||||
import LoadMoreHelper from './LoadMoreHelper';
|
||||
import { UnknownRuleListItem } from './components/AlertRuleListItem';
|
||||
import { AlertRuleListItemSkeleton } from './components/AlertRuleListItemLoader';
|
||||
import { hasClientSideFilters } from './hooks/grafanaFilter';
|
||||
import {
|
||||
GrafanaRuleWithOrigin,
|
||||
PromRuleWithOrigin,
|
||||
RuleWithOrigin,
|
||||
useFilteredRulesIteratorProvider,
|
||||
} from './hooks/useFilteredRulesIterator';
|
||||
import { FRONTEND_LIST_PAGE_SIZE, getSearchApiGroupPageSize } from './paginationLimits';
|
||||
import { FRONTEND_LIST_PAGE_SIZE, getFilteredRulesLimits } from './paginationLimits';
|
||||
|
||||
interface FilterViewProps {
|
||||
filterState: RulesFilter;
|
||||
@@ -78,10 +77,7 @@ function FilterViewResults({ filterState }: FilterViewProps) {
|
||||
* ⚠️ Make sure we are returning / using a "iterator" and not an "iterable" since the iterable is only a blueprint
|
||||
* and the iterator will allow us to exhaust the iterable in a stateful way
|
||||
*/
|
||||
const { iterable, abortController } = getFilteredRulesIterator(
|
||||
filterState,
|
||||
getSearchApiGroupPageSize(hasClientSideFilters(filterState))
|
||||
);
|
||||
const { iterable, abortController } = getFilteredRulesIterator(filterState, getFilteredRulesLimits(filterState));
|
||||
const rulesBatchIterator = iterable
|
||||
.pipe(
|
||||
bufferCountOrTime(FRONTEND_LIST_PAGE_SIZE, 1000),
|
||||
|
||||
@@ -7,7 +7,6 @@ import { GrafanaPromRuleGroupDTO, PromRuleGroupDTO } from 'app/types/unified-ale
|
||||
|
||||
import { FolderActionsButton } from '../components/folder-actions/FolderActionsButton';
|
||||
import { GrafanaNoRulesCTA } from '../components/rules/NoRulesCTA';
|
||||
import { shouldUseBackendFilters } from '../featureToggles';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
import { groups } from '../utils/navigation';
|
||||
|
||||
@@ -18,7 +17,7 @@ import { ListGroup } from './components/ListGroup';
|
||||
import { ListSection } from './components/ListSection';
|
||||
import { LoadMoreButton } from './components/LoadMoreButton';
|
||||
import { NoRulesFound } from './components/NoRulesFound';
|
||||
import { getGrafanaFilter } from './hooks/grafanaFilter';
|
||||
import { getGrafanaFilter, hasGrafanaClientSideFilters } from './hooks/grafanaFilter';
|
||||
import { toIndividualRuleGroups, useGrafanaGroupsGenerator } from './hooks/prometheusGroupsGenerator';
|
||||
import { useLazyLoadPrometheusGroups } from './hooks/useLazyLoadPrometheusGroups';
|
||||
import { FRONTED_GROUPED_PAGE_SIZE, getApiGroupPageSize } from './paginationLimits';
|
||||
@@ -36,25 +35,27 @@ export function PaginatedGrafanaLoader({ groupFilter, namespaceFilter }: LoaderP
|
||||
}
|
||||
|
||||
function PaginatedGroupsLoader({ groupFilter, namespaceFilter }: LoaderProps) {
|
||||
const useBackendFilters = shouldUseBackendFilters();
|
||||
|
||||
// When backend filters are enabled, groupFilter is handled on the backend
|
||||
const hasFilters = useBackendFilters ? Boolean(namespaceFilter) : Boolean(groupFilter || namespaceFilter);
|
||||
const filterState = { namespace: namespaceFilter, groupName: groupFilter };
|
||||
const { backendFilter } = getGrafanaFilter(filterState);
|
||||
|
||||
const hasFilters = Boolean(groupFilter || namespaceFilter);
|
||||
const needsClientSideFiltering = hasGrafanaClientSideFilters(filterState);
|
||||
|
||||
// If there are filters, we don't want to populate the cache to avoid performance issues
|
||||
// Filtering may trigger multiple HTTP requests, which would populate the cache with a lot of groups hurting performance
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({
|
||||
populateCache: hasFilters ? false : true,
|
||||
populateCache: needsClientSideFiltering ? false : true,
|
||||
limitAlerts: 0,
|
||||
});
|
||||
|
||||
// If there are no filters we can match one frontend page to one API page.
|
||||
// However, if there are filters, we need to fetch more groups from the API to populate one frontend page
|
||||
const apiGroupPageSize = getApiGroupPageSize(hasFilters);
|
||||
const apiGroupPageSize = getApiGroupPageSize(needsClientSideFiltering);
|
||||
|
||||
const searchGroupName = useBackendFilters ? groupFilter : undefined;
|
||||
|
||||
const groupsGenerator = useRef(toIndividualRuleGroups(grafanaGroupsGenerator(apiGroupPageSize, { searchGroupName })));
|
||||
const groupsGenerator = useRef(
|
||||
toIndividualRuleGroups(grafanaGroupsGenerator({ groupLimit: apiGroupPageSize }, backendFilter))
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const currentGenerator = groupsGenerator.current;
|
||||
|
||||
@@ -22,6 +22,27 @@ import {
|
||||
ruleTypeFilter,
|
||||
} from './filterPredicates';
|
||||
|
||||
/**
|
||||
* Determines if client-side filtering is needed for data source-managed rules.
|
||||
*/
|
||||
export function hasDatasourceClientSideFilters(filterState: Partial<RulesFilter>): boolean {
|
||||
// Check if any filter that applies to datasource rules is active
|
||||
return (
|
||||
(filterState.freeFormWords && filterState.freeFormWords.length > 0) ||
|
||||
Boolean(filterState.ruleName) ||
|
||||
Boolean(filterState.ruleState) ||
|
||||
Boolean(filterState.ruleType) ||
|
||||
(filterState.dataSourceNames && filterState.dataSourceNames.length > 0) ||
|
||||
(filterState.labels && filterState.labels.length > 0) ||
|
||||
Boolean(filterState.ruleHealth) ||
|
||||
Boolean(filterState.dashboardUid) ||
|
||||
Boolean(filterState.plugins) ||
|
||||
Boolean(filterState.contactPoint) ||
|
||||
Boolean(filterState.namespace) ||
|
||||
Boolean(filterState.groupName)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds filter configurations for data source-managed alert rules.
|
||||
*
|
||||
|
||||
@@ -32,12 +32,14 @@ export function buildTitleSearch(filterState: RulesFilter): string | undefined {
|
||||
* Normalize filter state for case-insensitive matching
|
||||
* Lowercase free form words, rule name, group name and namespace
|
||||
*/
|
||||
export function normalizeFilterState(filterState: RulesFilter): RulesFilter {
|
||||
export function normalizeFilterState(filterState: Partial<RulesFilter>): RulesFilter {
|
||||
return {
|
||||
...filterState,
|
||||
freeFormWords: filterState.freeFormWords.map((word) => word.toLowerCase()),
|
||||
freeFormWords: filterState.freeFormWords?.map((word) => word.toLowerCase()) ?? [],
|
||||
ruleName: filterState.ruleName?.toLowerCase(),
|
||||
groupName: filterState.groupName?.toLowerCase(),
|
||||
namespace: filterState.namespace?.toLowerCase(),
|
||||
dataSourceNames: filterState.dataSourceNames ?? [],
|
||||
labels: filterState.labels ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Annotation } from '../../utils/constants';
|
||||
import { getDatasourceAPIUid } from '../../utils/datasource';
|
||||
import { getFilter } from '../../utils/search';
|
||||
|
||||
import { getGrafanaFilter, hasClientSideFilters } from './grafanaFilter';
|
||||
import { getGrafanaFilter, hasGrafanaClientSideFilters } from './grafanaFilter';
|
||||
|
||||
jest.mock('../../utils/datasource');
|
||||
|
||||
@@ -670,41 +670,41 @@ describe('grafana-managed rules', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasClientSideFilters', () => {
|
||||
describe('hasGrafanaClientSideFilters', () => {
|
||||
describe('when alertingUIUseBackendFilters is disabled', () => {
|
||||
testWithFeatureToggles({ disable: ['alertingUIUseBackendFilters'] });
|
||||
|
||||
it('should return false when no filters are applied', () => {
|
||||
expect(hasClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for title-related filters (freeFormWords, ruleName)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for ruleType filter', () => {
|
||||
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for dashboardUid filter', () => {
|
||||
expect(hasClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for groupName filter', () => {
|
||||
expect(hasClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for client-side only filters', () => {
|
||||
expect(hasClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for backend-only filters (state, health, contactPoint)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -712,36 +712,36 @@ describe('grafana-managed rules', () => {
|
||||
testWithFeatureToggles({ enable: ['alertingUIUseBackendFilters'] });
|
||||
|
||||
it('should return false when no filters are applied', () => {
|
||||
expect(hasClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({}))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for title-related filters (handled by backend)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for ruleType filter (handled by backend)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for dashboardUid filter (handled by backend)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for groupName filter (handled by backend)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for client-side only filters', () => {
|
||||
expect(hasClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for backend-only filters (state, health, contactPoint)', () => {
|
||||
expect(hasClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -750,20 +750,20 @@ describe('grafana-managed rules', () => {
|
||||
|
||||
it('should return correct values for all filter types', () => {
|
||||
// Should return false for: empty, backend-handled (ruleType, dashboardUid), and backend-only filters
|
||||
expect(hasClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
|
||||
// Should return true for: frontend-handled filters
|
||||
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -772,20 +772,20 @@ describe('grafana-managed rules', () => {
|
||||
|
||||
it('should return correct values for all filter types', () => {
|
||||
// Should return false for: empty, all backend-handled filters, and backend-only filters
|
||||
expect(hasClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ ruleHealth: RuleHealth.Ok }))).toBe(false);
|
||||
expect(hasClientSideFilters(getFilter({ contactPoint: 'my-contact-point' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({}))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ freeFormWords: ['cpu'] }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleName: 'alert' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleType: PromRuleType.Alerting }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dashboardUid: 'test-dashboard' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ groupName: 'test-group' }))).toBe(false);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ ruleState: PromAlertingRuleState.Firing }))).toBe(false);
|
||||
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
|
||||
expect(hasClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ namespace: 'production' }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ dataSourceNames: ['prometheus'] }))).toBe(true);
|
||||
expect(hasGrafanaClientSideFilters(getFilter({ labels: ['severity=critical'] }))).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,26 +24,26 @@ import {
|
||||
/**
|
||||
* Determines if client-side filtering is needed for Grafana-managed rules.
|
||||
*/
|
||||
export function hasClientSideFilters(filterState: RulesFilter): boolean {
|
||||
export function hasGrafanaClientSideFilters(filterState: Partial<RulesFilter>): boolean {
|
||||
const { ruleFilterConfig, groupFilterConfig } = buildGrafanaFilterConfigs();
|
||||
|
||||
// Check each rule filter: if the config has a non-null handler AND the filter state has a value, we need client-side filtering
|
||||
const hasActiveRuleFilters =
|
||||
(ruleFilterConfig.freeFormWords !== null && filterState.freeFormWords.length > 0) ||
|
||||
(ruleFilterConfig.ruleName !== null && Boolean(filterState.ruleName)) ||
|
||||
(ruleFilterConfig.ruleState !== null && Boolean(filterState.ruleState)) ||
|
||||
(ruleFilterConfig.ruleType !== null && Boolean(filterState.ruleType)) ||
|
||||
(ruleFilterConfig.dataSourceNames !== null && filterState.dataSourceNames.length > 0) ||
|
||||
(ruleFilterConfig.labels !== null && filterState.labels.length > 0) ||
|
||||
(ruleFilterConfig.ruleHealth !== null && Boolean(filterState.ruleHealth)) ||
|
||||
(ruleFilterConfig.dashboardUid !== null && Boolean(filterState.dashboardUid)) ||
|
||||
(ruleFilterConfig.plugins !== null && Boolean(filterState.plugins)) ||
|
||||
(ruleFilterConfig.contactPoint !== null && Boolean(filterState.contactPoint));
|
||||
(ruleFilterConfig.freeFormWords !== null && Boolean(filterState?.freeFormWords?.length)) ||
|
||||
(ruleFilterConfig.ruleName !== null && Boolean(filterState?.ruleName)) ||
|
||||
(ruleFilterConfig.ruleState !== null && Boolean(filterState?.ruleState)) ||
|
||||
(ruleFilterConfig.ruleType !== null && Boolean(filterState?.ruleType)) ||
|
||||
(ruleFilterConfig.dataSourceNames !== null && Boolean(filterState?.dataSourceNames?.length)) ||
|
||||
(ruleFilterConfig.labels !== null && Boolean(filterState?.labels?.length)) ||
|
||||
(ruleFilterConfig.ruleHealth !== null && Boolean(filterState?.ruleHealth)) ||
|
||||
(ruleFilterConfig.dashboardUid !== null && Boolean(filterState?.dashboardUid)) ||
|
||||
(ruleFilterConfig.plugins !== null && Boolean(filterState?.plugins)) ||
|
||||
(ruleFilterConfig.contactPoint !== null && Boolean(filterState?.contactPoint));
|
||||
|
||||
// Check each group filter: if the config has a non-null handler AND the filter state has a value, we need client-side filtering
|
||||
const hasActiveGroupFilters =
|
||||
(groupFilterConfig.namespace !== null && Boolean(filterState.namespace)) ||
|
||||
(groupFilterConfig.groupName !== null && Boolean(filterState.groupName));
|
||||
(groupFilterConfig.namespace !== null && Boolean(filterState?.namespace)) ||
|
||||
(groupFilterConfig.groupName !== null && Boolean(filterState?.groupName));
|
||||
|
||||
return hasActiveRuleFilters || hasActiveGroupFilters;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export function hasClientSideFilters(filterState: RulesFilter): boolean {
|
||||
* The backend filter is used for server-side filtering when `shouldUseBackendFilters()` is enabled,
|
||||
* while the frontend filter provides client-side matching functions for rules and groups.
|
||||
*/
|
||||
export function getGrafanaFilter(filterState: RulesFilter) {
|
||||
export function getGrafanaFilter(filterState: Partial<RulesFilter>) {
|
||||
const normalizedFilterState = normalizeFilterState(filterState);
|
||||
|
||||
const { ruleFilterConfig, groupFilterConfig } = buildGrafanaFilterConfigs();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
import { MergeExclusive } from 'type-fest';
|
||||
|
||||
import { DataSourceRulesSourceIdentifier, RuleHealth } from 'app/types/unified-alerting';
|
||||
import { PromAlertingRuleState, PromRuleGroupDTO } from 'app/types/unified-alerting-dto';
|
||||
@@ -16,27 +17,23 @@ interface UseGeneratorHookOptions {
|
||||
limitAlerts?: number;
|
||||
}
|
||||
|
||||
interface FetchGroupsOptions {
|
||||
groupLimit?: number;
|
||||
groupNextToken?: string;
|
||||
}
|
||||
|
||||
export function usePrometheusGroupsGenerator() {
|
||||
const [getGroups] = useLazyGetGroupsQuery();
|
||||
|
||||
return useCallback(
|
||||
async function* (ruleSource: DataSourceRulesSourceIdentifier, groupLimit: number) {
|
||||
const getRuleSourceGroupsWithCache = async (fetchOptions: FetchGroupsOptions) => {
|
||||
const getRuleSourceGroupsWithCache = async (fetchOptions: GroupsNextPageOptions) => {
|
||||
const response = await getGroups({
|
||||
ruleSource: { uid: ruleSource.uid },
|
||||
notificationOptions: { showErrorAlert: false },
|
||||
groupLimit,
|
||||
...fetchOptions,
|
||||
}).unwrap();
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
yield* genericGroupsGenerator(getRuleSourceGroupsWithCache, groupLimit);
|
||||
yield* genericGroupsGenerator(getRuleSourceGroupsWithCache);
|
||||
},
|
||||
[getGroups]
|
||||
);
|
||||
@@ -52,8 +49,21 @@ interface GrafanaPromApiFilter {
|
||||
dashboardUid?: string;
|
||||
}
|
||||
|
||||
interface GrafanaFetchGroupsOptions extends FetchGroupsOptions {
|
||||
interface GrafanaFetchGroupsOptions extends GroupsNextPageOptions {
|
||||
filter?: GrafanaPromApiFilter;
|
||||
groupLimit?: number;
|
||||
// Limits the number of total rules returned across all groups
|
||||
// Rounds up to full groups, so the response may contain more rules than the group limit
|
||||
ruleLimit?: number;
|
||||
}
|
||||
|
||||
export type GrafanaFetchGroupsLimit = MergeExclusive<{ groupLimit: number }, { ruleLimit: number }>;
|
||||
|
||||
export type DataSourceFetchGroupsLimit = { groupLimit: number };
|
||||
|
||||
export interface FetchGroupsLimitOptions {
|
||||
grafanaManagedLimit: GrafanaFetchGroupsLimit;
|
||||
datasourceManagedLimit: DataSourceFetchGroupsLimit;
|
||||
}
|
||||
|
||||
export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions = {}) {
|
||||
@@ -78,11 +88,16 @@ export function useGrafanaGroupsGenerator(hookOptions: UseGeneratorHookOptions =
|
||||
);
|
||||
|
||||
return useCallback(
|
||||
async function* (groupLimit: number, filter?: GrafanaPromApiFilter) {
|
||||
yield* genericGroupsGenerator(
|
||||
(fetchOptions) => getGroupsAndProvideCache({ ...fetchOptions, filter }),
|
||||
groupLimit
|
||||
);
|
||||
async function* (limit: GrafanaFetchGroupsLimit, filter?: GrafanaPromApiFilter) {
|
||||
const fetchGroups = (fetchOptions: GroupsNextPageOptions) =>
|
||||
getGroupsAndProvideCache({
|
||||
...fetchOptions,
|
||||
filter,
|
||||
groupLimit: 'groupLimit' in limit ? limit.groupLimit : undefined,
|
||||
ruleLimit: 'ruleLimit' in limit ? limit.ruleLimit : undefined,
|
||||
});
|
||||
|
||||
yield* genericGroupsGenerator(fetchGroups);
|
||||
},
|
||||
[getGroupsAndProvideCache]
|
||||
);
|
||||
@@ -105,21 +120,24 @@ export function toIndividualRuleGroups<TGroup extends PromRuleGroupDTO>(
|
||||
})();
|
||||
}
|
||||
|
||||
interface GroupsNextPageOptions {
|
||||
groupNextToken?: string;
|
||||
}
|
||||
|
||||
// Generator lazily provides groups one by one only when needed
|
||||
// This might look a bit complex but it allows us to have one API for paginated and non-paginated Prometheus data sources
|
||||
// For unpaginated data sources we fetch everything in one go
|
||||
// For paginated we fetch the next page when needed
|
||||
async function* genericGroupsGenerator<TGroup>(
|
||||
fetchGroups: (options: FetchGroupsOptions) => Promise<PromRulesResponse<TGroup>>,
|
||||
groupLimit: number
|
||||
fetchGroups: (options: GroupsNextPageOptions) => Promise<PromRulesResponse<TGroup>>
|
||||
) {
|
||||
let response = await fetchGroups({ groupLimit });
|
||||
let response = await fetchGroups({ groupNextToken: undefined });
|
||||
yield response.data.groups;
|
||||
|
||||
let lastToken: string | undefined = response.data?.groupNextToken;
|
||||
|
||||
while (lastToken) {
|
||||
response = await fetchGroups({ groupNextToken: lastToken, groupLimit: groupLimit });
|
||||
response = await fetchGroups({ groupNextToken: lastToken });
|
||||
yield response.data.groups;
|
||||
lastToken = response.data?.groupNextToken;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,11 @@ import { RulePositionHash, createRulePositionHash } from '../rulePositionHash';
|
||||
|
||||
import { getDatasourceFilter } from './datasourceFilter';
|
||||
import { getGrafanaFilter } from './grafanaFilter';
|
||||
import { useGrafanaGroupsGenerator, usePrometheusGroupsGenerator } from './prometheusGroupsGenerator';
|
||||
import {
|
||||
FetchGroupsLimitOptions,
|
||||
useGrafanaGroupsGenerator,
|
||||
usePrometheusGroupsGenerator,
|
||||
} from './prometheusGroupsGenerator';
|
||||
|
||||
export type RuleWithOrigin = PromRuleWithOrigin | GrafanaRuleWithOrigin;
|
||||
|
||||
@@ -74,7 +78,7 @@ export function useFilteredRulesIteratorProvider() {
|
||||
const prometheusGroupsGenerator = usePrometheusGroupsGenerator();
|
||||
const grafanaGroupsGenerator = useGrafanaGroupsGenerator({ limitAlerts: 0 });
|
||||
|
||||
const getFilteredRulesIterable = (filterState: RulesFilter, groupLimit: number): GetIteratorResult => {
|
||||
const getFilteredRulesIterable = (filterState: RulesFilter, options: FetchGroupsLimitOptions): GetIteratorResult => {
|
||||
/* this is the abort controller that allows us to stop an AsyncIterable */
|
||||
const abortController = new AbortController();
|
||||
|
||||
@@ -83,7 +87,7 @@ export function useFilteredRulesIteratorProvider() {
|
||||
const { backendFilter, frontendFilter } = getGrafanaFilter(filterState);
|
||||
|
||||
const grafanaRulesGenerator: AsyncIterableX<RuleWithOrigin> = from(
|
||||
grafanaGroupsGenerator(groupLimit, backendFilter)
|
||||
grafanaGroupsGenerator(options.grafanaManagedLimit, backendFilter)
|
||||
).pipe(
|
||||
withAbort(abortController.signal),
|
||||
concatMap((groups) =>
|
||||
@@ -110,7 +114,7 @@ export function useFilteredRulesIteratorProvider() {
|
||||
const dataSourceGenerators: Array<AsyncIterableX<RuleWithOrigin>> = externalRulesSourcesToFetchFrom.map(
|
||||
(dataSourceIdentifier) => {
|
||||
const promGroupsGenerator: AsyncIterableX<RuleWithOrigin> = from(
|
||||
prometheusGroupsGenerator(dataSourceIdentifier, groupLimit)
|
||||
prometheusGroupsGenerator(dataSourceIdentifier, options.datasourceManagedLimit.groupLimit)
|
||||
).pipe(
|
||||
withAbort(abortController.signal),
|
||||
concatMap((groups) =>
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import { testWithFeatureToggles } from 'test/test-utils';
|
||||
|
||||
import { PromAlertingRuleState, PromRuleType } from 'app/types/unified-alerting-dto';
|
||||
|
||||
import { RuleHealth, RulesFilter } from '../search/rulesSearchParser';
|
||||
import { getFilter } from '../utils/search';
|
||||
|
||||
import {
|
||||
FILTERED_GROUPS_LARGE_API_PAGE_SIZE,
|
||||
FILTERED_GROUPS_SMALL_API_PAGE_SIZE,
|
||||
RULE_LIMIT_WITH_BACKEND_FILTERS,
|
||||
getFilteredRulesLimits,
|
||||
} from './paginationLimits';
|
||||
|
||||
describe('paginationLimits', () => {
|
||||
describe('getFilteredRulesLimits', () => {
|
||||
describe('when backend filters are disabled', () => {
|
||||
testWithFeatureToggles({ disable: ['alertingUIUseBackendFilters', 'alertingUIUseFullyCompatBackendFilters'] });
|
||||
|
||||
it('should return small limits when no filters are applied', () => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter({}));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
});
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ ruleState: PromAlertingRuleState.Firing },
|
||||
{ ruleHealth: RuleHealth.Ok },
|
||||
{ contactPoint: 'slack' },
|
||||
])('should return small grafana limit + large datasource limit for backend-only filter: %p', (filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
});
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ freeFormWords: ['cpu'] },
|
||||
{ ruleName: 'alert' },
|
||||
{ ruleType: PromRuleType.Alerting },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
{ dashboardUid: 'test-dashboard' },
|
||||
{ plugins: 'hide' as const },
|
||||
{ namespace: 'production' },
|
||||
{ groupName: 'test-group' },
|
||||
{ namespace: 'production', freeFormWords: ['cpu'] },
|
||||
])('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 alertingUIUseBackendFilters is enabled', () => {
|
||||
testWithFeatureToggles({ enable: ['alertingUIUseBackendFilters'] });
|
||||
|
||||
it('should return rule limit for grafana + default limit for datasource when no filters are applied', () => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter({}));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
});
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ freeFormWords: ['cpu'] },
|
||||
{ ruleName: 'alert' },
|
||||
{ ruleType: PromRuleType.Alerting },
|
||||
{ dashboardUid: 'test-dashboard' },
|
||||
{ groupName: 'test-group' },
|
||||
{ ruleState: PromAlertingRuleState.Firing },
|
||||
{ ruleHealth: RuleHealth.Ok },
|
||||
{ contactPoint: 'slack' },
|
||||
])(
|
||||
'should return rule limit for grafana + large limit for datasource when only backend filters are used: %p',
|
||||
(filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ namespace: 'production' },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
{ 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', () => {
|
||||
testWithFeatureToggles({ enable: ['alertingUIUseFullyCompatBackendFilters'] });
|
||||
|
||||
it('should return rule limit for grafana + default limit for datasource when no filters are applied', () => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter({}));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
});
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ ruleType: PromRuleType.Alerting },
|
||||
{ dashboardUid: 'test-dashboard' },
|
||||
{ ruleState: PromAlertingRuleState.Firing },
|
||||
{ ruleHealth: RuleHealth.Ok },
|
||||
{ contactPoint: 'slack' },
|
||||
])(
|
||||
'should return rule limit for grafana + large limit for datasource when only backend filters are used: %p',
|
||||
(filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ freeFormWords: ['cpu'] },
|
||||
{ ruleName: 'alert' },
|
||||
{ groupName: 'test-group' },
|
||||
{ namespace: 'production' },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
])('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 both backend filter toggles are enabled', () => {
|
||||
testWithFeatureToggles({ enable: ['alertingUIUseBackendFilters', 'alertingUIUseFullyCompatBackendFilters'] });
|
||||
|
||||
it('should return rule limit for grafana + default limit for datasource when no filters are applied', () => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter({}));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_SMALL_API_PAGE_SIZE });
|
||||
});
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ freeFormWords: ['cpu'] },
|
||||
{ ruleName: 'alert' },
|
||||
{ ruleType: PromRuleType.Alerting },
|
||||
{ dashboardUid: 'test-dashboard' },
|
||||
{ groupName: 'test-group' },
|
||||
{ ruleState: PromAlertingRuleState.Firing },
|
||||
{ ruleHealth: RuleHealth.Ok },
|
||||
{ contactPoint: 'slack' },
|
||||
])(
|
||||
'should return rule limit for grafana + large limit for datasource when only backend filters are used: %p',
|
||||
(filterState) => {
|
||||
const { grafanaManagedLimit, datasourceManagedLimit } = getFilteredRulesLimits(getFilter(filterState));
|
||||
|
||||
expect(grafanaManagedLimit).toEqual({ ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS });
|
||||
expect(datasourceManagedLimit).toEqual({ groupLimit: FILTERED_GROUPS_LARGE_API_PAGE_SIZE });
|
||||
}
|
||||
);
|
||||
|
||||
it.each<Partial<RulesFilter>>([
|
||||
{ namespace: 'production' },
|
||||
{ dataSourceNames: ['prometheus'] },
|
||||
{ labels: ['severity=critical'] },
|
||||
])('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 });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,10 @@
|
||||
import { shouldUseBackendFilters, shouldUseFullyCompatibleBackendFilters } from '../featureToggles';
|
||||
import { RulesFilter } from '../search/rulesSearchParser';
|
||||
|
||||
import { hasDatasourceClientSideFilters } from './hooks/datasourceFilter';
|
||||
import { hasGrafanaClientSideFilters } from './hooks/grafanaFilter';
|
||||
import { FetchGroupsLimitOptions } from './hooks/prometheusGroupsGenerator';
|
||||
|
||||
export const FRONTEND_LIST_PAGE_SIZE = 100;
|
||||
|
||||
export const FILTERED_GROUPS_LARGE_API_PAGE_SIZE = 2000;
|
||||
@@ -6,6 +13,8 @@ export const FILTERED_GROUPS_SMALL_API_PAGE_SIZE = 100;
|
||||
export const DEFAULT_GROUPS_API_PAGE_SIZE = 40;
|
||||
export const FRONTED_GROUPED_PAGE_SIZE = DEFAULT_GROUPS_API_PAGE_SIZE;
|
||||
|
||||
export const RULE_LIMIT_WITH_BACKEND_FILTERS = 100;
|
||||
|
||||
export function getApiGroupPageSize(hasFilters: boolean) {
|
||||
return hasFilters ? FILTERED_GROUPS_LARGE_API_PAGE_SIZE : DEFAULT_GROUPS_API_PAGE_SIZE;
|
||||
}
|
||||
@@ -13,3 +22,27 @@ export function getApiGroupPageSize(hasFilters: boolean) {
|
||||
export function getSearchApiGroupPageSize(hasFrontendFilters: boolean) {
|
||||
return hasFrontendFilters ? FILTERED_GROUPS_LARGE_API_PAGE_SIZE : FILTERED_GROUPS_SMALL_API_PAGE_SIZE;
|
||||
}
|
||||
|
||||
export function getFilteredRulesLimits(filterState: RulesFilter): FetchGroupsLimitOptions {
|
||||
return {
|
||||
grafanaManagedLimit: getGrafanaFilterLimits(filterState),
|
||||
datasourceManagedLimit: {
|
||||
groupLimit: hasDatasourceClientSideFilters(filterState)
|
||||
? FILTERED_GROUPS_LARGE_API_PAGE_SIZE
|
||||
: FILTERED_GROUPS_SMALL_API_PAGE_SIZE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getGrafanaFilterLimits(filterState: RulesFilter) {
|
||||
const backendFiltersEnabled = shouldUseFullyCompatibleBackendFilters() || shouldUseBackendFilters();
|
||||
|
||||
const frontendFiltersInUse = hasGrafanaClientSideFilters(filterState);
|
||||
const onlyBackendFiltersInUse = frontendFiltersInUse === false;
|
||||
|
||||
if (backendFiltersEnabled && onlyBackendFiltersInUse) {
|
||||
return { ruleLimit: RULE_LIMIT_WITH_BACKEND_FILTERS };
|
||||
}
|
||||
|
||||
return { groupLimit: getSearchApiGroupPageSize(frontendFiltersInUse) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user