From be259b2c7b7ff95d36020123a0acded88ae60955 Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Wed, 6 Dec 2023 17:21:38 +0100 Subject: [PATCH 1/4] Add prefill functionality to custom_threshold_rule_expression --- .../components/alert_flyout.tsx | 42 ---- .../components/metrics_alert_dropdown.tsx | 191 ------------------ .../custom_threshold_rule_expression.test.tsx | 48 ++++- .../custom_threshold_rule_expression.tsx | 82 +++++++- ....test.tsx => use_expression_data.test.tsx} | 0 .../components/custom_threshold/types.ts | 10 +- 6 files changed, 127 insertions(+), 246 deletions(-) delete mode 100644 x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx delete mode 100644 x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx rename x-pack/plugins/observability/public/components/custom_threshold/hooks/{use_metrics_explorer_data.test.tsx => use_expression_data.test.tsx} (100%) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx deleted file mode 100644 index 1a90ffc7460c..000000000000 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.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, { useCallback, useContext, useMemo } from 'react'; - -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; -import { TriggerActionsContext } from './triggers_actions_context'; -import { observabilityRuleCreationValidConsumers } from '../../../../common/constants'; - -interface Props { - visible?: boolean; - setVisible: React.Dispatch>; -} - -export function AlertFlyout(props: Props) { - const { visible, setVisible } = props; - const { triggersActionsUI } = useContext(TriggerActionsContext); - const onCloseFlyout = useCallback(() => setVisible(false), [setVisible]); - const AddAlertFlyout = useMemo( - () => - triggersActionsUI && - triggersActionsUI.getAddRuleFlyout({ - consumer: 'logs', - onClose: onCloseFlyout, - canChangeTrigger: false, - ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, - validConsumers: observabilityRuleCreationValidConsumers, - useRuleProducer: true, - }), - [triggersActionsUI, onCloseFlyout] - ); - - return <>{visible && AddAlertFlyout}; -} - -export function PrefilledThresholdAlertFlyout({ onClose }: { onClose(): void }) { - return ; -} diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx deleted file mode 100644 index 91b97161f78d..000000000000 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx +++ /dev/null @@ -1,191 +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 { i18n } from '@kbn/i18n'; -import React, { useState, useCallback, useMemo } from 'react'; -import { - EuiPopover, - EuiHeaderLink, - EuiContextMenu, - EuiContextMenuPanelDescriptor, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { InfraClientStartDeps } from '../types'; -import { PrefilledThresholdAlertFlyout } from './alert_flyout'; - -type VisibleFlyoutType = 'inventory' | 'threshold' | null; - -export function MetricsAlertDropdown() { - const [popoverOpen, setPopoverOpen] = useState(false); - const [visibleFlyoutType, setVisibleFlyoutType] = useState(null); - const uiCapabilities = useKibana().services.application?.capabilities; - const { - services: { observability }, - } = useKibana(); - const canCreateAlerts = useMemo( - () => Boolean(uiCapabilities?.infrastructure?.save), - [uiCapabilities] - ); - - const closeFlyout = useCallback(() => setVisibleFlyoutType(null), [setVisibleFlyoutType]); - - const closePopover = useCallback(() => { - setPopoverOpen(false); - }, [setPopoverOpen]); - - const togglePopover = useCallback(() => { - setPopoverOpen(!popoverOpen); - }, [setPopoverOpen, popoverOpen]); - const infrastructureAlertsPanel = useMemo( - () => ({ - id: 1, - title: i18n.translate( - 'xpack.observability.customThreshold.rule.infrastructureDropdownTitle', - { - defaultMessage: 'Infrastructure rules', - } - ), - items: [ - { - 'data-test-subj': 'inventory-alerts-create-rule', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.createInventoryRuleButton', - { - defaultMessage: 'Create inventory rule', - } - ), - onClick: () => { - closePopover(); - setVisibleFlyoutType('inventory'); - }, - }, - ], - }), - [setVisibleFlyoutType, closePopover] - ); - - const metricsAlertsPanel = useMemo( - () => ({ - id: 2, - title: i18n.translate('xpack.observability.customThreshold.rule.metricsDropdownTitle', { - defaultMessage: 'Metrics rules', - }), - items: [ - { - 'data-test-subj': 'metrics-threshold-alerts-create-rule', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.createThresholdRuleButton', - { - defaultMessage: 'Create threshold rule', - } - ), - onClick: () => { - closePopover(); - setVisibleFlyoutType('threshold'); - }, - }, - ], - }), - [setVisibleFlyoutType, closePopover] - ); - - const manageRulesLinkProps = observability.useRulesLink(); - - const manageAlertsMenuItem = useMemo( - () => ({ - name: i18n.translate('xpack.observability.customThreshold.rule.manageRules', { - defaultMessage: 'Manage rules', - }), - icon: 'tableOfContents', - onClick: manageRulesLinkProps.onClick, - }), - [manageRulesLinkProps] - ); - - const firstPanelMenuItems: EuiContextMenuPanelDescriptor['items'] = useMemo( - () => - canCreateAlerts - ? [ - { - 'data-test-subj': 'inventory-alerts-menu-option', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.infrastructureDropdownMenu', - { - defaultMessage: 'Infrastructure', - } - ), - panel: 1, - }, - { - 'data-test-subj': 'metrics-threshold-alerts-menu-option', - name: i18n.translate('xpack.observability.customThreshold.rule.metricsDropdownMenu', { - defaultMessage: 'Metrics', - }), - panel: 2, - }, - manageAlertsMenuItem, - ] - : [manageAlertsMenuItem], - [canCreateAlerts, manageAlertsMenuItem] - ); - - const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => - [ - { - id: 0, - title: i18n.translate('xpack.observability.customThreshold.rule.alertDropdownTitle', { - defaultMessage: 'Alerts and rules', - }), - items: firstPanelMenuItems, - }, - ].concat(canCreateAlerts ? [infrastructureAlertsPanel, metricsAlertsPanel] : []), - [infrastructureAlertsPanel, metricsAlertsPanel, firstPanelMenuItems, canCreateAlerts] - ); - return ( - <> - - - - } - isOpen={popoverOpen} - closePopover={closePopover} - > - - - - - ); -} - -interface AlertFlyoutProps { - visibleFlyoutType: VisibleFlyoutType; - onClose(): void; -} - -function AlertFlyout({ visibleFlyoutType, onClose }: AlertFlyoutProps) { - switch (visibleFlyoutType) { - case 'threshold': - return ; - default: - return null; - } -} diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index 0f1742469c31..bf2fa5773cbe 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -6,16 +6,16 @@ */ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { QueryClientProvider } from '@tanstack/react-query'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { queryClient } from '@kbn/osquery-plugin/public/query_client'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; - +import { QueryClientProvider } from '@tanstack/react-query'; +import { act } from 'react-dom/test-utils'; import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import Expressions from './custom_threshold_rule_expression'; +import { CustomThresholdPreFillOptions } from './types'; jest.mock('../../utils/kibana_react'); jest.mock('./components/preview_chart/preview_chart', () => ({ @@ -38,7 +38,10 @@ describe('Expression', () => { mockKibana(); }); - async function setup() { + async function setup( + currentOptions?: CustomThresholdPreFillOptions, + customRuleParams?: Record + ) { const ruleParams = { criteria: [], groupBy: undefined, @@ -50,6 +53,7 @@ describe('Expression', () => { language: 'kuery', }, }, + ...customRuleParams, }; const wrapper = mountWithIntl( @@ -62,6 +66,7 @@ describe('Expression', () => { setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} setRuleProperty={() => {}} metadata={{ + currentOptions, adHocDataViewList: [], }} dataViews={dataViewMock} @@ -99,6 +104,41 @@ describe('Expression', () => { ]); }); + it('should prefill the rule using the context metadata', async () => { + const index = 'changedMockedIndex'; + const currentOptions: CustomThresholdPreFillOptions = { + groupBy: ['host.hostname'], + filterQuery: 'foo', + searchConfiguration: { index }, + criteria: [ + { + metrics: [ + { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, + ], + }, + ], + }; + + const { ruleParams } = await setup(currentOptions, { searchConfiguration: undefined }); + + expect(ruleParams.groupBy).toEqual(['host.hostname']); + expect(ruleParams.searchConfiguration.query.query).toBe('foo'); + expect(ruleParams.searchConfiguration.index).toBe(index); + expect(ruleParams.criteria).toEqual([ + { + metrics: [ + { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, + ], + comparator: Comparator.GT, + threshold: [100], + timeSize: 1, + timeUnit: 'm', + }, + ]); + }); + it('should show an error message when searchSource throws an error', async () => { const errorMessage = 'Error in searchSource create'; const kibanaMock = kibanaStartMock.startContract(); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 539f8fbf4f58..d8eb6f57c773 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -96,14 +96,24 @@ export default function Expressions(props: Props) { let initialSearchConfiguration = ruleParams.searchConfiguration; if (!ruleParams.searchConfiguration || !ruleParams.searchConfiguration.index) { - const newSearchSource = data.search.searchSource.createEmpty(); - newSearchSource.setField('query', data.query.queryString.getDefaultQuery()); - const defaultDataView = await data.dataViews.getDefaultDataView(); - if (defaultDataView) { - newSearchSource.setField('index', defaultDataView); - setDataView(defaultDataView); + if (metadata?.currentOptions?.searchConfiguration) { + initialSearchConfiguration = { + ...metadata.currentOptions.searchConfiguration, + query: { + query: ruleParams.searchConfiguration?.query ?? '', + language: 'kuery', + }, + }; + } else { + const newSearchSource = data.search.searchSource.createEmpty(); + newSearchSource.setField('query', data.query.queryString.getDefaultQuery()); + const defaultDataView = await data.dataViews.getDefaultDataView(); + if (defaultDataView) { + newSearchSource.setField('index', defaultDataView); + setDataView(defaultDataView); + } + initialSearchConfiguration = newSearchSource.getSerializedFields(); } - initialSearchConfiguration = newSearchSource.getSerializedFields(); } try { @@ -142,6 +152,18 @@ export default function Expressions(props: Props) { } }; + if (!ruleParams.searchConfiguration) { + if (metadata?.currentOptions?.searchConfiguration) { + setRuleParams('searchConfiguration', { + ...metadata.currentOptions.searchConfiguration, + query: { + query: ruleParams.searchConfiguration?.query ?? '', + language: 'kuery', + }, + }); + } + } + initSearchSource(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data.search.searchSource, data.dataViews, dataView]); @@ -151,7 +173,15 @@ export default function Expressions(props: Props) { setTimeSize(ruleParams.criteria[0].timeSize); setTimeUnit(ruleParams.criteria[0].timeUnit); } else { - setRuleParams('criteria', [defaultExpression]); + preFillCriteria(); + } + + if (!ruleParams.filterQuery) { + preFillFilterQuery(); + } + + if (!ruleParams.groupBy) { + preFillGroupBy(); } if (typeof ruleParams.alertOnNoData === 'undefined') { @@ -259,6 +289,42 @@ export default function Expressions(props: Props) { [ruleParams.criteria, setRuleParams] ); + const preFillFilterQuery = useCallback(() => { + const md = metadata; + + if (md && md.currentOptions?.filterQuery) { + setRuleParams('searchConfiguration', { + ...ruleParams.searchConfiguration, + query: { + query: md.currentOptions.filterQuery, + language: 'kuery', + }, + }); + } + }, [metadata, setRuleParams, ruleParams.searchConfiguration]); + + const preFillCriteria = useCallback(() => { + const md = metadata; + if (md?.currentOptions?.criteria?.length) { + setRuleParams( + 'criteria', + md.currentOptions.criteria.map((criterion) => ({ + ...defaultExpression, + ...criterion, + })) + ); + } else { + setRuleParams('criteria', [defaultExpression]); + } + }, [metadata, setRuleParams]); + + const preFillGroupBy = useCallback(() => { + const md = metadata; + if (md && md.currentOptions?.groupBy) { + setRuleParams('groupBy', md.currentOptions.groupBy); + } + }, [metadata, setRuleParams]); + const hasGroupBy = useMemo( () => ruleParams.groupBy && ruleParams.groupBy.length > 0, [ruleParams.groupBy] diff --git a/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_metrics_explorer_data.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_data.test.tsx similarity index 100% rename from x-pack/plugins/observability/public/components/custom_threshold/hooks/use_metrics_explorer_data.test.tsx rename to x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_data.test.tsx diff --git a/x-pack/plugins/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability/public/components/custom_threshold/types.ts index dc0ad4eb7162..c3d1c475f5e3 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/types.ts @@ -15,6 +15,7 @@ import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; +import { OsqueryPluginStart } from '@kbn/osquery-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { @@ -28,11 +29,18 @@ import { CustomMetricExpressionParams, BaseMetricExpressionParams, aggType, + ThresholdParams, + MetricExpressionParams, } from '../../../common/custom_threshold_rule/types'; import { ObservabilityPublicStart } from '../../plugin'; +export type CustomThresholdPreFillOptions = Partial< + Omit & { criteria: Array> } +>; + export interface AlertContextMeta { adHocDataViewList: DataView[]; + currentOptions?: CustomThresholdPreFillOptions; } export type MetricExpression = Omit & { @@ -66,7 +74,7 @@ export interface InfraClientStartDeps { lens: LensPublicStart; observability: ObservabilityPublicStart; observabilityShared: ObservabilitySharedPluginStart; - osquery?: unknown; // OsqueryPluginStart; + osquery?: OsqueryPluginStart; share: SharePluginStart; spaces: SpacesPluginStart; storage: IStorageWrapper; From 7cab6d350a98cd92c3d17292cad2bb056eda87ef Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Thu, 7 Dec 2023 09:06:31 +0100 Subject: [PATCH 2/4] Fix type --- .../custom_threshold_rule_expression.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index d8eb6f57c773..dd3ed8e1110f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -63,6 +63,10 @@ export const defaultExpression: MetricExpression = { timeSize: 1, timeUnit: 'm', }; +const defaultQuery = { + query: '', + language: 'kuery', +}; // eslint-disable-next-line import/no-default-export export default function Expressions(props: Props) { @@ -156,10 +160,7 @@ export default function Expressions(props: Props) { if (metadata?.currentOptions?.searchConfiguration) { setRuleParams('searchConfiguration', { ...metadata.currentOptions.searchConfiguration, - query: { - query: ruleParams.searchConfiguration?.query ?? '', - language: 'kuery', - }, + query: defaultQuery, }); } } From a5103ef268b941a7901de93add040717128718c4 Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Thu, 7 Dec 2023 10:59:51 +0100 Subject: [PATCH 3/4] Update translation --- x-pack/plugins/translations/translations/fr-FR.json | 9 --------- x-pack/plugins/translations/translations/ja-JP.json | 9 --------- x-pack/plugins/translations/translations/zh-CN.json | 9 --------- 3 files changed, 27 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a8ac6bb851a5..8607021724be 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -29070,7 +29070,6 @@ "xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.rule": "Règle", "xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "Seuil dépassé", "xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "Lien vers l’affichage de résolution des problèmes d’alerte pour voir plus de contextes et de détails. La chaîne sera vide si server.publicBaseUrl n'est pas configuré.", - "xpack.observability.customThreshold.rule.alertDropdownTitle": "Alertes et règles", "xpack.observability.customThreshold.rule.alertFlyout.addCondition": "Ajouter une condition", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.avg": "Moyenne", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.cardinality": "Cardinalité", @@ -29118,19 +29117,11 @@ "xpack.observability.customThreshold.rule.alerting.threshold.defaultActionMessage": "\\{\\{context.reason\\}\\}\n\n\\{\\{rule.name\\}\\} est actif.\n\n[Voir les détails de l’alerte](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.defaultRecoveryMessage": "\\{\\{rule.name\\}\\} a récupéré.\n\n[Voir les détails de l’alerte](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.noDataFormattedValue": "[AUCUNE DONNÉE]", - "xpack.observability.customThreshold.rule.alertsButton": "Alertes et règles", "xpack.observability.customThreshold.rule.cloudActionVariableDescription": "Objet cloud défini par ECS s'il est disponible dans la source.", "xpack.observability.customThreshold.rule.containerActionVariableDescription": "Objet conteneur défini par ECS s'il est disponible dans la source.", - "xpack.observability.customThreshold.rule.createInventoryRuleButton": "Créer une règle d'inventaire", - "xpack.observability.customThreshold.rule.createThresholdRuleButton": "Créer une règle de seuil", "xpack.observability.customThreshold.rule.groupByKeysActionVariableDescription": "Objet contenant les groupes qui fournissent les données", "xpack.observability.customThreshold.rule.hostActionVariableDescription": "Objet hôte défini par ECS s'il est disponible dans la source.", - "xpack.observability.customThreshold.rule.infrastructureDropdownMenu": "Infrastructure", - "xpack.observability.customThreshold.rule.infrastructureDropdownTitle": "Règles d'infrastructure", "xpack.observability.customThreshold.rule.labelsActionVariableDescription": "Liste d'étiquettes associées avec l'entité sur laquelle l'alerte s'est déclenchée.", - "xpack.observability.customThreshold.rule.manageRules": "Gérer les règles", - "xpack.observability.customThreshold.rule.metricsDropdownMenu": "Indicateurs", - "xpack.observability.customThreshold.rule.metricsDropdownTitle": "Règles d'indicateurs", "xpack.observability.customThreshold.rule.orchestratorActionVariableDescription": "Objet orchestrateur défini par ECS s'il est disponible dans la source.", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "Une description concise de la raison du signalement", "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery doit être un filtre KQL valide", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c1db876e1454..07effffb0613 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -29070,7 +29070,6 @@ "xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.rule": "ルール", "xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "しきい値を超えました", "xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "アラートトラブルシューティングビューにリンクして、さらに詳しい状況や詳細を確認できます。server.publicBaseUrlが構成されていない場合は、空の文字列になります。", - "xpack.observability.customThreshold.rule.alertDropdownTitle": "アラートとルール", "xpack.observability.customThreshold.rule.alertFlyout.addCondition": "条件を追加", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.avg": "平均", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.cardinality": "基数", @@ -29118,19 +29117,11 @@ "xpack.observability.customThreshold.rule.alerting.threshold.defaultActionMessage": "\\{\\{context.reason\\}\\}\n\n\\{\\{rule.name\\}\\}はアクティブです。\n\n[アラート詳細を表示](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.defaultRecoveryMessage": "\\{\\{rule.name\\}\\}が回復しました。\n\n[アラート詳細を表示](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.noDataFormattedValue": "[データなし]", - "xpack.observability.customThreshold.rule.alertsButton": "アラートとルール", "xpack.observability.customThreshold.rule.cloudActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたクラウドオブジェクト。", "xpack.observability.customThreshold.rule.containerActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたコンテナーオブジェクト。", - "xpack.observability.customThreshold.rule.createInventoryRuleButton": "インベントリルールの作成", - "xpack.observability.customThreshold.rule.createThresholdRuleButton": "しきい値ルールを作成", "xpack.observability.customThreshold.rule.groupByKeysActionVariableDescription": "データを報告しているグループを含むオブジェクト", "xpack.observability.customThreshold.rule.hostActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたホストオブジェクト。", - "xpack.observability.customThreshold.rule.infrastructureDropdownMenu": "インフラストラクチャー", - "xpack.observability.customThreshold.rule.infrastructureDropdownTitle": "インフラストラクチャールール", "xpack.observability.customThreshold.rule.labelsActionVariableDescription": "このアラートがトリガーされたエンティティに関連付けられたラベルのリスト。", - "xpack.observability.customThreshold.rule.manageRules": "ルールの管理", - "xpack.observability.customThreshold.rule.metricsDropdownMenu": "メトリック", - "xpack.observability.customThreshold.rule.metricsDropdownTitle": "メトリックルール", "xpack.observability.customThreshold.rule.orchestratorActionVariableDescription": "ソースで使用可能な場合に、ECSで定義されたオーケストレーターオブジェクト。", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "アラートの理由の簡潔な説明", "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQueryは有効なKQLフィルターでなければなりません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d06158ed521e..0aa40cdb80e0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -29067,7 +29067,6 @@ "xpack.observability.customThreshold.rule.alertDetailsAppSection.summaryField.rule": "规则", "xpack.observability.customThreshold.rule.alertDetailsAppSection.thresholdTitle": "超出阈值", "xpack.observability.customThreshold.rule.alertDetailUrlActionVariableDescription": "链接到告警故障排除视图获取进一步的上下文和详情。如果未配置 server.publicBaseUrl,这将为空字符串。", - "xpack.observability.customThreshold.rule.alertDropdownTitle": "告警和规则", "xpack.observability.customThreshold.rule.alertFlyout.addCondition": "添加条件", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.avg": "平均值", "xpack.observability.customThreshold.rule.alertFlyout.aggregationText.cardinality": "基数", @@ -29115,19 +29114,11 @@ "xpack.observability.customThreshold.rule.alerting.threshold.defaultActionMessage": "\\{\\{context.reason\\}\\}\n\n\\{\\{rule.name\\}\\} 处于活动状态。\n\n[查看告警详情](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.defaultRecoveryMessage": "\\{\\{rule.name\\}\\} 已恢复。\n\n[查看告警详情](\\{\\{context.alertDetailsUrl\\}\\})\n", "xpack.observability.customThreshold.rule.alerting.threshold.noDataFormattedValue": "[无数据]", - "xpack.observability.customThreshold.rule.alertsButton": "告警和规则", "xpack.observability.customThreshold.rule.cloudActionVariableDescription": "ECS 定义的云对象(如果在源中可用)。", "xpack.observability.customThreshold.rule.containerActionVariableDescription": "ECS 定义的容器对象(如果在源中可用)。", - "xpack.observability.customThreshold.rule.createInventoryRuleButton": "创建库存规则", - "xpack.observability.customThreshold.rule.createThresholdRuleButton": "创建阈值规则", "xpack.observability.customThreshold.rule.groupByKeysActionVariableDescription": "包含正报告数据的组的对象", "xpack.observability.customThreshold.rule.hostActionVariableDescription": "ECS 定义的主机对象(如果在源中可用)。", - "xpack.observability.customThreshold.rule.infrastructureDropdownMenu": "基础设施", - "xpack.observability.customThreshold.rule.infrastructureDropdownTitle": "基础设施规则", "xpack.observability.customThreshold.rule.labelsActionVariableDescription": "与在其上触发此告警的实体关联的标签列表。", - "xpack.observability.customThreshold.rule.manageRules": "管理规则", - "xpack.observability.customThreshold.rule.metricsDropdownMenu": "指标", - "xpack.observability.customThreshold.rule.metricsDropdownTitle": "指标规则", "xpack.observability.customThreshold.rule.orchestratorActionVariableDescription": "ECS 定义的 Orchestrator 对象(如果在源中可用)。", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "告警原因的简洁描述", "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery 必须是有效的 KQL 筛选", From 85994b3a623aa3f568c092de7dcb466da5875c6e Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Mon, 11 Dec 2023 16:23:40 +0100 Subject: [PATCH 4/4] Renagem CustomThresholdPrefillOptions and a test for when searchConfiguration is undefined --- .../custom_threshold_rule_expression.test.tsx | 127 +++++++++++------- .../custom_threshold_rule_expression.tsx | 13 -- .../components/custom_threshold/types.ts | 4 +- 3 files changed, 79 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index bf2fa5773cbe..ce1c9527904c 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -15,7 +15,7 @@ import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/t import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import Expressions from './custom_threshold_rule_expression'; -import { CustomThresholdPreFillOptions } from './types'; +import { CustomThresholdPrefillOptions } from './types'; jest.mock('../../utils/kibana_react'); jest.mock('./components/preview_chart/preview_chart', () => ({ @@ -39,7 +39,7 @@ describe('Expression', () => { }); async function setup( - currentOptions?: CustomThresholdPreFillOptions, + currentOptions?: CustomThresholdPrefillOptions, customRuleParams?: Record ) { const ruleParams = { @@ -55,6 +55,10 @@ describe('Expression', () => { }, ...customRuleParams, }; + const metadata = { + currentOptions, + adHocDataViewList: [], + }; const wrapper = mountWithIntl( { errors={{}} setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} setRuleProperty={() => {}} - metadata={{ - currentOptions, - adHocDataViewList: [], - }} + metadata={metadata} dataViews={dataViewMock} onChangeMetaData={jest.fn()} /> @@ -86,6 +87,59 @@ describe('Expression', () => { return { wrapper, update, ruleParams }; } + const updateUseKibanaMock = (mockedIndex: any) => { + const mockedDataView = { + getIndexPattern: () => 'mockedIndexPattern', + getName: () => 'mockedName', + ...mockedIndex, + }; + const mockedSearchSource = { + id: 'data_source', + shouldOverwriteDataViewType: false, + requestStartHandlers: [], + inheritOptions: {}, + history: [], + fields: { + index: mockedIndex, + }, + getField: jest.fn(() => mockedDataView), + setField: jest.fn(), + getSerializedFields: jest.fn().mockReturnValue({ index: mockedIndex }), + dependencies: { + aggs: { + types: {}, + }, + }, + }; + const kibanaMock = kibanaStartMock.startContract(); + useKibanaMock.mockReturnValue({ + ...kibanaMock, + services: { + ...kibanaMock.services, + data: { + dataViews: { + create: jest.fn(), + getDefaultDataView: jest.fn(), + }, + query: { + timefilter: { + timefilter: jest.fn(), + }, + queryString: { + getDefaultQuery: jest.fn(), + }, + }, + search: { + searchSource: { + create: jest.fn(() => mockedSearchSource), + createEmpty: jest.fn(() => mockedSearchSource), + }, + }, + }, + }, + }); + }; + it('should use default metrics', async () => { const { ruleParams } = await setup(); expect(ruleParams.criteria).toEqual([ @@ -106,7 +160,7 @@ describe('Expression', () => { it('should prefill the rule using the context metadata', async () => { const index = 'changedMockedIndex'; - const currentOptions: CustomThresholdPreFillOptions = { + const currentOptions: CustomThresholdPrefillOptions = { groupBy: ['host.hostname'], filterQuery: 'foo', searchConfiguration: { index }, @@ -180,49 +234,7 @@ describe('Expression', () => { // We should not provide timeFieldName here to show thresholdRuleDataViewErrorNoTimestamp error // timeFieldName: '@timestamp', }; - const mockedDataView = { - getIndexPattern: () => 'mockedIndexPattern', - getName: () => 'mockedName', - ...mockedIndex, - }; - const mockedSearchSource = { - id: 'data_source', - shouldOverwriteDataViewType: false, - requestStartHandlers: [], - inheritOptions: {}, - history: [], - fields: { - index: mockedIndex, - }, - getField: jest.fn(() => mockedDataView), - dependencies: { - aggs: { - types: {}, - }, - }, - }; - const kibanaMock = kibanaStartMock.startContract(); - useKibanaMock.mockReturnValue({ - ...kibanaMock, - services: { - ...kibanaMock.services, - data: { - dataViews: { - create: jest.fn(), - }, - query: { - timefilter: { - timefilter: jest.fn(), - }, - }, - search: { - searchSource: { - create: jest.fn(() => mockedSearchSource), - }, - }, - }, - }, - }); + updateUseKibanaMock(mockedIndex); const { wrapper } = await setup(); expect( wrapper.find(`[data-test-subj="thresholdRuleDataViewErrorNoTimestamp"]`).first().text() @@ -230,4 +242,19 @@ describe('Expression', () => { 'The selected data view does not have a timestamp field, please select another data view.' ); }); + + it('should use output of getSerializedFields() as searchConfiguration', async () => { + const mockedIndex = { + id: 'c34a7c79-a88b-4b4a-ad19-72f6d24104e4', + title: 'metrics-fake_hosts', + fieldFormatMap: {}, + typeMeta: {}, + timeFieldName: '@timestamp', + }; + updateUseKibanaMock(mockedIndex); + const { ruleParams } = await setup(undefined, { searchConfiguration: undefined }); + expect(ruleParams.searchConfiguration).toEqual({ + index: mockedIndex, + }); + }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index dd3ed8e1110f..0df72511ce55 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -63,10 +63,6 @@ export const defaultExpression: MetricExpression = { timeSize: 1, timeUnit: 'm', }; -const defaultQuery = { - query: '', - language: 'kuery', -}; // eslint-disable-next-line import/no-default-export export default function Expressions(props: Props) { @@ -156,15 +152,6 @@ export default function Expressions(props: Props) { } }; - if (!ruleParams.searchConfiguration) { - if (metadata?.currentOptions?.searchConfiguration) { - setRuleParams('searchConfiguration', { - ...metadata.currentOptions.searchConfiguration, - query: defaultQuery, - }); - } - } - initSearchSource(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data.search.searchSource, data.dataViews, dataView]); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability/public/components/custom_threshold/types.ts index c3d1c475f5e3..9753cabbc118 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/types.ts @@ -34,13 +34,13 @@ import { } from '../../../common/custom_threshold_rule/types'; import { ObservabilityPublicStart } from '../../plugin'; -export type CustomThresholdPreFillOptions = Partial< +export type CustomThresholdPrefillOptions = Partial< Omit & { criteria: Array> } >; export interface AlertContextMeta { adHocDataViewList: DataView[]; - currentOptions?: CustomThresholdPreFillOptions; + currentOptions?: CustomThresholdPrefillOptions; } export type MetricExpression = Omit & {