From c23af148bd9afa3c89478d2f65c9a9d54015e5fe Mon Sep 17 00:00:00 2001 From: FrankHassanabad Date: Thu, 1 Oct 2020 17:15:59 -0600 Subject: [PATCH] Updates the edit rules page to only have what is selected for editing --- .../rules/select_rule_type/index.test.tsx | 220 +++++++++++++++++- .../rules/select_rule_type/index.tsx | 153 ++++++------ .../rules/step_define_rule/index.tsx | 2 +- 3 files changed, 297 insertions(+), 78 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx index 87401408c3cc1..b2c22af3a75f9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx @@ -5,21 +5,235 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { SelectRuleType } from './index'; -import { useFormFieldMock } from '../../../../common/mock'; +import { TestProviders, useFormFieldMock } from '../../../../common/mock'; jest.mock('../../../../common/lib/kibana'); describe('SelectRuleType', () => { + // I do this to avoid the messy warning from happening + // Warning: React does not recognize the `isVisible` prop on a DOM element. + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(jest.fn()); + }); + + afterEach(() => { + jest.spyOn(console, 'error').mockRestore(); + }); + it('renders correctly', () => { const Component = () => { const field = useFormFieldMock(); - return ; + return ( + + ); }; const wrapper = shallow(); expect(wrapper.dive().find('[data-test-subj="selectRuleType"]')).toHaveLength(1); }); + + describe('update mode vs. non-update mode', () => { + it('renders all the cards when not in update mode', () => { + const field = useFormFieldMock({ value: 'query' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeTruthy(); + }); + + it('renders only the card selected when in update mode of "eql"', () => { + const field = useFormFieldMock({ value: 'eql' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "machine_learning', () => { + const field = useFormFieldMock({ value: 'machine_learning' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "query', () => { + const field = useFormFieldMock({ value: 'query' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "threshold"', () => { + const field = useFormFieldMock({ value: 'threshold' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "threat_match', () => { + const field = useFormFieldMock({ value: 'threat_match' }); + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeTruthy(); + }); + }); + + describe('permissions', () => { + it('renders machine learning as disabled if "hasValidLicense" is false and it is not selected', () => { + const field = useFormFieldMock({ value: 'query' }); + const wrapper = mount( + + + + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(true); + }); + + it('renders machine learning as not disabled if "hasValidLicense" is false and it is selected', () => { + const field = useFormFieldMock({ value: 'machine_learning' }); + const wrapper = mount( + + + + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(false); + }); + + it('renders machine learning as disabled if "isMlAdmin" is false and it is not selected', () => { + const field = useFormFieldMock({ value: 'query' }); + const wrapper = mount( + + + + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(true); + }); + + it('renders machine learning as not disabled if "isMlAdmin" is false and it is selected', () => { + const field = useFormFieldMock({ value: 'machine_learning' }); + const wrapper = mount( + + + + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(false); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx index 9a1d11a2dfe42..5feed799ca783 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx @@ -21,19 +21,19 @@ import { MlCardDescription } from './ml_card_description'; import { Type } from '../../../../../common/detection_engine/schemas/common/schemas'; interface SelectRuleTypeProps { - describedByIds?: string[]; + describedByIds: string[]; field: FieldHook; - hasValidLicense?: boolean; - isMlAdmin?: boolean; - isReadOnly?: boolean; + hasValidLicense: boolean; + isMlAdmin: boolean; + isUpdateView: boolean; } export const SelectRuleType: React.FC = ({ describedByIds = [], field, - isReadOnly = false, - hasValidLicense = false, - isMlAdmin = false, + isUpdateView, + hasValidLicense, + isMlAdmin, }) => { const ruleType = field.value as Type; const setType = useCallback( @@ -47,54 +47,54 @@ export const SelectRuleType: React.FC = ({ const setQuery = useCallback(() => setType('query'), [setType]); const setThreshold = useCallback(() => setType('threshold'), [setType]); const setThreatMatch = useCallback(() => setType('threat_match'), [setType]); - const mlCardDisabled = isReadOnly || !hasValidLicense || !isMlAdmin; const licensingUrl = useKibana().services.application.getUrlForApp('kibana', { path: '#/management/stack/license_management', }); const eqlSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setEql, isSelected: isEqlRule(ruleType), + isVisible: !isUpdateView || isEqlRule(ruleType), }), - [isReadOnly, ruleType, setEql] + [ruleType, setEql, isUpdateView] ); const querySelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setQuery, isSelected: isQueryRule(ruleType), + isVisible: !isUpdateView || isQueryRule(ruleType), }), - [isReadOnly, ruleType, setQuery] + [ruleType, setQuery, isUpdateView] ); const mlSelectableConfig = useMemo( () => ({ - isDisabled: mlCardDisabled, + isDisabled: !hasValidLicense || !isMlAdmin, onClick: setMl, isSelected: isMlRule(ruleType), + isVisible: !isUpdateView || isMlRule(ruleType), }), - [mlCardDisabled, ruleType, setMl] + [ruleType, setMl, isUpdateView, hasValidLicense, isMlAdmin] ); const thresholdSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setThreshold, isSelected: isThresholdRule(ruleType), + isVisible: !isUpdateView || isThresholdRule(ruleType), }), - [isReadOnly, ruleType, setThreshold] + [ruleType, setThreshold, isUpdateView] ); const threatMatchSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setThreatMatch, isSelected: isThreatMatchRule(ruleType), + isVisible: !isUpdateView || isThreatMatchRule(ruleType), }), - [isReadOnly, ruleType, setThreatMatch] + [ruleType, setThreatMatch, isUpdateView] ); return ( @@ -105,62 +105,67 @@ export const SelectRuleType: React.FC = ({ label={field.label} > - - } - isDisabled={querySelectableConfig.isDisabled && !querySelectableConfig.isSelected} - selectable={querySelectableConfig} - /> - - - - } - icon={} - isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected} - selectable={mlSelectableConfig} - /> - - - } - isDisabled={ - thresholdSelectableConfig.isDisabled && !thresholdSelectableConfig.isSelected - } - selectable={thresholdSelectableConfig} - /> - - - } - isDisabled={eqlSelectableConfig.isDisabled && !eqlSelectableConfig.isSelected} - selectable={eqlSelectableConfig} - /> - - - } - isDisabled={ - threatMatchSelectableConfig.isDisabled && !threatMatchSelectableConfig.isSelected - } - selectable={threatMatchSelectableConfig} - /> - + {querySelectableConfig.isVisible && ( + + } + selectable={querySelectableConfig} + /> + + )} + {mlSelectableConfig.isVisible && ( + + + } + icon={} + isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected} + selectable={mlSelectableConfig} + /> + + )} + {thresholdSelectableConfig.isVisible && ( + + } + selectable={thresholdSelectableConfig} + /> + + )} + {eqlSelectableConfig.isVisible && ( + + } + selectable={eqlSelectableConfig} + /> + + )} + {threatMatchSelectableConfig.isVisible && ( + + } + selectable={threatMatchSelectableConfig} + /> + + )} ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index f728a508fef86..97c7c09430afb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -236,7 +236,7 @@ const StepDefineRuleComponent: FC = ({ component={SelectRuleType} componentProps={{ describedByIds: ['detectionEngineStepDefineRuleType'], - isReadOnly: isUpdateView, + isUpdateView, hasValidLicense: hasMlLicense(mlCapabilities), isMlAdmin: hasMlAdminPermissions(mlCapabilities), }}