diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json
index 6c6c7f8516d5..b9fdd88d2d64 100644
--- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json
+++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json
@@ -37785,7 +37785,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.multiSelectFields.placeholderText": "Sélectionner un champ",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.placeholderText": "Sélectionner un champ",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsLabel": "Champs",
- "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsMin": "Au moins un champ est requis.",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.referencesUrlInvalidError": "Le format de l’URL n’est pas valide.",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.resetDefaultIndicesButton": "Réinitialiser sur les modèles d'indexation par défaut",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.rulePreviewTitle": "Aperçu de la règle",
@@ -39091,7 +39090,6 @@
"xpack.securitySolution.detectionEngine.userUnauthenticatedMsgBody": "Vous ne disposez pas des autorisations requises pour visualiser le moteur de détection. Pour une aide supplémentaire, contactez votre administrateur.",
"xpack.securitySolution.detectionEngine.userUnauthenticatedTitle": "Autorisations de moteur de détection requises",
"xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errMin": "La taille de la fenêtre d'historique doit être supérieure à 0.",
- "xpack.securitySolution.detectionEngine.validations.stepDefineRule.newTermsFieldsMax": "Le nombre de champs doit être de 3 au maximum.",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityFieldFieldData.thresholdCardinalityFieldNotSuppliedMessage": "Un champ Cardinalité est requis.",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityValueFieldData.numberGreaterThanOrEqualOneErrorMessage": "La valeur doit être supérieure ou égale à un.",
"xpack.securitySolution.detectionEngine.validations.thresholdFieldFieldData.arrayLengthGreaterThanMaxErrorMessage": "Le nombre de champs doit être de 3 au maximum.",
diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json
index 569228e7d408..caca8084d790 100644
--- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json
+++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json
@@ -37643,7 +37643,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.multiSelectFields.placeholderText": "フィールドを選択",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.placeholderText": "フィールドを選択",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsLabel": "フィールド",
- "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsMin": "1つ以上のフィールドが必要です。",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.referencesUrlInvalidError": "URLの形式が無効です",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.resetDefaultIndicesButton": "デフォルトインデックスパターンにリセット",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.rulePreviewTitle": "ルールプレビュー",
@@ -38948,7 +38947,6 @@
"xpack.securitySolution.detectionEngine.userUnauthenticatedMsgBody": "検出エンジンを表示するための必要なアクセス権がありません。ヘルプについては、管理者にお問い合わせください。",
"xpack.securitySolution.detectionEngine.userUnauthenticatedTitle": "検出エンジンアクセス権が必要です",
"xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errMin": "履歴ウィンドウサイズは0よりも大きい値でなければなりません。",
- "xpack.securitySolution.detectionEngine.validations.stepDefineRule.newTermsFieldsMax": "フィールド数は3以下でなければなりません。",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityFieldFieldData.thresholdCardinalityFieldNotSuppliedMessage": "カーディナリティフィールドは必須です。",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityValueFieldData.numberGreaterThanOrEqualOneErrorMessage": "値は 1 以上でなければなりません。",
"xpack.securitySolution.detectionEngine.validations.thresholdFieldFieldData.arrayLengthGreaterThanMaxErrorMessage": "フィールド数は3以下でなければなりません。",
diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json
index 611dbf21d2a4..8fe52666e960 100644
--- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json
+++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json
@@ -37071,7 +37071,6 @@
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.multiSelectFields.placeholderText": "选择字段",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.placeholderText": "选择字段",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsLabel": "字段",
- "xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsMin": "至少需要一个字段。",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.referencesUrlInvalidError": "URL 的格式无效",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.resetDefaultIndicesButton": "重置为默认索引模式",
"xpack.securitySolution.detectionEngine.createRule.stepDefineRule.rulePreviewTitle": "规则预览",
@@ -38371,7 +38370,6 @@
"xpack.securitySolution.detectionEngine.userUnauthenticatedMsgBody": "您没有所需的权限,无法查看检测引擎。若需要更多帮助,请联系您的管理员。",
"xpack.securitySolution.detectionEngine.userUnauthenticatedTitle": "需要检测引擎权限",
"xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errMin": "历史记录窗口大小必须大于 0。",
- "xpack.securitySolution.detectionEngine.validations.stepDefineRule.newTermsFieldsMax": "字段数目不得超过 3 个。",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityFieldFieldData.thresholdCardinalityFieldNotSuppliedMessage": "基数字段必填。",
"xpack.securitySolution.detectionEngine.validations.thresholdCardinalityValueFieldData.numberGreaterThanOrEqualOneErrorMessage": "值必须大于或等于 1。",
"xpack.securitySolution.detectionEngine.validations.thresholdFieldFieldData.arrayLengthGreaterThanMaxErrorMessage": "字段数目不得超过 3 个。",
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_ml_rule_config.ts b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_ml_rule_config.ts
index 139acb0473c8..9b60c15dc135 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_ml_rule_config.ts
+++ b/x-pack/plugins/security_solution/public/common/components/ml/hooks/use_ml_rule_config.ts
@@ -5,16 +5,15 @@
* 2.0.
*/
-import { useMemo } from 'react';
import type { DataViewFieldBase } from '@kbn/es-query';
import type { FieldSpec } from '@kbn/data-plugin/common';
-import { getTermsAggregationFields } from '../../../../detection_engine/rule_creation_ui/components/step_define_rule/utils';
import { useRuleFields } from '../../../../detection_engine/rule_management/logic/use_rule_fields';
import { useMlCapabilities } from './use_ml_capabilities';
import { useMlRuleValidations } from './use_ml_rule_validations';
import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
import { hasMlLicense } from '../../../../../common/machine_learning/has_ml_license';
+import { useTermsAggregationFields } from '../../../hooks/use_terms_aggregation_fields';
export interface UseMlRuleConfigReturn {
hasMlAdminPermissions: boolean;
@@ -45,10 +44,7 @@ export const useMLRuleConfig = ({
const { loading: mlFieldsLoading, fields: mlFields } = useRuleFields({
machineLearningJobId,
});
- const mlSuppressionFields = useMemo(
- () => getTermsAggregationFields(mlFields as FieldSpec[]),
- [mlFields]
- );
+ const mlSuppressionFields = useTermsAggregationFields(mlFields);
return {
hasMlAdminPermissions: hasMlAdminPermissions(mlCapabilities),
diff --git a/x-pack/plugins/security_solution/public/common/constants.ts b/x-pack/plugins/security_solution/public/common/constants.ts
index c114f70915a7..a9761e87c20e 100644
--- a/x-pack/plugins/security_solution/public/common/constants.ts
+++ b/x-pack/plugins/security_solution/public/common/constants.ts
@@ -18,3 +18,5 @@ export const RISK_SCORE_HIGH = 73;
export const RISK_SCORE_CRITICAL = 99;
export const ONBOARDING_VIDEO_SOURCE = '//play.vidyard.com/K6kKDBbP9SpXife9s2tHNP.html?';
+
+export const DEFAULT_HISTORY_WINDOW_SIZE = '7d';
diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_terms_aggregation_fields.ts b/x-pack/plugins/security_solution/public/common/hooks/use_terms_aggregation_fields.ts
new file mode 100644
index 000000000000..e62b16e82676
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/hooks/use_terms_aggregation_fields.ts
@@ -0,0 +1,29 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useMemo } from 'react';
+import type { FieldSpec } from '@kbn/data-views-plugin/common';
+import type { DataViewFieldBase } from '@kbn/es-query';
+import { getTermsAggregationFields } from '../../detection_engine/rule_creation_ui/components/step_define_rule/utils';
+
+export function useTermsAggregationFields(fields?: DataViewFieldBase[]) {
+ const termsAggregationFields = useMemo(
+ /**
+ * Typecasting to FieldSpec because fields is
+ * typed as DataViewFieldBase[] which does not have
+ * the 'aggregatable' property, however the type is incorrect
+ *
+ * fields does contain elements with the aggregatable property.
+ * We will need to determine where these types are defined and
+ * figure out where the discrepancy is.
+ */
+ () => getTermsAggregationFields((fields as FieldSpec[]) ?? []),
+ [fields]
+ );
+
+ return termsAggregationFields;
+}
diff --git a/x-pack/plugins/security_solution/public/common/utils/date_math.ts b/x-pack/plugins/security_solution/public/common/utils/date_math.ts
new file mode 100644
index 000000000000..28dbe15955a2
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/utils/date_math.ts
@@ -0,0 +1,29 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+/**
+ * Converts a date math string to a duration string by removing the 'now-' prefix.
+ *
+ * @param historyStart - Date math string to convert. For example, "now-30d".
+ * @returns Resulting duration string. For example, "30d".
+ */
+export const convertDateMathToDuration = (dateMathString: string): string => {
+ if (dateMathString.startsWith('now-')) {
+ return dateMathString.substring(4);
+ }
+
+ return dateMathString;
+};
+
+/**
+ * Converts a duration string to a dateMath string by adding the 'now-' prefix.
+ *
+ * @param durationString - Duration string to convert. For example, "30d".
+ * @returns Resulting date math string. For example, "now-30d".
+ */
+export const convertDurationToDateMath = (durationString: string): string =>
+ `now-${durationString}`;
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/history_window_start_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/history_window_start_edit.tsx
new file mode 100644
index 000000000000..c876fe926ce4
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/history_window_start_edit.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { ScheduleItemField } from '../schedule_item_field';
+import { type FieldConfig, UseField } from '../../../../shared_imports';
+import { type HistoryWindowStart } from '../../../../../common/api/detection_engine';
+import * as i18n from './translations';
+import { validateHistoryWindowStart } from './validate_history_window_start';
+
+const COMPONENT_PROPS = {
+ idAria: 'historyWindowSize',
+ dataTestSubj: 'historyWindowSize',
+ timeTypes: ['m', 'h', 'd'],
+};
+
+interface HistoryWindowStartEditProps {
+ path: string;
+}
+
+export function HistoryWindowStartEdit({ path }: HistoryWindowStartEditProps): JSX.Element {
+ return (
+
+ );
+}
+
+const HISTORY_WINDOW_START_FIELD_CONFIG: FieldConfig = {
+ label: i18n.HISTORY_WINDOW_START_LABEL,
+ helpText: i18n.HELP_TEXT,
+ validations: [
+ {
+ validator: validateHistoryWindowStart,
+ },
+ ],
+};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/index.tsx
similarity index 51%
rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/translations.ts
rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/index.tsx
index 1bf73b4a46b4..a904f5d42771 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/translations.ts
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/index.tsx
@@ -5,11 +5,4 @@
* 2.0.
*/
-import { i18n } from '@kbn/i18n';
-
-export const NEW_TERMS_FIELD_PLACEHOLDER = i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.placeholderText',
- {
- defaultMessage: 'Select a field',
- }
-);
+export { HistoryWindowStartEdit } from './history_window_start_edit';
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/translations.ts
new file mode 100644
index 000000000000..75ff11fe5e1b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/translations.ts
@@ -0,0 +1,36 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const HISTORY_WINDOW_START_LABEL = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.historyWindowSizeLabel',
+ {
+ defaultMessage: 'History Window Size',
+ }
+);
+
+export const HELP_TEXT = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.historyWindowSizeHelpText',
+ {
+ defaultMessage: "New terms rules only alert if terms don't appear in historical data.",
+ }
+);
+
+export const MUST_BE_POSITIVE_INTEGER_VALIDATION_ERROR = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errNumber',
+ {
+ defaultMessage: 'History window size must be a positive number.',
+ }
+);
+
+export const MUST_BE_GREATER_THAN_ZERO_VALIDATION_ERROR = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errMin',
+ {
+ defaultMessage: 'History window size must be greater than 0.',
+ }
+);
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/validate_history_window_start.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/validate_history_window_start.ts
new file mode 100644
index 000000000000..4e274683ad08
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/history_window_start_edit/validate_history_window_start.ts
@@ -0,0 +1,31 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { type ValidationFunc } from '../../../../shared_imports';
+import * as i18n from './translations';
+
+export function validateHistoryWindowStart(...args: Parameters) {
+ const [{ path, value }] = args;
+
+ const historyWindowSize = Number.parseInt(String(value), 10);
+
+ if (Number.isNaN(historyWindowSize)) {
+ return {
+ code: 'ERR_NOT_INT_NUMBER',
+ path,
+ message: i18n.MUST_BE_POSITIVE_INTEGER_VALIDATION_ERROR,
+ };
+ }
+
+ if (historyWindowSize <= 0) {
+ return {
+ code: 'ERR_MIN_LENGTH',
+ path,
+ message: i18n.MUST_BE_GREATER_THAN_ZERO_VALIDATION_ERROR,
+ };
+ }
+}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/index.tsx
new file mode 100644
index 000000000000..22c3c9e9047c
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/index.tsx
@@ -0,0 +1,8 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { NewTermsFieldsEdit } from './new_terms_fields_edit';
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_edit.tsx
new file mode 100644
index 000000000000..f50c62ed8f7a
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_edit.tsx
@@ -0,0 +1,43 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { memo } from 'react';
+import { UseField, fieldValidators } from '../../../../shared_imports';
+import { NewTermsFieldsField } from './new_terms_fields_field';
+import * as i18n from './translations';
+
+interface NewTermsFieldsEditProps {
+ path: string;
+ fieldNames: string[];
+}
+
+export const NewTermsFieldsEdit = memo(function NewTermsFieldsEdit({
+ path,
+ fieldNames,
+}: NewTermsFieldsEditProps): JSX.Element {
+ return (
+
+ );
+});
+
+const NEW_TERMS_FIELDS_CONFIG = {
+ label: i18n.NEW_TERMS_FIELDS_LABEL,
+ helpText: i18n.HELP_TEXT,
+ validations: [
+ {
+ validator: fieldValidators.emptyField(i18n.MIN_FIELDS_COUNT_VALIDATION_ERROR),
+ },
+ {
+ validator: fieldValidators.maxLengthField(i18n.MAX_FIELDS_COUNT_VALIDATION_ERROR),
+ },
+ ],
+};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_field.tsx
new file mode 100644
index 000000000000..a9c1148402f1
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/new_terms_fields_field.tsx
@@ -0,0 +1,40 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { memo } from 'react';
+
+import type { DataViewFieldBase } from '@kbn/es-query';
+import { ComboBoxField } from '@kbn/es-ui-shared-plugin/static/forms/components';
+import type { FieldHook } from '../../../../shared_imports';
+import { PLACEHOLDER } from './translations';
+
+interface NewTermsFieldsProps {
+ fieldNames: DataViewFieldBase[];
+ field: FieldHook;
+}
+
+const FIELD_COMBO_BOX_WIDTH = 410;
+
+const fieldDescribedByIds = 'newTermsFieldEdit';
+
+export const NewTermsFieldsField = memo(function NewTermsFieldsField({
+ fieldNames,
+ field,
+}: NewTermsFieldsProps): JSX.Element {
+ const fieldEuiFieldProps = {
+ fullWidth: true,
+ noSuggestions: false,
+ options: fieldNames.map((name) => ({ label: name })),
+ placeholder: PLACEHOLDER,
+ onCreateOption: undefined,
+ style: { width: `${FIELD_COMBO_BOX_WIDTH}px` },
+ };
+
+ return (
+
+ );
+});
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/translations.ts
new file mode 100644
index 000000000000..4eb18b9a318e
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/new_terms_fields_edit/translations.ts
@@ -0,0 +1,47 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { MAX_NUMBER_OF_NEW_TERMS_FIELDS } from '../../../../../common/constants';
+
+export const NEW_TERMS_FIELDS_LABEL = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsLabel',
+ {
+ defaultMessage: 'Fields',
+ }
+);
+
+export const PLACEHOLDER = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.placeholderText',
+ {
+ defaultMessage: 'Select a field',
+ }
+);
+
+export const HELP_TEXT = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldNewTermsFieldHelpText',
+ {
+ defaultMessage: 'Select a field to check for new terms.',
+ }
+);
+
+export const MIN_FIELDS_COUNT_VALIDATION_ERROR = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsField.minFieldsCountError',
+ {
+ defaultMessage: 'A minimum of one field is required.',
+ }
+);
+
+export const MAX_FIELDS_COUNT_VALIDATION_ERROR = {
+ length: MAX_NUMBER_OF_NEW_TERMS_FIELDS,
+ message: i18n.translate(
+ 'xpack.securitySolution.detectionEngine.validations.stepDefineRule.newTermsField.maxFieldsCountError',
+ {
+ defaultMessage: 'Number of fields must be 3 or less.',
+ }
+ ),
+};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/index.ts
new file mode 100644
index 000000000000..f2458bbb5a4e
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/index.ts
@@ -0,0 +1,8 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export { ScheduleItemField } from './schedule_item_field';
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_form/index.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/schedule_item_field.test.tsx
similarity index 92%
rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_form/index.test.tsx
rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/schedule_item_field.test.tsx
index 057ecbdae5fc..bf019bd43f2f 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_form/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/schedule_item_field.test.tsx
@@ -8,14 +8,14 @@
import React from 'react';
import { mount, shallow } from 'enzyme';
-import { ScheduleItem } from '.';
+import { ScheduleItemField } from './schedule_item_field';
import { TestProviders, useFormFieldMock } from '../../../../common/mock';
-describe('ScheduleItem', () => {
+describe('ScheduleItemField', () => {
it('renders correctly', () => {
const mockField = useFormFieldMock();
const wrapper = shallow(
- {
const mockField = useFormFieldMock();
const wrapper = mount(
- {
const mockField = useFormFieldMock();
const wrapper = mount(
- {
const mockField = useFormFieldMock();
const wrapper = mount(
- {
}
};
-export const ScheduleItem = ({
+export const ScheduleItemField = ({
dataTestSubj,
field,
idAria,
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_form/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/translations.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_form/translations.ts
rename to x-pack/plugins/security_solution/public/detection_engine/rule_creation/components/schedule_item_field/translations.ts
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx
index 26c81f325ea1..a91487afc448 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/description_step/index.tsx
@@ -72,6 +72,8 @@ import {
ALERT_SUPPRESSION_MISSING_FIELDS_FIELD_NAME,
} from '../../../rule_creation/components/alert_suppression_edit';
import { THRESHOLD_ALERT_SUPPRESSION_ENABLED } from '../../../rule_creation/components/threshold_alert_suppression_edit';
+import { NEW_TERMS_FIELDS_LABEL } from '../../../rule_creation/components/new_terms_fields_edit/translations';
+import { HISTORY_WINDOW_START_LABEL } from '../../../rule_creation/components/history_window_start_edit/translations';
import type { FieldValueQueryBar } from '../query_bar_field';
const DescriptionListContainer = styled(EuiDescriptionList)`
@@ -341,6 +343,9 @@ export const getDescriptionItem = (
} else if (field === 'threatMapping') {
const threatMap: ThreatMapping = get(field, data);
return buildThreatMappingDescription(label, threatMap);
+ } else if (field === 'newTermsFields') {
+ const values: string[] = get(field, data);
+ return buildStringArrayDescription(NEW_TERMS_FIELDS_LABEL, field, values);
} else if (Array.isArray(get(field, data)) && field !== 'threatMapping') {
const values: string[] = get(field, data);
return buildStringArrayDescription(label, field, values);
@@ -357,6 +362,9 @@ export const getDescriptionItem = (
} else if (field === 'maxSignals') {
const value: number | undefined = get(field, data);
return value ? [{ title: label, description: value }] : [];
+ } else if (field === 'historyWindowSize') {
+ const value: number = get(field, data);
+ return value ? [{ title: HISTORY_WINDOW_START_LABEL, description: value }] : [];
}
const description: string = get(field, data);
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/index.tsx
deleted file mode 100644
index 7dfb24200e64..000000000000
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/new_terms_fields/index.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React, { useMemo } from 'react';
-
-import type { DataViewFieldBase } from '@kbn/es-query';
-import type { FieldHook } from '../../../../shared_imports';
-import { Field } from '../../../../shared_imports';
-import { NEW_TERMS_FIELD_PLACEHOLDER } from './translations';
-
-interface NewTermsFieldsProps {
- browserFields: DataViewFieldBase[];
- field: FieldHook;
-}
-
-const FIELD_COMBO_BOX_WIDTH = 410;
-
-const fieldDescribedByIds = 'detectionEngineStepDefineRuleNewTermsField';
-
-export const NewTermsFieldsComponent: React.FC = ({
- browserFields,
- field,
-}: NewTermsFieldsProps) => {
- const fieldEuiFieldProps = useMemo(
- () => ({
- fullWidth: true,
- noSuggestions: false,
- options: browserFields.map((browserField) => ({ label: browserField.name })),
- placeholder: NEW_TERMS_FIELD_PLACEHOLDER,
- onCreateOption: undefined,
- style: { width: `${FIELD_COMBO_BOX_WIDTH}px` },
- }),
- [browserFields]
- );
- return ;
-};
-
-export const NewTermsFields = React.memo(NewTermsFieldsComponent);
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/helpers.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/helpers.ts
index 045e957c4e12..fcbdfdf4f86b 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/helpers.ts
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/helpers.ts
@@ -107,7 +107,7 @@ export const getIsRulePreviewDisabled = ({
threatMapping,
machineLearningJobId,
queryBar,
- newTermsFields,
+ newTermsFields = [],
}: {
ruleType: Type;
isQueryBarValid: boolean;
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx
index 7b056b596979..f7845c31378a 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/index.tsx
@@ -55,7 +55,6 @@ import {
} from '../../../../shared_imports';
import type { FormHook, FieldHook } from '../../../../shared_imports';
import { schema } from './schema';
-import { getTermsAggregationFields } from './utils';
import { useExperimentalFeatureFieldsTransform } from './use_experimental_feature_fields_transform';
import * as i18n from './translations';
import {
@@ -72,8 +71,6 @@ import { EqlQueryEdit } from '../../../rule_creation/components/eql_query_edit';
import { DataViewSelectorField } from '../data_view_selector_field';
import { ThreatMatchInput } from '../threatmatch_input';
import { useFetchIndex } from '../../../../common/containers/source';
-import { NewTermsFields } from '../new_terms_fields';
-import { ScheduleItem } from '../../../rule_creation/components/schedule_item_form';
import { RequiredFields } from '../../../rule_creation/components/required_fields';
import { DocLink } from '../../../../common/components/links_to_docs/doc_link';
import { useLicense } from '../../../../common/hooks/use_license';
@@ -90,6 +87,10 @@ import {
} from '../../../rule_creation/components/alert_suppression_edit';
import { ThresholdAlertSuppressionEdit } from '../../../rule_creation/components/threshold_alert_suppression_edit';
import { usePersistentAlertSuppressionState } from './use_persistent_alert_suppression_state';
+import { useTermsAggregationFields } from '../../../../common/hooks/use_terms_aggregation_fields';
+import { HistoryWindowStartEdit } from '../../../rule_creation/components/history_window_start_edit';
+import { NewTermsFieldsEdit } from '../../../rule_creation/components/new_terms_fields_edit';
+import { usePersistentNewTermsState } from './use_persistent_new_terms_state';
import { EsqlQueryEdit } from '../../../rule_creation/components/esql_query_edit';
import { usePersistentQuery } from './use_persistent_query';
@@ -211,18 +212,11 @@ const StepDefineRuleComponent: FC = ({
() => (indexPattern.fields as FieldSpec[]).filter((field) => field.aggregatable === true),
[indexPattern.fields]
);
- const termsAggregationFields = useMemo(
- /**
- * Typecasting to FieldSpec because fields is
- * typed as DataViewFieldBase[] which does not have
- * the 'aggregatable' property, however the type is incorrect
- *
- * fields does contain elements with the aggregatable property.
- * We will need to determine where these types are defined and
- * figure out where the discrepency is.
- */
- () => getTermsAggregationFields(indexPattern.fields as FieldSpec[]),
- [indexPattern.fields]
+
+ const termsAggregationFields = useTermsAggregationFields(indexPattern.fields);
+ const termsAggregationFieldNames = useMemo(
+ () => termsAggregationFields.map((field) => field.name),
+ [termsAggregationFields]
);
const [threatIndexPatternsLoading, { indexPatterns: threatIndexPatterns }] =
@@ -245,6 +239,12 @@ const StepDefineRuleComponent: FC = ({
form,
});
usePersistentAlertSuppressionState({ form });
+ usePersistentNewTermsState({
+ form,
+ ruleTypePath: 'ruleType',
+ newTermsFieldsPath: 'newTermsFields',
+ historyWindowStartPath: 'historyWindowSize',
+ });
const handleSetRuleFromTimeline = useCallback(
({ index: timelineIndex, queryBar: timelineQueryBar, eqlOptions }) => {
@@ -730,30 +730,14 @@ const StepDefineRuleComponent: FC = ({
>
-
- <>
-
-
- >
-
+ {isNewTermsRule(ruleType) && (
+
+ <>
+
+
+ >
+
+ )}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/schema.tsx
index 323e321eee8d..4f168d94388d 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/schema.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/schema.tsx
@@ -17,12 +17,10 @@ import {
} from '../../../../common/components/threat_match/helpers';
import {
isEsqlRule,
- isNewTermsRule,
isThreatMatchRule,
isThresholdRule,
isSuppressionRuleConfiguredWithGroupBy,
} from '../../../../../common/detection_engine/utils';
-import { MAX_NUMBER_OF_NEW_TERMS_FIELDS } from '../../../../../common/constants';
import { isMlRule } from '../../../../../common/machine_learning/helpers';
import type { ERROR_CODE, FormSchema, ValidationFunc } from '../../../../shared_imports';
import { FIELD_TYPES, fieldValidators } from '../../../../shared_imports';
@@ -478,106 +476,8 @@ export const schema: FormSchema = {
},
],
},
- newTermsFields: {
- type: FIELD_TYPES.COMBO_BOX,
- label: i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsLabel',
- {
- defaultMessage: 'Fields',
- }
- ),
- helpText: i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldNewTermsFieldHelpText',
- {
- defaultMessage: 'Select a field to check for new terms.',
- }
- ),
- validations: [
- {
- validator: (
- ...args: Parameters
- ): ReturnType> | undefined => {
- const [{ formData }] = args;
- const needsValidation = isNewTermsRule(formData.ruleType);
- if (!needsValidation) {
- return;
- }
-
- return fieldValidators.emptyField(
- i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.newTermsFieldsMin',
- {
- defaultMessage: 'A minimum of one field is required.',
- }
- )
- )(...args);
- },
- },
- {
- validator: (
- ...args: Parameters
- ): ReturnType> | undefined => {
- const [{ formData }] = args;
- const needsValidation = isNewTermsRule(formData.ruleType);
- if (!needsValidation) {
- return;
- }
- return fieldValidators.maxLengthField({
- length: MAX_NUMBER_OF_NEW_TERMS_FIELDS,
- message: i18n.translate(
- 'xpack.securitySolution.detectionEngine.validations.stepDefineRule.newTermsFieldsMax',
- {
- defaultMessage: 'Number of fields must be 3 or less.',
- }
- ),
- })(...args);
- },
- },
- ],
- },
- historyWindowSize: {
- label: i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.historyWindowSizeLabel',
- {
- defaultMessage: 'History Window Size',
- }
- ),
- helpText: i18n.translate(
- 'xpack.securitySolution.detectionEngine.createRule.stepScheduleRule.historyWindowSizeHelpText',
- {
- defaultMessage: "New terms rules only alert if terms don't appear in historical data.",
- }
- ),
- validations: [
- {
- validator: (
- ...args: Parameters
- ): ReturnType> | undefined => {
- const [{ path, formData }] = args;
- const needsValidation = isNewTermsRule(formData.ruleType);
-
- if (!needsValidation) {
- return;
- }
-
- const filterTimeVal = formData.historyWindowSize.match(/\d+/g);
-
- if (filterTimeVal <= 0) {
- return {
- code: 'ERR_MIN_LENGTH',
- path,
- message: i18n.translate(
- 'xpack.securitySolution.detectionEngine.validations.stepDefineRule.historyWindowSize.errMin',
- {
- defaultMessage: 'History window size must be greater than 0.',
- }
- ),
- };
- }
- },
- },
- ],
- },
+ newTermsFields: {},
+ historyWindowSize: {},
[ALERT_SUPPRESSION_FIELDS_FIELD_NAME]: {
validations: [
{
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_persistent_new_terms_state.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_persistent_new_terms_state.tsx
new file mode 100644
index 000000000000..61bfa45ba8a7
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_define_rule/use_persistent_new_terms_state.tsx
@@ -0,0 +1,73 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { useEffect, useRef } from 'react';
+import usePrevious from 'react-use/lib/usePrevious';
+import { isNewTermsRule } from '../../../../../common/detection_engine/utils';
+import type { FormHook } from '../../../../shared_imports';
+import { useFormData } from '../../../../shared_imports';
+import { type DefineStepRule } from '../../../../detections/pages/detection_engine/rules/types';
+import {
+ type NewTermsFields,
+ type HistoryWindowStart,
+} from '../../../../../common/api/detection_engine';
+
+interface UsePersistentNewTermsStateParams {
+ form: FormHook;
+ ruleTypePath: string;
+ newTermsFieldsPath: string;
+ historyWindowStartPath: string;
+}
+
+interface LastNewTermsState {
+ newTermsFields: NewTermsFields;
+ historyWindowStart: HistoryWindowStart;
+}
+
+export function usePersistentNewTermsState({
+ form,
+ ruleTypePath,
+ newTermsFieldsPath,
+ historyWindowStartPath,
+}: UsePersistentNewTermsStateParams): void {
+ const lastNewTermsState = useRef();
+ const [formData] = useFormData({ form, watch: [newTermsFieldsPath, historyWindowStartPath] });
+
+ const {
+ [ruleTypePath]: ruleType,
+ [newTermsFieldsPath]: newTermsFields,
+ [historyWindowStartPath]: historyWindowStart,
+ } = formData;
+ const previousRuleType = usePrevious(ruleType);
+
+ useEffect(() => {
+ if (
+ isNewTermsRule(ruleType) &&
+ !isNewTermsRule(previousRuleType) &&
+ lastNewTermsState.current
+ ) {
+ form.updateFieldValues({
+ [newTermsFieldsPath]: lastNewTermsState.current.newTermsFields,
+ [historyWindowStartPath]: lastNewTermsState.current.historyWindowStart,
+ });
+
+ return;
+ }
+
+ if (isNewTermsRule(ruleType)) {
+ lastNewTermsState.current = { newTermsFields, historyWindowStart };
+ }
+ }, [
+ form,
+ ruleType,
+ previousRuleType,
+ newTermsFieldsPath,
+ historyWindowStartPath,
+ newTermsFields,
+ historyWindowStart,
+ ]);
+}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_schedule_rule/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_schedule_rule/index.tsx
index 2197b069de40..7c309ed32a68 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_schedule_rule/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/step_schedule_rule/index.tsx
@@ -13,11 +13,11 @@ import type {
ScheduleStepRule,
} from '../../../../detections/pages/detection_engine/rules/types';
import { StepRuleDescription } from '../description_step';
-import { ScheduleItem } from '../../../rule_creation/components/schedule_item_form';
import { Form, UseField } from '../../../../shared_imports';
import type { FormHook } from '../../../../shared_imports';
import { StepContentWrapper } from '../../../rule_creation/components/step_content_wrapper';
import { schema } from './schema';
+import { ScheduleItemField } from '../../../rule_creation/components/schedule_item_field';
const StyledForm = styled(Form)`
max-width: 235px !important;
@@ -43,7 +43,7 @@ const StepScheduleRuleComponent: FC = ({
= ({
/>
{
const timeObj: { unit: Unit; value: number } = {
@@ -530,7 +531,7 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep
query: ruleFields.queryBar?.query?.query as string,
required_fields: requiredFields,
new_terms_fields: ruleFields.newTermsFields,
- history_window_start: `now-${ruleFields.historyWindowSize}`,
+ history_window_start: convertDurationToDateMath(ruleFields.historyWindowSize),
...alertSuppressionFields,
}
: isEsqlFields(ruleFields) && !('index' in ruleFields)
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
index 3e08f4ce3acc..487154918553 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx
@@ -43,7 +43,6 @@ import * as timelinesI18n from '../../../../timelines/components/timeline/transl
import { useRuleIndexPattern } from '../../../rule_creation_ui/pages/form';
import { DataSourceType } from '../../../../detections/pages/detection_engine/rules/types';
import type { Duration } from '../../../../detections/pages/detection_engine/rules/types';
-import { convertHistoryStartToSize } from '../../../../detections/pages/detection_engine/rules/helpers';
import { MlJobsDescription } from '../../../rule_creation/components/ml_jobs_description/ml_jobs_description';
import { MlJobLink } from '../../../rule_creation/components/ml_job_link/ml_job_link';
import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/use_security_jobs';
@@ -60,6 +59,8 @@ import {
} from './rule_definition_section.styles';
import { getQueryLanguageLabel } from './helpers';
import { useDefaultIndexPattern } from '../../hooks/use_default_index_pattern';
+import { convertDateMathToDuration } from '../../../../common/utils/date_math';
+import { DEFAULT_HISTORY_WINDOW_SIZE } from '../../../../common/constants';
import {
EQL_OPTIONS_EVENT_CATEGORY_FIELD_LABEL,
EQL_OPTIONS_EVENT_TIEBREAKER_FIELD_LABEL,
@@ -418,7 +419,9 @@ interface HistoryWindowSizeProps {
}
export const HistoryWindowSize = ({ historyWindowStart }: HistoryWindowSizeProps) => {
- const size = historyWindowStart ? convertHistoryStartToSize(historyWindowStart) : '7d';
+ const size = historyWindowStart
+ ? convertDateMathToDuration(historyWindowStart)
+ : DEFAULT_HISTORY_WINDOW_SIZE;
return (
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_adapter.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_adapter.tsx
new file mode 100644
index 000000000000..084a81e3b959
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_adapter.tsx
@@ -0,0 +1,13 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { HistoryWindowStartEdit } from '../../../../../../../rule_creation/components/history_window_start_edit';
+
+export function HistoryWindowStartEditAdapter(): JSX.Element {
+ return ;
+}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_form.tsx
new file mode 100644
index 000000000000..87ac2fee64e7
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/history_window_start/history_window_start_edit_form.tsx
@@ -0,0 +1,48 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { type FormData } from '../../../../../../../../shared_imports';
+import { HistoryWindowStartEditAdapter } from './history_window_start_edit_adapter';
+import type { HistoryWindowStart } from '../../../../../../../../../common/api/detection_engine';
+import {
+ convertDurationToDateMath,
+ convertDateMathToDuration,
+} from '../../../../../../../../common/utils/date_math';
+import { DEFAULT_HISTORY_WINDOW_SIZE } from '../../../../../../../../common/constants';
+import { RuleFieldEditFormWrapper } from '../../../field_final_side';
+
+export function HistoryWindowStartEditForm(): JSX.Element {
+ return (
+
+ );
+}
+
+interface HistoryWindowFormData {
+ historyWindowSize: HistoryWindowStart;
+}
+
+function deserializer(defaultValue: FormData): HistoryWindowFormData {
+ return {
+ historyWindowSize: defaultValue.history_window_start
+ ? convertDateMathToDuration(defaultValue.history_window_start)
+ : DEFAULT_HISTORY_WINDOW_SIZE,
+ };
+}
+
+function serializer(formData: FormData): { history_window_start: HistoryWindowStart } {
+ return {
+ history_window_start: convertDurationToDateMath(formData.historyWindowSize),
+ };
+}
+
+const schema = {};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_adapter.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_adapter.tsx
new file mode 100644
index 000000000000..476bf9869bd9
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_adapter.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { NewTermsFieldsEdit } from '../../../../../../../rule_creation/components/new_terms_fields_edit';
+import { useDiffableRuleDataView } from '../hooks/use_diffable_rule_data_view';
+import type { DiffableRule } from '../../../../../../../../../common/api/detection_engine';
+import { useTermsAggregationFields } from '../../../../../../../../common/hooks/use_terms_aggregation_fields';
+
+interface NewTermsFieldsEditAdapterProps {
+ finalDiffableRule: DiffableRule;
+}
+
+export function NewTermsFieldsEditAdapter({
+ finalDiffableRule,
+}: NewTermsFieldsEditAdapterProps): JSX.Element {
+ const { dataView } = useDiffableRuleDataView(finalDiffableRule);
+ const termsAggregationFields = useTermsAggregationFields(dataView?.fields ?? []);
+ const fieldNames = termsAggregationFields.map((field) => field.name);
+
+ return ;
+}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_form.tsx
new file mode 100644
index 000000000000..9b8d709b27f0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/new_terms_fields/new_terms_fields_edit_form.tsx
@@ -0,0 +1,18 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { RuleFieldEditFormWrapper } from '../../../field_final_side';
+import { NewTermsFieldsEditAdapter } from './new_terms_fields_edit_adapter';
+
+export function NewTermsFieldsEditForm(): JSX.Element {
+ return (
+
+ );
+}
+
+const schema = {};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx
index 12e7bbea9a20..3acd7c3050fb 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/fields/rule_schedule.tsx
@@ -10,8 +10,8 @@ import { parseDuration } from '@kbn/alerting-plugin/common';
import { type FormSchema, type FormData, UseField } from '../../../../../../../shared_imports';
import { schema } from '../../../../../../rule_creation_ui/components/step_schedule_rule/schema';
import type { RuleSchedule } from '../../../../../../../../common/api/detection_engine';
-import { ScheduleItem } from '../../../../../../rule_creation/components/schedule_item_form';
import { secondsToDurationString } from '../../../../../../../detections/pages/detection_engine/rules/helpers';
+import { ScheduleItemField } from '../../../../../../rule_creation/components/schedule_item_field';
export const ruleScheduleSchema = {
interval: schema.interval,
@@ -28,8 +28,8 @@ const componentProps = {
export function RuleScheduleEdit(): JSX.Element {
return (
<>
-
-
+
+
>
);
}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/new_terms_rule_field_edit.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/new_terms_rule_field_edit.tsx
index e2860d431aff..f47607abe384 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/new_terms_rule_field_edit.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/final_edit/new_terms_rule_field_edit.tsx
@@ -7,9 +7,12 @@
import React from 'react';
import type { UpgradeableNewTermsFields } from '../../../../model/prebuilt_rule_upgrade/fields';
-import { KqlQueryEditForm } from './fields/kql_query';
-import { DataSourceEditForm } from './fields/data_source';
import { AlertSuppressionEditForm } from './fields/alert_suppression';
+import { DataSourceEditForm } from './fields/data_source';
+import { HistoryWindowStartEditForm } from './fields/history_window_start/history_window_start_edit_form';
+import { KqlQueryEditForm } from './fields/kql_query';
+import { NewTermsFieldsEditForm } from './fields/new_terms_fields/new_terms_fields_edit_form';
+import { assertUnreachable } from '../../../../../../../common/utility_types';
interface NewTermsRuleFieldEditProps {
fieldName: UpgradeableNewTermsFields;
@@ -17,13 +20,17 @@ interface NewTermsRuleFieldEditProps {
export function NewTermsRuleFieldEdit({ fieldName }: NewTermsRuleFieldEditProps) {
switch (fieldName) {
- case 'kql_query':
- return ;
- case 'data_source':
- return ;
case 'alert_suppression':
return ;
+ case 'data_source':
+ return ;
+ case 'history_window_start':
+ return ;
+ case 'kql_query':
+ return ;
+ case 'new_terms_fields':
+ return ;
default:
- return null; // Will be replaced with `assertUnreachable(fieldName)` once all fields are implemented
+ return assertUnreachable(fieldName);
}
}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/schedule_form.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/schedule_form.tsx
index 88e1411a5e0b..518c18a59a41 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/schedule_form.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/bulk_actions/forms/schedule_form.tsx
@@ -9,11 +9,11 @@ import { EuiCallOut } from '@elastic/eui';
import React, { useCallback } from 'react';
import type { BulkActionEditPayload } from '../../../../../../../common/api/detection_engine/rule_management';
import { BulkActionEditTypeEnum } from '../../../../../../../common/api/detection_engine/rule_management';
-import { ScheduleItem } from '../../../../../rule_creation/components/schedule_item_form';
import type { FormSchema } from '../../../../../../shared_imports';
import { UseField, useForm } from '../../../../../../shared_imports';
import { bulkSetSchedule as i18n } from '../translations';
import { BulkEditFormWrapper } from './bulk_edit_form_wrapper';
+import { ScheduleItemField } from '../../../../../rule_creation/components/schedule_item_field';
export interface ScheduleFormData {
interval: string;
@@ -79,7 +79,7 @@ export const ScheduleForm = ({ rulesCount, onClose, onConfirm }: ScheduleFormCom
>
({
newTermsFields: ('new_terms_fields' in rule && rule.new_terms_fields) || [],
historyWindowSize:
'history_window_start' in rule && rule.history_window_start
- ? convertHistoryStartToSize(rule.history_window_start)
- : '7d',
+ ? convertDateMathToDuration(rule.history_window_start)
+ : DEFAULT_HISTORY_WINDOW_SIZE,
shouldLoadQueryDynamically: Boolean(rule.type === 'saved_query' && rule.saved_id),
[ALERT_SUPPRESSION_FIELDS_FIELD_NAME]:
('alert_suppression' in rule &&
@@ -189,14 +191,6 @@ export const getDefineStepsData = (rule: RuleResponse): DefineStepRule => ({
),
});
-export const convertHistoryStartToSize = (relativeTime: string) => {
- if (relativeTime.startsWith('now-')) {
- return relativeTime.substring(4);
- } else {
- return relativeTime;
- }
-};
-
export const getScheduleStepsData = (rule: RuleResponse): ScheduleStepRule => {
const { interval, from } = rule;
const fromHumanizedValue = getHumanizedDuration(from, interval);
diff --git a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts
index 903b463d6f58..483cbf06b625 100644
--- a/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts
+++ b/x-pack/test/security_solution_cypress/cypress/screens/create_new_rule.ts
@@ -275,10 +275,10 @@ export const ESQL_QUERY_BAR = '[data-test-subj="ruleEsqlQueryBar"]';
export const NEW_TERMS_INPUT_AREA = '[data-test-subj="newTermsInput"]';
export const NEW_TERMS_HISTORY_SIZE =
- '[data-test-subj="detectionEngineStepDefineRuleHistoryWindowSize"] [data-test-subj="interval"]';
+ '[data-test-subj="historyWindowSize"] [data-test-subj="interval"]';
export const NEW_TERMS_HISTORY_TIME_TYPE =
- '[data-test-subj="detectionEngineStepDefineRuleHistoryWindowSize"] [data-test-subj="timeType"]';
+ '[data-test-subj="historyWindowSize"] [data-test-subj="timeType"]';
export const LOAD_QUERY_DYNAMICALLY_CHECKBOX =
'[data-test-subj="detectionEngineStepDefineRuleShouldLoadQueryDynamically"] input';