diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx index 07bf3516fbdef..1a5e3f278eb5e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.test.tsx @@ -45,8 +45,7 @@ describe('useBulkEditSelectTest', () => { useBulkEditSelect({ items, totalItemCount: 4, - tagsFilter: ['test: 123'], - searchText: 'rules*', + filters: { tags: ['test: 123'], searchText: 'rules*' }, }) ); @@ -58,8 +57,7 @@ describe('useBulkEditSelectTest', () => { useBulkEditSelect({ items, totalItemCount: 4, - tagsFilter: ['test: 123'], - searchText: 'rules*', + filters: { tags: ['test: 123'], searchText: 'rules*' }, }) ); @@ -107,8 +105,7 @@ describe('useBulkEditSelectTest', () => { useBulkEditSelect({ items, totalItemCount: 4, - tagsFilter: ['test: 123'], - searchText: 'rules*', + filters: { tags: ['test: 123'], searchText: 'rules*' }, }) ); @@ -124,8 +121,10 @@ describe('useBulkEditSelectTest', () => { useBulkEditSelect({ items, totalItemCount: 4, - tagsFilter: ['test: 123'], - searchText: 'rules*', + filters: { + tags: ['test: 123'], + searchText: 'rules*', + }, }) ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx index 84e762cbe93f8..fe70b4fa0e3bc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/hooks/use_bulk_edit_select.tsx @@ -7,7 +7,7 @@ import { useReducer, useMemo, useCallback } from 'react'; import { fromKueryExpression, nodeBuilder } from '@kbn/es-query'; import { mapFiltersToKueryNode } from '../lib/rule_api/map_filters_to_kuery_node'; -import { RuleTableItem, RuleStatus } from '../../types'; +import { RuleTableItem, RulesListFilters } from '../../types'; interface BulkEditSelectionState { selectedIds: Set; @@ -73,27 +73,11 @@ const reducer = (state: BulkEditSelectionState, action: Action) => { interface UseBulkEditSelectProps { totalItemCount: number; items: RuleTableItem[]; - typesFilter?: string[]; - actionTypesFilter?: string[]; - tagsFilter?: string[]; - ruleExecutionStatusesFilter?: string[]; - ruleLastRunOutcomesFilter?: string[]; - ruleStatusesFilter?: RuleStatus[]; - searchText?: string; + filters?: RulesListFilters; } export function useBulkEditSelect(props: UseBulkEditSelectProps) { - const { - totalItemCount = 0, - items = [], - typesFilter, - actionTypesFilter, - tagsFilter, - ruleExecutionStatusesFilter, - ruleLastRunOutcomesFilter, - ruleStatusesFilter, - searchText, - } = props; + const { totalItemCount = 0, items = [], filters } = props; const [state, dispatch] = useReducer(reducer, { ...initialState, @@ -187,15 +171,20 @@ export function useBulkEditSelect(props: UseBulkEditSelectProps) { const getFilterKueryNode = useCallback( (idsToExclude?: string[]) => { - const ruleFilterKueryNode = mapFiltersToKueryNode({ - typesFilter, - actionTypesFilter, - tagsFilter, - ruleExecutionStatusesFilter, - ruleLastRunOutcomesFilter, - ruleStatusesFilter, - searchText, - }); + const ruleFilterKueryNode = mapFiltersToKueryNode( + filters + ? { + typesFilter: filters.types, + actionTypesFilter: filters.actionTypes, + tagsFilter: filters.tags, + ruleExecutionStatusesFilter: filters.ruleExecutionStatuses, + ruleLastRunOutcomesFilter: filters.ruleLastRunOutcomes, + ruleParamsFilter: filters.ruleParams, + ruleStatusesFilter: filters.ruleStatuses, + searchText: filters.searchText, + } + : {} + ); if (idsToExclude && idsToExclude.length) { const excludeFilter = fromKueryExpression( @@ -209,15 +198,7 @@ export function useBulkEditSelect(props: UseBulkEditSelectProps) { return ruleFilterKueryNode; }, - [ - typesFilter, - actionTypesFilter, - tagsFilter, - ruleExecutionStatusesFilter, - ruleLastRunOutcomesFilter, - ruleStatusesFilter, - searchText, - ] + [filters] ); const getFilter = useCallback(() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 67d475ae2689e..eb82c6c14583f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -363,8 +363,7 @@ export const RulesList = ({ } = useBulkEditSelect({ totalItemCount: rulesState.totalItemCount, items: tableItems, - ...filters, - typesFilter: rulesTypesFilter, + filters: { ...filters, types: rulesTypesFilter }, }); const handleUpdateFiltersEffect = useCallback( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx index 038b79bded049..8181815478926 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_filters_bar.tsx @@ -81,7 +81,7 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) isGrouped refresh={refresh} canLoadRules={canLoadRules} - selectedTags={filters.tags} + selectedTags={filters.tags!} onChange={(value) => updateFilters({ filter: 'tags', value })} />, ]; @@ -93,7 +93,7 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) if (isRuleStatusFilterEnabled) { return ( updateFilters({ filter: 'ruleStatuses', value })} /> ); @@ -106,7 +106,7 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) return [ updateFilters({ filter: 'ruleExecutionStatuses', value })} />, ]; @@ -114,7 +114,7 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) return [ updateFilters({ filter: 'ruleLastRunOutcomes', value })} />, ]; @@ -124,14 +124,14 @@ export const RulesListFiltersBar = React.memo((props: RulesListFiltersBarProps) updateFilters({ filter: 'types', value })} />, showActionFilter && ( updateFilters({ filter: 'actionTypes', value })} /> ), diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 655a3e6de8662..dece3f172c55a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -782,14 +782,14 @@ export interface ConnectorServices { } export interface RulesListFilters { - actionTypes: string[]; - ruleExecutionStatuses: string[]; - ruleLastRunOutcomes: string[]; - ruleParams: Record; - ruleStatuses: RuleStatus[]; - searchText: string; - tags: string[]; - types: string[]; + actionTypes?: string[]; + ruleExecutionStatuses?: string[]; + ruleLastRunOutcomes?: string[]; + ruleParams?: Record; + ruleStatuses?: RuleStatus[]; + searchText?: string; + tags?: string[]; + types?: string[]; kueryNode?: KueryNode; } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts index 617429f3a23c9..df754be0506b2 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/rules_list/bulk_actions.ts @@ -14,7 +14,12 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { createAlert, scheduleRule, snoozeAlert } from '../../../lib/alert_api_actions'; +import { + createAlert, + createAlertManualCleanup, + scheduleRule, + snoozeAlert, +} from '../../../lib/alert_api_actions'; import { ObjectRemover } from '../../../lib/object_remover'; export default ({ getPageObjects, getService }: FtrProviderContext) => { @@ -29,7 +34,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('rulesTab'); } - describe('rules list', () => { + describe('rules list bulk actions', () => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('rulesTab'); @@ -205,5 +210,81 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(toastTitle).to.eql('Updated API key for 1 rule.'); }); }); + + it('should apply filters to bulk actions when using the select all button', async () => { + const rule1 = await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'a' }, + }); + const rule2 = await createAlertManualCleanup({ + supertest, + overwrites: { name: 'b', rule_type_id: 'test.always-firing' }, + }); + const rule3 = await createAlert({ + supertest, + objectRemover, + overwrites: { name: 'c' }, + }); + + await refreshAlertsList(); + expect(await testSubjects.getVisibleText('totalRulesCount')).to.be('3 rules'); + + await testSubjects.click('ruleTypeFilterButton'); + await testSubjects.existOrFail('ruleTypetest.noopFilterOption'); + await testSubjects.click('ruleTypetest.noopFilterOption'); + await testSubjects.click(`checkboxSelectRow-${rule1.id}`); + await testSubjects.click('selectAllRulesButton'); + + await testSubjects.click('showBulkActionButton'); + await testSubjects.click('bulkDisable'); + + await retry.try(async () => { + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql('Disabled 2 rules'); + }); + + await testSubjects.click('rules-list-clear-filter'); + await refreshAlertsList(); + + await pageObjects.triggersActionsUI.searchAlerts(rule1.name); + expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Disabled'); + await pageObjects.triggersActionsUI.searchAlerts(rule2.name); + expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Enabled'); + await pageObjects.triggersActionsUI.searchAlerts(rule3.name); + expect(await testSubjects.getVisibleText('statusDropdown')).to.be('Disabled'); + + await testSubjects.click('rules-list-clear-filter'); + await refreshAlertsList(); + + await testSubjects.click('ruleStatusFilterButton'); + await testSubjects.existOrFail('ruleStatusFilterOption-enabled'); + await testSubjects.click('ruleStatusFilterOption-enabled'); + await testSubjects.click(`checkboxSelectRow-${rule2.id}`); + await testSubjects.click('selectAllRulesButton'); + + await testSubjects.click('showBulkActionButton'); + await testSubjects.click('bulkDelete'); + await testSubjects.existOrFail('rulesDeleteConfirmation'); + await testSubjects.click('confirmModalConfirmButton'); + + await retry.try(async () => { + const toastTitle = await pageObjects.common.closeToast(); + expect(toastTitle).to.eql('Deleted 1 rule'); + }); + + await testSubjects.click('rules-list-clear-filter'); + await refreshAlertsList(); + + await retry.try( + async () => { + expect(await testSubjects.getVisibleText('totalRulesCount')).to.be('2 rules'); + }, + async () => { + // If the delete fails, make sure rule2 gets cleaned up + objectRemover.add(rule2.id, 'alert', 'alerts'); + } + ); + }); }); };