Skip to content

Commit

Permalink
[Security Solution][Exceptions] - Add exception list duplication opti…
Browse files Browse the repository at this point in the history
…ons with and without expired items (#154991)

## Summary

Adds the following:

- Add the option to duplicate from the shared exception list management
actions dropdowns
  - User can select to include exception items with expired TTL
  - User can select to not include exception items with expired TTL 
  - Cypress tests added for both options
  • Loading branch information
yctercero committed Apr 21, 2023
1 parent fdc23f5 commit 1115532
Show file tree
Hide file tree
Showing 67 changed files with 2,297 additions and 326 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -32,6 +32,7 @@ interface ExceptionListHeaderComponentProps {
onDeleteList: () => void;
onManageRules: () => void;
onExportList: () => void;
onDuplicateList: () => void;
}

export interface BackOptions {
Expand All @@ -54,6 +55,7 @@ const ExceptionListHeaderComponent: FC<ExceptionListHeaderComponentProps> = ({
onDeleteList,
onManageRules,
onExportList,
onDuplicateList,
}) => {
const { isModalVisible, listDetails, onEdit, onSave, onCancel } = useExceptionListHeader({
name,
Expand Down Expand Up @@ -100,6 +102,7 @@ const ExceptionListHeaderComponent: FC<ExceptionListHeaderComponentProps> = ({
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onExportList={onExportList}
onDuplicateList={onDuplicateList}
/>,
]}
breadcrumbs={[
Expand Down
Expand Up @@ -17,6 +17,7 @@ const onExportList = jest.fn();
const onDeleteList = jest.fn();
const onManageRules = jest.fn();
const onNavigate = jest.fn();
const onDuplicateList = jest.fn();
jest.mock('./use_list_header');

describe('ExceptionListHeader', () => {
Expand All @@ -40,6 +41,7 @@ describe('ExceptionListHeader', () => {
onExportList={onExportList}
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onDuplicateList={onDuplicateList}
backOptions={{ pageId: '', path: '', onNavigate }}
/>
);
Expand Down Expand Up @@ -70,6 +72,7 @@ describe('ExceptionListHeader', () => {
onExportList={onExportList}
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onDuplicateList={onDuplicateList}
backOptions={{ pageId: '', path: '', onNavigate }}
/>
);
Expand All @@ -79,6 +82,7 @@ describe('ExceptionListHeader', () => {

expect(wrapper.queryByTestId('RightSideMenuItemsMenuActionsActionItem1')).toBeEnabled();
expect(wrapper.queryByTestId('RightSideMenuItemsMenuActionsActionItem2')).toBeDisabled();
expect(wrapper.queryByTestId('RightSideMenuItemsMenuActionsActionItem3')).toBeDisabled();
expect(wrapper.queryByTestId('EditTitleIcon')).not.toBeInTheDocument();
});
it('should render the List Header with name, default description and actions', () => {
Expand All @@ -93,6 +97,7 @@ describe('ExceptionListHeader', () => {
onExportList={onExportList}
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onDuplicateList={onDuplicateList}
backOptions={{ pageId: '', path: '', onNavigate }}
/>
);
Expand Down Expand Up @@ -123,6 +128,7 @@ describe('ExceptionListHeader', () => {
onExportList={onExportList}
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onDuplicateList={onDuplicateList}
backOptions={{ pageId: '', path: '', onNavigate }}
/>
);
Expand All @@ -148,6 +154,7 @@ describe('ExceptionListHeader', () => {
onExportList={onExportList}
onDeleteList={onDeleteList}
onManageRules={onManageRules}
onDuplicateList={onDuplicateList}
backOptions={{ pageId: '', path: 'test-path', onNavigate }}
/>
);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -21,6 +21,7 @@ interface MenuItemsProps {
onDeleteList: () => void;
onManageRules: () => void;
onExportList: () => void;
onDuplicateList: () => void;
}

const MenuItemsComponent: FC<MenuItemsProps> = ({
Expand All @@ -32,6 +33,7 @@ const MenuItemsComponent: FC<MenuItemsProps> = ({
onDeleteList,
onManageRules,
onExportList,
onDuplicateList,
}) => {
const referencedLinks = useMemo(
() =>
Expand Down Expand Up @@ -100,6 +102,15 @@ const MenuItemsComponent: FC<MenuItemsProps> = ({
},
{
key: '2',
icon: 'copy',
label: i18n.EXCEPTION_LIST_HEADER_DUPLICATE_ACTION,
onClick: () => {
if (typeof onDuplicateList === 'function') onDuplicateList();
},
disabled: !canUserEditList,
},
{
key: '3',
icon: 'trash',
label: i18n.EXCEPTION_LIST_HEADER_DELETE_ACTION,
onClick: () => {
Expand Down
Expand Up @@ -15,6 +15,7 @@ import { securityLinkAnchorComponentMock } from '../../mocks/security_link_compo
const onExportList = jest.fn();
const onDeleteList = jest.fn();
const onManageRules = jest.fn();
const onDuplicateList = jest.fn();
describe('MenuItems', () => {
it('should render linkedRules, manageRules and menuActions', () => {
const wrapper = render(
Expand All @@ -24,6 +25,7 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
Expand All @@ -40,6 +42,7 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
Expand All @@ -55,15 +58,17 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
fireEvent.click(wrapper.getByTestId('MenuActionsButtonIcon'));
expect(wrapper).toMatchSnapshot();
expect(wrapper.getByTestId('MenuActionsActionItem1')).toBeEnabled();
expect(wrapper.getByTestId('MenuActionsActionItem2')).toBeEnabled();
expect(wrapper.getByTestId('MenuActionsActionItem3')).toBeEnabled();
});
it('should render delete action disabled', () => {
it('should render delete action disabled when "canUserEditList" is "false"', () => {
const wrapper = render(
<MenuItems
isReadonly={false}
Expand All @@ -72,12 +77,15 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
fireEvent.click(wrapper.getByTestId('MenuActionsButtonIcon'));
expect(wrapper).toMatchSnapshot();
expect(wrapper.getByTestId('MenuActionsActionItem1')).toBeEnabled();
expect(wrapper.getByTestId('MenuActionsActionItem2')).toBeDisabled();
expect(wrapper.getByTestId('MenuActionsActionItem3')).toBeDisabled();
});
it('should not render Manage rules', () => {
const wrapper = render(
Expand All @@ -88,6 +96,7 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
Expand All @@ -102,6 +111,7 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
Expand All @@ -116,6 +126,7 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
Expand All @@ -132,12 +143,31 @@ describe('MenuItems', () => {
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
fireEvent.click(wrapper.getByTestId('MenuActionsButtonIcon'));
fireEvent.click(wrapper.getByTestId('MenuActionsActionItem2'));
fireEvent.click(wrapper.getByTestId('MenuActionsActionItem3'));

expect(onDeleteList).toHaveBeenCalled();
});

it('should call onDuplicateList', () => {
const wrapper = render(
<MenuItems
isReadonly={false}
linkedRules={rules}
securityLinkAnchorComponent={securityLinkAnchorComponentMock}
onExportList={onExportList}
onDeleteList={onDeleteList}
onDuplicateList={onDuplicateList}
onManageRules={onManageRules}
/>
);
fireEvent.click(wrapper.getByTestId('MenuActionsButtonIcon'));
fireEvent.click(wrapper.getByTestId('MenuActionsActionItem2'));

expect(onDuplicateList).toHaveBeenCalled();
});
});
Expand Up @@ -67,6 +67,12 @@ export const EXCEPTION_LIST_HEADER_DELETE_ACTION = i18n.translate(
defaultMessage: 'Delete exception list',
}
);
export const EXCEPTION_LIST_HEADER_DUPLICATE_ACTION = i18n.translate(
'exceptionList-components.exception_list_header_duplicate_action',
{
defaultMessage: 'Duplicate exception list',
}
);
export const EXCEPTION_LIST_HEADER_LINK_RULES_BUTTON = i18n.translate(
'exceptionList-components.exception_list_header_link_rules_button',
{
Expand Down
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { LIST_ID, NAMESPACE_TYPE } from '../../constants/index.mock';

import { DuplicateExceptionListQuerySchema } from '.';

export const getDuplicateExceptionListQuerySchemaMock = (): DuplicateExceptionListQuerySchema => ({
list_id: LIST_ID,
namespace_type: NAMESPACE_TYPE,
include_expired_exceptions: 'true',
});

0 comments on commit 1115532

Please sign in to comment.