Skip to content

Commit ef37576

Browse files
kibanamachinedplumleeelasticmachine
authored
[8.x] [Security Solution] Adds enable on install UI workflow to prebuilt rules page (#191529) (#193368)
# Backport This will backport the following commits from `main` to `8.x`: - [[Security Solution] Adds enable on install UI workflow to prebuilt rules page (#191529)](#191529) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Davis Plumlee","email":"56367316+dplumlee@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-09-18T21:56:10Z","message":"[Security Solution] Adds enable on install UI workflow to prebuilt rules page (#191529)\n\n## Summary\r\n\r\nAdds overflow button UI to all prebuilt rules install buttons in order\r\nto enable the rule when it is successfully installed. Previously, a user\r\nwould have to navigate back to the rules page and find the rule(s) they\r\njust installed to enable, this combines those two workflows into a\r\nsingle button action - speeding up the out of the box rule\r\nimplementation.\r\n\r\n### Screenshots\r\n**Prebuilt rules table columns**\r\n<img width=\"530\" alt=\"Screenshot 2024-09-04 at 10 38 05 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/4a009afa-a8f0-4eaa-a76b-8f4e509f35a3\">\r\n\r\n\r\n**Prebuilt rules table bulk install**\r\n<img width=\"1478\" alt=\"Screenshot 2024-09-04 at 10 38 16 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/eb6deb9b-9b4e-4be3-a4ac-0da06d6f1e8e\">\r\n\r\n\r\n**Prebuilt rule details flyout**\r\n<img width=\"1489\" alt=\"Screenshot 2024-09-04 at 10 38 44 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/a4bce22d-7e90-42e4-8522-cf411a297659\">\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [ ] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"3bea483f34e03ea1bbeb836350c650fd06673f10","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:enhancement","v9.0.0","Team:Detections and Resp","Team: SecuritySolution","Team:Detection Rule Management","Feature:Prebuilt Detection Rules","v8.16.0"],"title":"[Security Solution] Adds enable on install UI workflow to prebuilt rules page","number":191529,"url":"https://github.com/elastic/kibana/pull/191529","mergeCommit":{"message":"[Security Solution] Adds enable on install UI workflow to prebuilt rules page (#191529)\n\n## Summary\r\n\r\nAdds overflow button UI to all prebuilt rules install buttons in order\r\nto enable the rule when it is successfully installed. Previously, a user\r\nwould have to navigate back to the rules page and find the rule(s) they\r\njust installed to enable, this combines those two workflows into a\r\nsingle button action - speeding up the out of the box rule\r\nimplementation.\r\n\r\n### Screenshots\r\n**Prebuilt rules table columns**\r\n<img width=\"530\" alt=\"Screenshot 2024-09-04 at 10 38 05 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/4a009afa-a8f0-4eaa-a76b-8f4e509f35a3\">\r\n\r\n\r\n**Prebuilt rules table bulk install**\r\n<img width=\"1478\" alt=\"Screenshot 2024-09-04 at 10 38 16 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/eb6deb9b-9b4e-4be3-a4ac-0da06d6f1e8e\">\r\n\r\n\r\n**Prebuilt rule details flyout**\r\n<img width=\"1489\" alt=\"Screenshot 2024-09-04 at 10 38 44 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/a4bce22d-7e90-42e4-8522-cf411a297659\">\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [ ] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"3bea483f34e03ea1bbeb836350c650fd06673f10"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/191529","number":191529,"mergeCommit":{"message":"[Security Solution] Adds enable on install UI workflow to prebuilt rules page (#191529)\n\n## Summary\r\n\r\nAdds overflow button UI to all prebuilt rules install buttons in order\r\nto enable the rule when it is successfully installed. Previously, a user\r\nwould have to navigate back to the rules page and find the rule(s) they\r\njust installed to enable, this combines those two workflows into a\r\nsingle button action - speeding up the out of the box rule\r\nimplementation.\r\n\r\n### Screenshots\r\n**Prebuilt rules table columns**\r\n<img width=\"530\" alt=\"Screenshot 2024-09-04 at 10 38 05 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/4a009afa-a8f0-4eaa-a76b-8f4e509f35a3\">\r\n\r\n\r\n**Prebuilt rules table bulk install**\r\n<img width=\"1478\" alt=\"Screenshot 2024-09-04 at 10 38 16 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/eb6deb9b-9b4e-4be3-a4ac-0da06d6f1e8e\">\r\n\r\n\r\n**Prebuilt rule details flyout**\r\n<img width=\"1489\" alt=\"Screenshot 2024-09-04 at 10 38 44 AM\"\r\nsrc=\"https://github.com/user-attachments/assets/a4bce22d-7e90-42e4-8522-cf411a297659\">\r\n\r\n\r\n### Checklist\r\n\r\nDelete any items that are not applicable to this PR.\r\n\r\n- [x] Any text added follows [EUI's writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\r\nsentence case text and includes [i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n- [ ]\r\n[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)\r\nwas added for features that require explanation or tutorials\r\n- [ ] [Unit or functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere updated or added to match the most common scenarios\r\n- [ ] [Flaky Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was\r\nused on any tests changed\r\n\r\n\r\n### For maintainers\r\n\r\n- [ ] This was checked for breaking API changes and was [labeled\r\nappropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"3bea483f34e03ea1bbeb836350c650fd06673f10"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 676d73f commit ef37576

File tree

7 files changed

+304
-78
lines changed

7 files changed

+304
-78
lines changed

x-pack/plugins/security_solution/public/detection_engine/rule_management/api/hooks/prebuilt_rules/use_perform_specific_rules_install_mutation.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,27 @@ import { useInvalidateFindRulesQuery } from '../use_find_rules_query';
1616
import { useInvalidateFetchRuleManagementFiltersQuery } from '../use_fetch_rule_management_filters_query';
1717
import { useInvalidateFetchRulesSnoozeSettingsQuery } from '../use_fetch_rules_snooze_settings_query';
1818
import { useInvalidateFetchPrebuiltRulesInstallReviewQuery } from './use_fetch_prebuilt_rules_install_review_query';
19+
import type { BulkAction } from '../../api';
1920
import { performInstallSpecificRules } from '../../api';
2021
import { useInvalidateFetchCoverageOverviewQuery } from '../use_fetch_coverage_overview_query';
22+
import { useBulkActionMutation } from '../use_bulk_action_mutation';
2123

2224
export const PERFORM_SPECIFIC_RULES_INSTALLATION_KEY = [
2325
'POST',
2426
'SPECIFIC_RULES',
2527
PERFORM_RULE_INSTALLATION_URL,
2628
];
2729

30+
export interface UsePerformSpecificRulesInstallParams {
31+
rules: InstallSpecificRulesRequest['rules'];
32+
enable?: boolean;
33+
}
34+
2835
export const usePerformSpecificRulesInstallMutation = (
2936
options?: UseMutationOptions<
3037
PerformRuleInstallationResponseBody,
3138
Error,
32-
InstallSpecificRulesRequest['rules']
39+
UsePerformSpecificRulesInstallParams
3340
>
3441
) => {
3542
const invalidateFindRulesQuery = useInvalidateFindRulesQuery();
@@ -40,15 +47,15 @@ export const usePerformSpecificRulesInstallMutation = (
4047
useInvalidateFetchPrebuiltRulesInstallReviewQuery();
4148
const invalidateRuleStatus = useInvalidateFetchPrebuiltRulesStatusQuery();
4249
const invalidateFetchCoverageOverviewQuery = useInvalidateFetchCoverageOverviewQuery();
50+
const { mutateAsync } = useBulkActionMutation();
4351

4452
return useMutation<
4553
PerformRuleInstallationResponseBody,
4654
Error,
47-
InstallSpecificRulesRequest['rules']
55+
UsePerformSpecificRulesInstallParams
4856
>(
49-
(rulesToInstall: InstallSpecificRulesRequest['rules']) => {
50-
return performInstallSpecificRules(rulesToInstall);
51-
},
57+
(rulesToInstall: UsePerformSpecificRulesInstallParams) =>
58+
performInstallSpecificRules(rulesToInstall.rules),
5259
{
5360
...options,
5461
mutationKey: PERFORM_SPECIFIC_RULES_INSTALLATION_KEY,
@@ -62,6 +69,14 @@ export const usePerformSpecificRulesInstallMutation = (
6269
invalidateRuleStatus();
6370
invalidateFetchCoverageOverviewQuery();
6471

72+
const [response, , { enable }] = args;
73+
74+
if (response && enable) {
75+
const ruleIdsToEnable = response.results.created.map((rule) => rule.id);
76+
const bulkAction: BulkAction = { type: 'enable', ids: ruleIdsToEnable };
77+
mutateAsync({ bulkAction });
78+
}
79+
6580
if (options?.onSettled) {
6681
options.onSettled(...args);
6782
}

x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/add_prebuilt_rules_table/add_prebuilt_rules_header_buttons.tsx

+73-13
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@
55
* 2.0.
66
*/
77

8-
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
9-
import React from 'react';
8+
import {
9+
EuiButton,
10+
EuiButtonIcon,
11+
EuiContextMenuItem,
12+
EuiContextMenuPanel,
13+
EuiFlexGroup,
14+
EuiFlexItem,
15+
EuiLoadingSpinner,
16+
EuiPopover,
17+
} from '@elastic/eui';
18+
import React, { useCallback, useMemo } from 'react';
19+
import { useBoolean } from 'react-use';
1020
import { useUserData } from '../../../../../detections/components/user_info';
1121
import { useAddPrebuiltRulesTableContext } from './add_prebuilt_rules_table_context';
1222
import * as i18n from './translations';
@@ -31,19 +41,69 @@ export const AddPrebuiltRulesHeaderButtons = () => {
3141
const isRuleInstalling = loadingRules.length > 0;
3242
const isRequestInProgress = isRuleInstalling || isRefetching || isUpgradingSecurityPackages;
3343

44+
const [isOverflowPopoverOpen, setOverflowPopover] = useBoolean(false);
45+
46+
const onOverflowButtonClick = () => {
47+
setOverflowPopover(!isOverflowPopoverOpen);
48+
};
49+
50+
const closeOverflowPopover = useCallback(() => {
51+
setOverflowPopover(false);
52+
}, [setOverflowPopover]);
53+
54+
const enableOnClick = useCallback(() => {
55+
installSelectedRules(true);
56+
closeOverflowPopover();
57+
}, [closeOverflowPopover, installSelectedRules]);
58+
59+
const installOnClick = useCallback(() => {
60+
installSelectedRules();
61+
}, [installSelectedRules]);
62+
63+
const overflowItems = useMemo(
64+
() => [
65+
<EuiContextMenuItem key="copy" icon={'play'} onClick={enableOnClick}>
66+
{i18n.INSTALL_AND_ENABLE_BUTTON_LABEL}
67+
</EuiContextMenuItem>,
68+
],
69+
[enableOnClick]
70+
);
71+
3472
return (
3573
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
3674
{shouldDisplayInstallSelectedRulesButton ? (
37-
<EuiFlexItem grow={false}>
38-
<EuiButton
39-
onClick={installSelectedRules}
40-
disabled={!canUserEditRules || isRequestInProgress}
41-
data-test-subj="installSelectedRulesButton"
42-
>
43-
{i18n.INSTALL_SELECTED_RULES(numberOfSelectedRules)}
44-
{isRuleInstalling ? <EuiLoadingSpinner size="s" /> : undefined}
45-
</EuiButton>
46-
</EuiFlexItem>
75+
<>
76+
<EuiFlexItem grow={false}>
77+
<EuiButton
78+
onClick={installOnClick}
79+
disabled={!canUserEditRules || isRequestInProgress}
80+
data-test-subj="installSelectedRulesButton"
81+
>
82+
{i18n.INSTALL_SELECTED_RULES(numberOfSelectedRules)}
83+
{isRuleInstalling && <EuiLoadingSpinner size="s" />}
84+
</EuiButton>
85+
</EuiFlexItem>
86+
<EuiFlexItem grow={false}>
87+
<EuiPopover
88+
button={
89+
<EuiButtonIcon
90+
display="base"
91+
size="m"
92+
iconType="boxesVertical"
93+
aria-label={i18n.INSTALL_RULES_OVERFLOW_BUTTON_ARIA_LABEL}
94+
onClick={onOverflowButtonClick}
95+
disabled={!canUserEditRules || isRequestInProgress}
96+
/>
97+
}
98+
isOpen={isOverflowPopoverOpen}
99+
closePopover={closeOverflowPopover}
100+
panelPaddingSize="s"
101+
anchorPosition="downRight"
102+
>
103+
<EuiContextMenuPanel size="s" items={overflowItems} />
104+
</EuiPopover>
105+
</EuiFlexItem>
106+
</>
47107
) : null}
48108
<EuiFlexItem grow={false}>
49109
<EuiButton
@@ -55,7 +115,7 @@ export const AddPrebuiltRulesHeaderButtons = () => {
55115
aria-label={i18n.INSTALL_ALL_ARIA_LABEL}
56116
>
57117
{i18n.INSTALL_ALL}
58-
{isRuleInstalling ? <EuiLoadingSpinner size="s" /> : undefined}
118+
{isRuleInstalling && <EuiLoadingSpinner size="s" />}
59119
</EuiButton>
60120
</EuiFlexItem>
61121
</EuiFlexGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
EuiButtonEmpty,
10+
EuiButtonIcon,
11+
EuiContextMenuItem,
12+
EuiContextMenuPanel,
13+
EuiFlexGroup,
14+
EuiFlexItem,
15+
EuiLoadingSpinner,
16+
EuiPopover,
17+
} from '@elastic/eui';
18+
import React, { useCallback, useMemo } from 'react';
19+
import { useBoolean } from 'react-use';
20+
import type { Rule } from '../../../../rule_management/logic';
21+
import type { RuleSignatureId } from '../../../../../../common/api/detection_engine';
22+
import type { AddPrebuiltRulesTableActions } from './add_prebuilt_rules_table_context';
23+
import * as i18n from './translations';
24+
25+
export interface PrebuiltRulesInstallButtonProps {
26+
ruleId: RuleSignatureId;
27+
record: Rule;
28+
installOneRule: AddPrebuiltRulesTableActions['installOneRule'];
29+
loadingRules: RuleSignatureId[];
30+
isDisabled: boolean;
31+
}
32+
33+
export const PrebuiltRulesInstallButton = ({
34+
ruleId,
35+
record,
36+
installOneRule,
37+
loadingRules,
38+
isDisabled,
39+
}: PrebuiltRulesInstallButtonProps) => {
40+
const isRuleInstalling = loadingRules.includes(ruleId);
41+
const isInstallButtonDisabled = isRuleInstalling || isDisabled;
42+
const [isPopoverOpen, setPopover] = useBoolean(false);
43+
44+
const onOverflowButtonClick = useCallback(() => {
45+
setPopover(!isPopoverOpen);
46+
}, [isPopoverOpen, setPopover]);
47+
48+
const closeOverflowPopover = useCallback(() => {
49+
setPopover(false);
50+
}, [setPopover]);
51+
52+
const enableOnClick = useCallback(() => {
53+
installOneRule(ruleId, true);
54+
closeOverflowPopover();
55+
}, [closeOverflowPopover, installOneRule, ruleId]);
56+
57+
const installOnClick = useCallback(() => {
58+
installOneRule(ruleId);
59+
}, [installOneRule, ruleId]);
60+
61+
const overflowItems = useMemo(
62+
() => [
63+
<EuiContextMenuItem key="copy" icon={'play'} onClick={enableOnClick}>
64+
{i18n.INSTALL_AND_ENABLE_BUTTON_LABEL}
65+
</EuiContextMenuItem>,
66+
],
67+
[enableOnClick]
68+
);
69+
70+
const popoverButton = useMemo(
71+
() => (
72+
<EuiButtonIcon
73+
display="empty"
74+
size="s"
75+
iconType="boxesVertical"
76+
aria-label={i18n.INSTALL_RULES_OVERFLOW_BUTTON_ARIA_LABEL}
77+
onClick={onOverflowButtonClick}
78+
disabled={isInstallButtonDisabled}
79+
/>
80+
),
81+
[isInstallButtonDisabled, onOverflowButtonClick]
82+
);
83+
84+
if (isRuleInstalling) {
85+
return (
86+
<EuiLoadingSpinner
87+
size="s"
88+
data-test-subj={`installSinglePrebuiltRuleButton-loadingSpinner-${ruleId}`}
89+
/>
90+
);
91+
}
92+
return (
93+
<EuiFlexGroup responsive={false} gutterSize="xs" alignItems="center">
94+
<EuiFlexItem grow={false}>
95+
<EuiButtonEmpty
96+
size="s"
97+
disabled={isInstallButtonDisabled}
98+
onClick={installOnClick}
99+
data-test-subj={`installSinglePrebuiltRuleButton-${ruleId}`}
100+
aria-label={i18n.INSTALL_RULE_BUTTON_ARIA_LABEL(record.name)}
101+
>
102+
{i18n.INSTALL_BUTTON_LABEL}
103+
</EuiButtonEmpty>
104+
</EuiFlexItem>
105+
<EuiFlexItem grow={false}>
106+
<EuiPopover
107+
button={popoverButton}
108+
isOpen={isPopoverOpen}
109+
closePopover={closeOverflowPopover}
110+
panelPaddingSize="s"
111+
anchorPosition="downRight"
112+
>
113+
<EuiContextMenuPanel size="s" items={overflowItems} />
114+
</EuiPopover>
115+
</EuiFlexItem>
116+
</EuiFlexGroup>
117+
);
118+
};

0 commit comments

Comments
 (0)