From 5f171f2765eda862675dea1322e45afea1a77e59 Mon Sep 17 00:00:00 2001 From: Amardeepsingh Siglani Date: Thu, 16 Nov 2023 04:18:39 -0800 Subject: [PATCH 1/3] support more cases for detection; use code editor for condition Signed-off-by: Amardeepsingh Siglani --- .../RuleEditor/DetectionVisualEditor.tsx | 80 +++++++++---------- public/utils/validation.ts | 4 +- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/public/pages/Rules/components/RuleEditor/DetectionVisualEditor.tsx b/public/pages/Rules/components/RuleEditor/DetectionVisualEditor.tsx index 2c20e2392..9ea8b145a 100644 --- a/public/pages/Rules/components/RuleEditor/DetectionVisualEditor.tsx +++ b/public/pages/Rules/components/RuleEditor/DetectionVisualEditor.tsx @@ -29,10 +29,10 @@ import { EuiFilePicker, EuiButtonEmpty, EuiCallOut, + EuiCodeEditor, } from '@elastic/eui'; import _ from 'lodash'; import { validateCondition, validateDetectionFieldName } from '../../../../utils/validation'; -import { SelectionExpField } from './components/SelectionExpField'; export interface DetectionVisualEditorProps { detectionYml: string; @@ -90,7 +90,7 @@ const detectionModifierOptions = [ ]; const defaultDetectionObj: DetectionObject = { - condition: '', + condition: 'Selection_1', selections: [ { name: 'Selection_1', @@ -178,7 +178,18 @@ export class DetectionVisualEditor extends React.Component< const selectionMapJSON = detectionJSON[selectionKey]; const selectionDataEntries: SelectionData[] = []; - if (typeof selectionMapJSON === 'object') { + if (Array.isArray(selectionMapJSON)) { + selectionDataEntries.push({ + field: '', + modifier: 'all', + values: selectionMapJSON, + selectedRadioId: `${ + selectionMapJSON.length <= 1 + ? SelectionMapValueRadioId.VALUE + : SelectionMapValueRadioId.LIST + }-${selectionIdx}-0`, + }); + } else if (typeof selectionMapJSON === 'object') { Object.keys(selectionMapJSON).forEach((fieldKey, dataIdx) => { const [field, modifier] = fieldKey.split('|'); const val = selectionMapJSON[fieldKey]; @@ -212,11 +223,15 @@ export class DetectionVisualEditor extends React.Component< }; selections.forEach((selection) => { - const selectionMaps: any = {}; + let selectionMaps: any = {}; selection.data.forEach((datum) => { - const key = `${datum.field}${datum.modifier ? `|${datum.modifier}` : ''}`; - selectionMaps[key] = datum.values; + if (datum.field) { + const key = `${datum.field}${datum.modifier ? `|${datum.modifier}` : ''}`; + selectionMaps[key] = datum.values; + } else { + selectionMaps = datum.values; + } }); compiledDetection[selection.name] = selectionMaps; @@ -228,21 +243,16 @@ export class DetectionVisualEditor extends React.Component< private validateData = (selections: Selection[]) => { const { errors } = this.state; selections.map((selection, selIdx) => { - const fieldNames = new Set(); selection.data.map((data, idx) => { if ('field' in data) { const fieldName = `field_${selIdx}_${idx}`; delete errors.fields[fieldName]; - if (!data.field) { - errors.fields[fieldName] = 'Key name is required'; - } else if (fieldNames.has(data.field)) { - errors.fields[fieldName] = 'Key name already used'; - } else { - fieldNames.add(data.field); - if (!validateDetectionFieldName(data.field)) { - errors.fields[fieldName] = 'Invalid key name.'; - } + + if (!validateDetectionFieldName(data.field)) { + errors.fields[fieldName] = + 'Invalid key name. Valid characters are a-z, A-Z, 0-9, hyphens, dots, and underscores'; } + errors.touched[fieldName] = true; } @@ -343,10 +353,7 @@ export class DetectionVisualEditor extends React.Component< }; private validateCondition = (value: string) => { - const { - errors, - detectionObj: { selections }, - } = this.state; + const { errors } = this.state; value = value.trim(); delete errors.fields['condition']; if (!value) { @@ -354,18 +361,6 @@ export class DetectionVisualEditor extends React.Component< } else { if (!validateCondition(value)) { errors.fields['condition'] = 'Invalid condition.'; - } else { - const selectionNames = _.map(selections, 'name'); - const conditions = _.pull(value.split(' '), ...['and', 'or', 'not']); - conditions.map((selection) => { - if (_.indexOf(selectionNames, selection) === -1) { - errors.fields[ - 'condition' - ] = `Invalid selection name ${selection}. Allowed names: "${selectionNames.join( - ', ' - )}"`; - } - }); } } @@ -376,8 +371,6 @@ export class DetectionVisualEditor extends React.Component< }; private updateCondition = (value: string) => { - value = value.trim(); - const detectionObj: DetectionObject = { ...this.state.detectionObj, condition: value }; this.setState( { @@ -729,6 +722,7 @@ export class DetectionVisualEditor extends React.Component< { const newData = [ ...selection.data, @@ -754,14 +748,15 @@ export class DetectionVisualEditor extends React.Component< fullWidth iconType={'plusInCircle'} onClick={() => { + const selectionName = `Selection_${selections.length + 1}`; this.setState({ detectionObj: { - condition, + condition: `${condition} and ${selectionName}`, selections: [ ...selections, { ...defaultDetectionObj.selections[0], - name: `Selection_${selections.length + 1}`, + name: selectionName, }, ], }, @@ -796,11 +791,16 @@ export class DetectionVisualEditor extends React.Component< } > - this.updateCondition(value)} + onBlur={(e) => { + this.updateCondition(this.state.detectionObj.condition); + }} + data-test-subj={'rule_detection_field'} /> diff --git a/public/utils/validation.ts b/public/utils/validation.ts index 2dc9c306f..63d5b0bd6 100644 --- a/public/utils/validation.ts +++ b/public/utils/validation.ts @@ -14,10 +14,10 @@ export const LOG_TYPE_NAME_REGEX = new RegExp(/^[a-z0-9_-]{2,50}$/); // This regex pattern support MIN to MAX character limit, capital and lowercase letters, // numbers 0-9, hyphens, dot, and underscores. -export const DETECTION_NAME_REGEX = new RegExp(/^[a-zA-Z0-9_.-]{5,50}$/); +export const DETECTION_NAME_REGEX = new RegExp(/^[a-zA-Z0-9_.-]{1,50}$/); export const DETECTION_CONDITION_REGEX = new RegExp( - /^((not )?[a-zA-Z0-9_]+)?( (and|or|and not|or not|not) ?([a-zA-Z0-9_]+))*(? Date: Thu, 16 Nov 2023 04:36:15 -0800 Subject: [PATCH 2/3] updated snapshots Signed-off-by: Amardeepsingh Siglani --- babel.config.js | 4 +- package.json | 2 + .../DetectionVisualEditor.test.tsx.snap | 250 ++++++++++++++---- .../RuleEditorContainer.test.tsx.snap | 250 ++++++++++++++---- .../RuleEditorForm.test.tsx.snap | 250 ++++++++++++++---- 5 files changed, 592 insertions(+), 164 deletions(-) diff --git a/babel.config.js b/babel.config.js index 838132a9b..449368657 100644 --- a/babel.config.js +++ b/babel.config.js @@ -13,7 +13,7 @@ module.exports = { ], plugins: [ [require('@babel/plugin-transform-runtime'), { regenerator: true }], - require('@babel/plugin-proposal-class-properties'), - require('@babel/plugin-proposal-object-rest-spread'), + require('@babel/plugin-transform-class-properties'), + require('@babel/plugin-transform-object-rest-spread'), ], }; diff --git a/package.json b/package.json index b25b37817..86e734f51 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "@cypress/request": "^3.0.0" }, "devDependencies": { + "@babel/plugin-transform-class-properties": "^7.22.9", + "@babel/plugin-transform-object-rest-spread": "^7.22.9", "@elastic/elastic-eslint-config-kibana": "link:../../packages/opensearch-eslint-config-opensearch-dashboards", "@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards", "@testing-library/dom": "^8.11.3", diff --git a/public/pages/Rules/components/RuleEditor/__snapshots__/DetectionVisualEditor.test.tsx.snap b/public/pages/Rules/components/RuleEditor/__snapshots__/DetectionVisualEditor.test.tsx.snap index afcdacc96..ec4313442 100644 --- a/public/pages/Rules/components/RuleEditor/__snapshots__/DetectionVisualEditor.test.tsx.snap +++ b/public/pages/Rules/components/RuleEditor/__snapshots__/DetectionVisualEditor.test.tsx.snap @@ -333,7 +333,8 @@ Object { class="euiSpacer euiSpacer--m" />
+