Skip to content

Commit e343175

Browse files
authored
[SIEM] Move Timeline Template field to first step of rule creation (#60840)
* Move timeline template to Define step of Rule creation This required a refactor/simplification of the step_define_rule logic to make things work. In retrospect I think that the issue was we were not handling incoming `defaultValues` props well, which was causing local component state to be lost. Now that we're doing a merge and removed a few unneeded local useStates, things are a) working and b) cleaner * Fix Rule details/edit view with updated data We need to fix the other side of the equation to get these to work: the timeline data was moved to a different step during creation, but when viewing on the frontend we split the rule data back into the separate "steps." * Remove unused import * Fix bug in formatDefineStepData I neglected to pass through index in a previous commit. * Update tests now that timeline has movied to a different step * Fix more tests * Update StepRuleDescription snapshots * Fix cypress Rule Creation test Timeline template moved, and so tests broke. * Add unit tests for filterRuleFieldsForType
1 parent 5755b2a commit e343175

File tree

15 files changed

+308
-348
lines changed

15 files changed

+308
-348
lines changed

x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import {
1313
ABOUT_SEVERITY,
1414
ABOUT_STEP,
1515
ABOUT_TAGS,
16-
ABOUT_TIMELINE,
1716
ABOUT_URLS,
1817
DEFINITION_CUSTOM_QUERY,
1918
DEFINITION_INDEX_PATTERNS,
19+
DEFINITION_TIMELINE,
2020
DEFINITION_STEP,
2121
RULE_NAME_HEADER,
2222
SCHEDULE_LOOPBACK,
@@ -170,10 +170,6 @@ describe('Signal detection rules', () => {
170170
.eq(ABOUT_RISK)
171171
.invoke('text')
172172
.should('eql', newRule.riskScore);
173-
cy.get(ABOUT_STEP)
174-
.eq(ABOUT_TIMELINE)
175-
.invoke('text')
176-
.should('eql', 'Default blank timeline');
177173
cy.get(ABOUT_STEP)
178174
.eq(ABOUT_URLS)
179175
.invoke('text')
@@ -202,6 +198,10 @@ describe('Signal detection rules', () => {
202198
.eq(DEFINITION_CUSTOM_QUERY)
203199
.invoke('text')
204200
.should('eql', `${newRule.customQuery} `);
201+
cy.get(DEFINITION_STEP)
202+
.eq(DEFINITION_TIMELINE)
203+
.invoke('text')
204+
.should('eql', 'Default blank timeline');
205205

206206
cy.get(SCHEDULE_STEP)
207207
.eq(SCHEDULE_RUNS)

x-pack/legacy/plugins/siem/cypress/screens/rule_details.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
export const ABOUT_FALSE_POSITIVES = 4;
7+
export const ABOUT_FALSE_POSITIVES = 3;
88

9-
export const ABOUT_MITRE = 5;
9+
export const ABOUT_MITRE = 4;
1010

1111
export const ABOUT_RULE_DESCRIPTION = '[data-test-subj=stepAboutRuleDetailsToggleDescriptionText]';
1212

@@ -16,14 +16,14 @@ export const ABOUT_SEVERITY = 0;
1616

1717
export const ABOUT_STEP = '[data-test-subj="aboutRule"] .euiDescriptionList__description';
1818

19-
export const ABOUT_TAGS = 6;
19+
export const ABOUT_TAGS = 5;
2020

21-
export const ABOUT_TIMELINE = 2;
22-
23-
export const ABOUT_URLS = 3;
21+
export const ABOUT_URLS = 2;
2422

2523
export const DEFINITION_CUSTOM_QUERY = 1;
2624

25+
export const DEFINITION_TIMELINE = 3;
26+
2727
export const DEFINITION_INDEX_PATTERNS =
2828
'[data-test-subj=definitionRule] [data-test-subj="listItemColumnStepRuleDescription"] .euiDescriptionList__description .euiBadge__text';
2929

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/__mocks__/mock.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,6 @@ export const mockAboutStepRule = (isNew = false): AboutStepRule => ({
155155
references: ['www.test.co'],
156156
falsePositives: ['test'],
157157
tags: ['tag1', 'tag2'],
158-
timeline: {
159-
id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
160-
title: 'Titled timeline',
161-
},
162158
threat: [
163159
{
164160
framework: 'mockFramework',
@@ -186,6 +182,10 @@ export const mockDefineStepRule = (isNew = false): DefineStepRule => ({
186182
machineLearningJobId: '',
187183
index: ['filebeat-'],
188184
queryBar: mockQueryBar,
185+
timeline: {
186+
id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2',
187+
title: 'Titled timeline',
188+
},
189189
});
190190

191191
export const mockScheduleStepRule = (isNew = false, enabled = false): ScheduleStepRule => ({

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/__snapshots__/index.test.tsx.snap

Lines changed: 11 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/index.test.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
Filter,
1919
FilterManager,
2020
} from '../../../../../../../../../../src/plugins/data/public';
21-
import { mockAboutStepRule } from '../../all/__mocks__/mock';
21+
import { mockAboutStepRule, mockDefineStepRule } from '../../all/__mocks__/mock';
2222
import { coreMock } from '../../../../../../../../../../src/core/public/mocks';
2323
import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations';
2424
import * as i18n from './translations';
@@ -263,7 +263,7 @@ describe('description_step', () => {
263263
test('returns expected ListItems array when given valid inputs', () => {
264264
const result: ListItems[] = buildListItems(mockAboutStep, schema, mockFilterManager);
265265

266-
expect(result.length).toEqual(10);
266+
expect(result.length).toEqual(9);
267267
});
268268
});
269269

@@ -431,10 +431,11 @@ describe('description_step', () => {
431431

432432
describe('timeline', () => {
433433
test('returns timeline title if one exists', () => {
434+
const mockDefineStep = mockDefineStepRule();
434435
const result: ListItems[] = getDescriptionItem(
435436
'timeline',
436437
'Timeline label',
437-
mockAboutStep,
438+
mockDefineStep,
438439
mockFilterManager
439440
);
440441

@@ -444,7 +445,7 @@ describe('description_step', () => {
444445

445446
test('returns default timeline title if none exists', () => {
446447
const mockStep = {
447-
...mockAboutStep,
448+
...mockDefineStepRule(),
448449
timeline: {
449450
id: '12345',
450451
},

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/default_value.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66

77
import { AboutStepRule } from '../../types';
8-
import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations';
98

109
export const threatDefault = [
1110
{
@@ -24,10 +23,6 @@ export const stepAboutDefaultValue: AboutStepRule = {
2423
references: [''],
2524
falsePositives: [''],
2625
tags: [],
27-
timeline: {
28-
id: null,
29-
title: DEFAULT_TIMELINE_TITLE,
30-
},
3126
threat: threatDefault,
3227
note: '',
3328
};

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/index.tsx

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import { stepAboutDefaultValue } from './default_value';
3737
import { isUrlInvalid } from './helpers';
3838
import { schema } from './schema';
3939
import * as I18n from './translations';
40-
import { PickTimeline } from '../pick_timeline';
4140
import { StepContentWrapper } from '../step_content_wrapper';
4241
import { MarkdownEditorForm } from '../../../../../components/markdown_editor/form';
4342

@@ -216,15 +215,6 @@ const StepAboutRuleComponent: FC<StepAboutRuleProps> = ({
216215
buttonContent={AdvancedSettingsAccordionButton}
217216
>
218217
<EuiSpacer size="m" />
219-
<UseField
220-
path="timeline"
221-
component={PickTimeline}
222-
componentProps={{
223-
idAria: 'detectionEngineStepAboutRuleTimeline',
224-
isDisabled: isLoading,
225-
dataTestSubj: 'detectionEngineStepAboutRuleTimeline',
226-
}}
227-
/>
228218
<UseField
229219
path="references"
230220
component={AddItem}

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_about_rule/schema.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,6 @@ export const schema: FormSchema = {
9191
}
9292
),
9393
},
94-
timeline: {
95-
label: i18n.translate(
96-
'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateLabel',
97-
{
98-
defaultMessage: 'Timeline template',
99-
}
100-
),
101-
helpText: i18n.translate(
102-
'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldTimelineTemplateHelpText',
103-
{
104-
defaultMessage:
105-
'Select an existing timeline to use as a template when investigating generated signals.',
106-
}
107-
),
108-
},
10994
references: {
11095
label: i18n.translate(
11196
'xpack.siem.detectionEngine.createRule.stepAboutRule.fieldReferenceUrlsLabel',

x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/index.tsx

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import {
1212
EuiFormRow,
1313
EuiButton,
1414
} from '@elastic/eui';
15-
import { isEmpty } from 'lodash/fp';
1615
import React, { FC, memo, useCallback, useState, useEffect, useContext } from 'react';
1716
import styled from 'styled-components';
1817
import deepEqual from 'fast-deep-equal';
1918

2019
import { IIndexPattern } from '../../../../../../../../../../src/plugins/data/public';
2120
import { useFetchIndexPatterns } from '../../../../../containers/detection_engine/rules';
2221
import { DEFAULT_INDEX_KEY } from '../../../../../../common/constants';
22+
import { DEFAULT_TIMELINE_TITLE } from '../../../../../components/timeline/translations';
2323
import { MlCapabilitiesContext } from '../../../../../components/ml/permissions/ml_capabilities_provider';
2424
import { useUiSetting$ } from '../../../../../lib/kibana';
2525
import { setFieldValue, isMlRule } from '../../helpers';
@@ -30,6 +30,7 @@ import { QueryBarDefineRule } from '../query_bar';
3030
import { SelectRuleType } from '../select_rule_type';
3131
import { AnomalyThresholdSlider } from '../anomaly_threshold_slider';
3232
import { MlJobSelect } from '../ml_job_select';
33+
import { PickTimeline } from '../pick_timeline';
3334
import { StepContentWrapper } from '../step_content_wrapper';
3435
import {
3536
Field,
@@ -61,6 +62,10 @@ const stepDefineDefaultValue: DefineStepRule = {
6162
filters: [],
6263
saved_id: undefined,
6364
},
65+
timeline: {
66+
id: null,
67+
title: DEFAULT_TIMELINE_TITLE,
68+
},
6469
};
6570

6671
const MyLabelButton = styled(EuiButtonEmpty)`
@@ -77,23 +82,6 @@ MyLabelButton.defaultProps = {
7782
flush: 'right',
7883
};
7984

80-
const getStepDefaultValue = (
81-
indicesConfig: string[],
82-
defaultValues: DefineStepRule | null
83-
): DefineStepRule => {
84-
if (defaultValues != null) {
85-
return {
86-
...defaultValues,
87-
isNew: false,
88-
};
89-
} else {
90-
return {
91-
...stepDefineDefaultValue,
92-
index: indicesConfig != null ? indicesConfig : [],
93-
};
94-
}
95-
};
96-
9785
const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
9886
addPadding = false,
9987
defaultValues,
@@ -106,18 +94,16 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
10694
}) => {
10795
const mlCapabilities = useContext(MlCapabilitiesContext);
10896
const [openTimelineSearch, setOpenTimelineSearch] = useState(false);
109-
const [localUseIndicesConfig, setLocalUseIndicesConfig] = useState(false);
97+
const [indexModified, setIndexModified] = useState(false);
11098
const [localIsMlRule, setIsMlRule] = useState(false);
11199
const [indicesConfig] = useUiSetting$<string[]>(DEFAULT_INDEX_KEY);
112-
const [mylocalIndicesConfig, setMyLocalIndicesConfig] = useState(
113-
defaultValues != null ? defaultValues.index : indicesConfig ?? []
114-
);
100+
const [myStepData, setMyStepData] = useState<DefineStepRule>({
101+
...stepDefineDefaultValue,
102+
index: indicesConfig ?? [],
103+
});
115104
const [
116105
{ browserFields, indexPatterns: indexPatternQueryBar, isLoading: indexPatternLoadingQueryBar },
117-
] = useFetchIndexPatterns(mylocalIndicesConfig);
118-
const [myStepData, setMyStepData] = useState<DefineStepRule>(
119-
getStepDefaultValue(indicesConfig, null)
120-
);
106+
] = useFetchIndexPatterns(myStepData.index);
121107

122108
const { form } = useForm({
123109
defaultValue: myStepData,
@@ -138,15 +124,13 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
138124
}, [form]);
139125

140126
useEffect(() => {
141-
if (indicesConfig != null && defaultValues != null) {
142-
const myDefaultValues = getStepDefaultValue(indicesConfig, defaultValues);
143-
if (!deepEqual(myDefaultValues, myStepData)) {
144-
setMyStepData(myDefaultValues);
145-
setLocalUseIndicesConfig(deepEqual(myDefaultValues.index, indicesConfig));
146-
setFieldValue(form, schema, myDefaultValues);
147-
}
127+
const { isNew, ...values } = myStepData;
128+
if (defaultValues != null && !deepEqual(values, defaultValues)) {
129+
const newValues = { ...values, ...defaultValues, isNew: false };
130+
setMyStepData(newValues);
131+
setFieldValue(form, schema, newValues);
148132
}
149-
}, [defaultValues, indicesConfig]);
133+
}, [defaultValues, setMyStepData, setFieldValue]);
150134

151135
useEffect(() => {
152136
if (setForm != null) {
@@ -195,7 +179,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
195179
path="index"
196180
config={{
197181
...schema.index,
198-
labelAppend: !localUseIndicesConfig ? (
182+
labelAppend: indexModified ? (
199183
<MyLabelButton onClick={handleResetIndices} iconType="refresh">
200184
{i18n.RESET_DEFAULT_INDEX}
201185
</MyLabelButton>
@@ -253,17 +237,22 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
253237
/>
254238
</>
255239
</EuiFormRow>
240+
<UseField
241+
path="timeline"
242+
component={PickTimeline}
243+
componentProps={{
244+
idAria: 'detectionEngineStepDefineRuleTimeline',
245+
isDisabled: isLoading,
246+
dataTestSubj: 'detectionEngineStepDefineRuleTimeline',
247+
}}
248+
/>
256249
<FormDataProvider pathsToWatch={['index', 'ruleType']}>
257250
{({ index, ruleType }) => {
258251
if (index != null) {
259-
if (deepEqual(index, indicesConfig) && !localUseIndicesConfig) {
260-
setLocalUseIndicesConfig(true);
261-
}
262-
if (!deepEqual(index, indicesConfig) && localUseIndicesConfig) {
263-
setLocalUseIndicesConfig(false);
264-
}
265-
if (index != null && !isEmpty(index) && !deepEqual(index, mylocalIndicesConfig)) {
266-
setMyLocalIndicesConfig(index);
252+
if (deepEqual(index, indicesConfig) && indexModified) {
253+
setIndexModified(false);
254+
} else if (!deepEqual(index, indicesConfig) && !indexModified) {
255+
setIndexModified(true);
267256
}
268257
}
269258

0 commit comments

Comments
 (0)