diff --git a/public/app/features/alerting/unified/hooks/useFilteredRules.test.ts b/public/app/features/alerting/unified/hooks/useFilteredRules.test.ts index db277cea39d..f6bbe0164f8 100644 --- a/public/app/features/alerting/unified/hooks/useFilteredRules.test.ts +++ b/public/app/features/alerting/unified/hooks/useFilteredRules.test.ts @@ -273,9 +273,25 @@ describe('filterRules', function () { const ruleQuery = '[alongnameinthefirstgroup][thishas spaces][somethingelse]'; const namespaceQuery = 'foo|bar'; const groupQuery = 'some|group'; + const freeForm = '.+'; - const performFilter = () => - filterRules([ns], getFilter({ groupName: groupQuery, ruleName: ruleQuery, namespace: namespaceQuery })); - expect(performFilter).not.toThrow(); + expect(() => + filterRules( + [ns], + getFilter({ groupName: groupQuery, ruleName: ruleQuery, namespace: namespaceQuery, freeFormWords: [freeForm] }) + ) + ).not.toThrow(); + }); + + // these test may same to be the same as the one above but it tests different edge-cases + it('does not crash with other regex values', () => { + const rules = [mockCombinedRule({ name: 'rule' })]; + + const ns = mockCombinedRuleNamespace({ + name: 'namespace', + groups: [mockCombinedRuleGroup('group', rules)], + }); + + expect(() => filterRules([ns], getFilter({ freeFormWords: ['.+'] }))).not.toThrow(); }); }); diff --git a/public/app/features/alerting/unified/hooks/useFilteredRules.ts b/public/app/features/alerting/unified/hooks/useFilteredRules.ts index f7674c92500..d515a79ab89 100644 --- a/public/app/features/alerting/unified/hooks/useFilteredRules.ts +++ b/public/app/features/alerting/unified/hooks/useFilteredRules.ts @@ -8,6 +8,7 @@ import { Matcher } from 'app/plugins/datasource/alertmanager/types'; import { CombinedRuleGroup, CombinedRuleNamespace, Rule } from 'app/types/unified-alerting'; import { isPromAlertingRuleState, PromRuleType, RulerGrafanaRuleDTO } from 'app/types/unified-alerting-dto'; +import { logError } from '../Analytics'; import { applySearchFilterToQuery, getSearchFilterFromQuery, RulesFilter } from '../search/rulesSearchParser'; import { labelsMatchMatchers, matcherToMatcherField } from '../utils/alertmanager'; import { Annotation } from '../utils/constants'; @@ -28,6 +29,8 @@ import { useURLSearchParams } from './useURLSearchParams'; const MAX_NEEDLE_SIZE = 25; const INFO_THRESHOLD = Infinity; +const SEARCH_FAILED_ERR = new Error('Failed to search rules'); + /** * Escape query strings so that regex characters don't interfere * with uFuzzy search methods. @@ -160,7 +163,20 @@ export const filterRules = ( } // If a namespace and group have rules that match the rules filters then keep them. - return filteredNamespaces.reduce(reduceNamespaces(filterState), []); + const filteredRuleNamespaces: CombinedRuleNamespace[] = []; + + try { + const matches = filteredNamespaces.reduce(reduceNamespaces(filterState), []); + matches.forEach((match) => { + filteredRuleNamespaces.push(match); + }); + } catch { + logError(SEARCH_FAILED_ERR, { + search: JSON.stringify(filterState), + }); + } + + return filteredRuleNamespaces; }; const reduceNamespaces = (filterState: RulesFilter) => {