Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

import moment from 'moment';
import { DataSourceType } from '../../../../detections/pages/detection_engine/rules/types';
import { isNoisy, getTimeframeOptions, getIsRulePreviewDisabled } from './helpers';
import {
isNoisy,
getTimeframeOptions,
getIsRulePreviewDisabled,
isEveryThresholdFieldValid,
} from './helpers';
import type { FieldsMap } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';

describe('query_preview/helpers', () => {
const timeframeEnd = moment();
Expand Down Expand Up @@ -90,6 +96,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: [],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -109,6 +116,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: false,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -128,6 +136,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -147,6 +156,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -166,6 +176,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -183,6 +194,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -200,6 +212,7 @@ describe('query_preview/helpers', () => {
ruleType: 'eql',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -217,6 +230,7 @@ describe('query_preview/helpers', () => {
ruleType: 'new_terms',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -234,6 +248,7 @@ describe('query_preview/helpers', () => {
ruleType: 'threat_match',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -253,6 +268,7 @@ describe('query_preview/helpers', () => {
ruleType: 'eql',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -274,6 +290,7 @@ describe('query_preview/helpers', () => {
ruleType: 'eql',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand Down Expand Up @@ -304,6 +321,7 @@ describe('query_preview/helpers', () => {
ruleType: 'eql',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -325,6 +343,7 @@ describe('query_preview/helpers', () => {
ruleType: 'eql',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -347,6 +366,7 @@ describe('query_preview/helpers', () => {
ruleType: 'machine_learning',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: [],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
Expand All @@ -358,6 +378,90 @@ describe('query_preview/helpers', () => {
});
expect(isDisabled).toEqual(false);
});

test('disabled for ML rule when machine learning job id is empty', () => {
const isDisabled = getIsRulePreviewDisabled({
ruleType: 'machine_learning',
isQueryBarValid: true,
isThreatQueryBarValid: true,
isThresholdValid: false,
index: [],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
threatIndex: [],
threatMapping: [],
machineLearningJobId: [],
queryBar: { filters: [], query: { query: '', language: '' }, saved_id: null },
newTermsFields: [],
});
expect(isDisabled).toEqual(true);
});

test('enabled when threshold rule with non empty query', () => {
const isDisabled = getIsRulePreviewDisabled({
ruleType: 'threshold',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: true,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
threatIndex: [],
threatMapping: [],
machineLearningJobId: [],
queryBar: {
filters: [],
query: { query: 'any where true', language: 'eql' },
saved_id: null,
},
newTermsFields: [],
});
expect(isDisabled).toEqual(false);
});

test('disabled when threshold rule with empty query', () => {
const isDisabled = getIsRulePreviewDisabled({
ruleType: 'threshold',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: true,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
threatIndex: [],
threatMapping: [],
machineLearningJobId: [],
queryBar: {
filters: [],
query: { query: '', language: 'eql' },
saved_id: null,
},
newTermsFields: [],
});
expect(isDisabled).toEqual(true);
});

test('disabled when threshold rule with invalid threshold', () => {
const isDisabled = getIsRulePreviewDisabled({
ruleType: 'threshold',
isQueryBarValid: true,
isThreatQueryBarValid: false,
isThresholdValid: false,
index: ['test-*'],
dataViewId: undefined,
dataSourceType: DataSourceType.IndexPatterns,
threatIndex: [],
threatMapping: [],
machineLearningJobId: [],
queryBar: {
filters: [],
query: { query: 'any where true', language: 'eql' },
saved_id: null,
},
newTermsFields: [],
});
expect(isDisabled).toEqual(true);
});
});

describe('getTimeframeOptions', () => {
Expand Down Expand Up @@ -386,4 +490,33 @@ describe('query_preview/helpers', () => {
expect(options).toEqual([{ value: 'h', text: 'Last hour' }]);
});
});

describe('isEveryThresholdFieldValud', () => {
const fieldLabels = [
'threshold.field',
'threshold.value',
'threshold.cardinality.field',
'threshold.cardinality.value',
];
const allFieldsValid = fieldLabels.reduce((acc, label) => {
acc[label] = { isValid: true };
return acc;
}, {} as Record<string, { isValid: boolean }>) as unknown as FieldsMap;

test('returns true if all fields are valid', () => {
const isValid = isEveryThresholdFieldValid(allFieldsValid);

expect(isValid).toEqual(true);
});

test.each(fieldLabels)('returns false if a field is invalid', (fieldLabel) => {
const fields = {
...allFieldsValid,
// Override the field that should be invalid for this test case
[fieldLabel]: { isValid: false },
} as unknown as FieldsMap;

expect(isEveryThresholdFieldValid(fields)).toEqual(false);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import { isEmpty } from 'lodash';
import type { EuiSelectOption } from '@elastic/eui';
import type { Type, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import type { FieldsMap } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import * as i18n from './translations';

import type { FieldValueQueryBar } from '../query_bar_field';
Expand Down Expand Up @@ -96,10 +97,17 @@ const isThreatMatchPreviewDisabled = ({
return false;
};

export const isEveryThresholdFieldValid = (fields: FieldsMap): boolean =>
fields['threshold.field']?.isValid &&
fields['threshold.value']?.isValid &&
fields['threshold.cardinality.field']?.isValid &&
fields['threshold.cardinality.value']?.isValid;

export const getIsRulePreviewDisabled = ({
ruleType,
isQueryBarValid,
isThreatQueryBarValid,
isThresholdValid,
index,
dataViewId,
dataSourceType,
Expand All @@ -112,6 +120,7 @@ export const getIsRulePreviewDisabled = ({
ruleType: Type;
isQueryBarValid: boolean;
isThreatQueryBarValid: boolean;
isThresholdValid: boolean;
index: string[];
dataViewId: string | undefined;
dataSourceType: DataSourceType;
Expand All @@ -125,7 +134,7 @@ export const getIsRulePreviewDisabled = ({
return isEsqlPreviewDisabled({ isQueryBarValid, queryBar });
}
if (ruleType === 'machine_learning') {
return !machineLearningJobId ?? machineLearningJobId?.length === 0;
return machineLearningJobId === undefined || machineLearningJobId.length === 0;
}
if (
!isQueryBarValid ||
Expand All @@ -145,7 +154,10 @@ export const getIsRulePreviewDisabled = ({
return isEmpty(queryBar.query.query);
}
if (ruleType === 'query' || ruleType === 'threshold') {
return isEmpty(queryBar.query.query) && isEmpty(queryBar.filters);
return (
(isEmpty(queryBar.query.query) && isEmpty(queryBar.filters)) ||
(ruleType === 'threshold' && !isThresholdValid)
);
}
if (ruleType === 'new_terms') {
return isNewTermsPreviewDisabled(newTermsFields);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ import {
} from '../../../../../common/constants';
import { useKibana, useUiSetting$ } from '../../../../common/lib/kibana';
import { RulePreview } from '../../components/rule_preview';
import { getIsRulePreviewDisabled } from '../../components/rule_preview/helpers';
import {
getIsRulePreviewDisabled,
isEveryThresholdFieldValid,
} from '../../components/rule_preview/helpers';
import { useStartMlJobs } from '../../../rule_management/logic/use_start_ml_jobs';
import { VALIDATION_WARNING_CODE_FIELD_NAME_MAP } from '../../../rule_creation/constants/validation_warning_codes';
import { extractValidationMessages } from '../../../rule_creation/logic/extract_validation_messages';
Expand Down Expand Up @@ -227,6 +230,7 @@ const CreateRulePageComponent: React.FC = () => {
defineStepFormFields.threatIndex?.isValid &&
defineStepFormFields.threatQueryBar?.isValid &&
defineStepFormFields.threatMapping?.isValid,
isThresholdValid: isEveryThresholdFieldValid(defineStepFormFields),
index: memoizedIndex,
dataViewId: defineStepData.dataViewId,
dataSourceType: defineStepData.dataSourceType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import { useConfirmValidationErrorsModal } from '../../../../common/hooks/use_co
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
import { isEsqlRule } from '../../../../../common/detection_engine/utils';
import { RulePreview } from '../../components/rule_preview';
import { getIsRulePreviewDisabled } from '../../components/rule_preview/helpers';
import {
getIsRulePreviewDisabled,
isEveryThresholdFieldValid,
} from '../../components/rule_preview/helpers';
import type {
RuleResponse,
RuleUpdateProps,
Expand Down Expand Up @@ -164,6 +167,7 @@ const EditRulePageComponent: FC<{ rule: RuleResponse }> = ({ rule }) => {
defineStepFormFields.threatIndex?.isValid &&
defineStepFormFields.threatQueryBar?.isValid &&
defineStepFormFields.threatMapping?.isValid,
isThresholdValid: isEveryThresholdFieldValid(defineStepFormFields),
index: memoizedIndex,
dataViewId: defineStepData.dataViewId,
dataSourceType: defineStepData.dataSourceType,
Expand Down