From 93d63b9b7de07e041b3d5d0b9986aa1eec44bbfb Mon Sep 17 00:00:00 2001 From: Giovanni Geraci Date: Wed, 16 Jun 2021 03:43:34 +0200 Subject: [PATCH 01/32] Remove old limit on Console application (#100882) In latest stable version of Kibana(7.13), Console application seems to be not limited anymore to connecting only to the first host defined in elasticsearch.hosts defined on kibana.yml. On previous Kibana releases, if the first Elasticsearch host was not available(i.e. maintenance or failure), Console application was returning an error: "Client request error: connect EHOSTUNREACH". Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/user/production-considerations/production.asciidoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/user/production-considerations/production.asciidoc b/docs/user/production-considerations/production.asciidoc index 1ffca4b6ae6ab1..b75b556588cfd2 100644 --- a/docs/user/production-considerations/production.asciidoc +++ b/docs/user/production-considerations/production.asciidoc @@ -122,8 +122,6 @@ active in case of failure from the currently used instance. Kibana can be configured to connect to multiple Elasticsearch nodes in the same cluster. In situations where a node becomes unavailable, Kibana will transparently connect to an available node and continue operating. Requests to available hosts will be routed in a round robin fashion. -Currently the Console application is limited to connecting to the first node listed. - In kibana.yml: [source,js] -------- From 752609dc477c1e82817c0093c255bd14745a24b6 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 15 Jun 2021 21:34:01 -0700 Subject: [PATCH 02/32] skip flaky suite (#102282) --- x-pack/test/api_integration/apis/ml/modules/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts index ab46c4f0333c8c..dae0044c47ccac 100644 --- a/x-pack/test/api_integration/apis/ml/modules/index.ts +++ b/x-pack/test/api_integration/apis/ml/modules/index.ts @@ -12,7 +12,8 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const fleetPackages = ['apache-0.5.0', 'nginx-0.5.0']; - describe('modules', function () { + // Failing: See https://github.com/elastic/kibana/issues/102282 + describe.skip('modules', function () { before(async () => { for (const fleetPackage of fleetPackages) { await ml.testResources.installFleetPackage(fleetPackage); From 4cd073f65109f03b29dc5179dec120f5e141c07a Mon Sep 17 00:00:00 2001 From: Pete Hampton Date: Wed, 16 Jun 2021 08:11:32 +0100 Subject: [PATCH 03/32] Bug: ES query returning no records for d-rule alerts. (#102160) --- .../server/usage/detections/detection_rule_helpers.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts b/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts index ebcda694411355..8d5a2efc7fae11 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts @@ -177,6 +177,8 @@ export const updateDetectionRuleUsage = ( return updatedUsage; }; +const MAX_RESULTS_WINDOW = 10_000; // elasticsearch index.max_result_window default value + export const getDetectionRuleMetrics = async ( kibanaIndex: string, signalsIndex: string, @@ -189,14 +191,14 @@ export const getDetectionRuleMetrics = async ( filterPath: [], ignoreUnavailable: true, index: kibanaIndex, - size: 10_000, // elasticsearch index.max_result_window default value + size: MAX_RESULTS_WINDOW, }; try { const { body: ruleResults } = await esClient.search(ruleSearchOptions); const { body: detectionAlertsResp } = (await esClient.search({ index: `${signalsIndex}*`, - size: 0, + size: MAX_RESULTS_WINDOW, body: { aggs: { detectionAlerts: { @@ -224,7 +226,7 @@ export const getDetectionRuleMetrics = async ( type: 'cases-comments', fields: [], page: 1, - perPage: 10_000, + perPage: MAX_RESULTS_WINDOW, filter: 'cases-comments.attributes.type: alert', }); From f4d22b36fb300e8cbcee1606d1f7fde70b58b9b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Wed, 16 Jun 2021 09:13:37 +0200 Subject: [PATCH 04/32] [ML] Adds popover help for ROC curve (#101893) Co-authored-by: Lisa Cawley --- .../evaluate_panel.tsx | 15 +---- .../roc_curve_help_popover.tsx | 55 +++++++++++++++++++ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 4 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/roc_curve_help_popover.tsx diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index bc1c9dbed1dcc7..086adcecd077a3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -17,7 +17,6 @@ import { EuiDataGridPopoverContents, EuiFlexGroup, EuiFlexItem, - EuiIconTip, EuiSpacer, EuiText, EuiTitle, @@ -51,6 +50,7 @@ import { isTrainingFilter } from './is_training_filter'; import { useRocCurve } from './use_roc_curve'; import { useConfusionMatrix } from './use_confusion_matrix'; import { MulticlassConfusionMatrixHelpPopover } from './confusion_matrix_help_popover'; +import { RocCurveHelpPopover } from './roc_curve_help_popover'; export interface EvaluatePanelProps { jobConfig: DataFrameAnalyticsConfig; @@ -409,7 +409,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, se {/* AUC ROC Chart */} - + = ({ jobConfig, jobStatus, se - + {Array.isArray(errorRocCurve) && ( diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/roc_curve_help_popover.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/roc_curve_help_popover.tsx new file mode 100644 index 00000000000000..f828cbabde8949 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/roc_curve_help_popover.tsx @@ -0,0 +1,55 @@ +/* + * 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, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + HelpPopover, + HelpPopoverButton, +} from '../../../../../components/help_popover/help_popover'; + +export const RocCurveHelpPopover = () => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + { + setIsPopoverOpen(!isPopoverOpen); + }} + /> + } + closePopover={() => setIsPopoverOpen(false)} + isOpen={isPopoverOpen} + title={i18n.translate('xpack.ml.dataframe.analytics.rocCurvePopoverTitle', { + defaultMessage: 'Receiver operating characteristic (ROC) curve', + })} + > +

+ +

+

+ +

+

+ +

+
+ ); +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1f5cbcd4d6c3cc..c67dd383a2ea2c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -14027,7 +14027,6 @@ "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionMeanRecallStat": "平均再現率", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionOverallAccuracyStat": "全体的な精度", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionOverallAccuracyTooltip": "合計予測数に対する正しいクラス予測数の比率。", - "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionRocInfoTooltip": "受信者操作特性 (ROC) 曲線は、異なる予測確率しきい値で分類プロセスのパフォーマンスを表すプロットです。", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionRocTitle": "受信者操作特性 (ROC) 曲線", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionTitle": "モデル評価", "xpack.ml.dataframe.analytics.classificationExploration.showActions": "アクションを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8006153cafb063..23cde5dd1fcff4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -14208,7 +14208,6 @@ "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionMeanRecallStat": "平均召回率", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionOverallAccuracyStat": "总体准确率", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionOverallAccuracyTooltip": "正确类预测数目与预测总数的比率。", - "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionRocInfoTooltip": "接受者操作特性 (ROC) 曲线是表示在不同预测概率阈值下分类过程的性能绘图。", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionRocTitle": "接受者操作特性 (ROC) 曲线", "xpack.ml.dataframe.analytics.classificationExploration.evaluateSectionTitle": "模型评估", "xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, other {个文档}}已评估", From f39bf9c985b139e29e9ac8d8c0b37ab1f96edbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Casper=20H=C3=BCbertz?= Date: Wed, 16 Jun 2021 09:35:08 +0200 Subject: [PATCH 05/32] [APM] More styles fixes related to the new page template (#102253) * [APM] Service map: Add border to panel * [APM] Metrics: Add border to panel * [APM] Service overview: Add border to panel * [APM] Transactions and charts: Add border to panel --- .../apm/public/components/app/service_map/index.tsx | 2 +- .../apm/public/components/app/service_metrics/index.tsx | 2 +- .../public/components/app/service_node_metrics/index.tsx | 6 ++++-- .../apm/public/components/app/service_overview/index.tsx | 8 ++++---- .../service_overview_instances_chart_and_table.tsx | 2 +- .../service_overview_throughput_chart.tsx | 2 +- .../transaction_details/WaterfallWithSummmary/index.tsx | 4 ++-- .../public/components/app/transaction_details/index.tsx | 6 ++++-- .../public/components/app/transaction_overview/index.tsx | 2 +- .../charts/instances_latency_distribution_chart/index.tsx | 2 +- .../shared/charts/transaction_breakdown_chart/index.tsx | 2 +- .../components/shared/charts/transaction_charts/index.tsx | 4 ++-- .../shared/charts/transaction_error_rate_chart/index.tsx | 2 +- 13 files changed, 24 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index df8438c5c80a4a..582eafe7553af7 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -146,7 +146,7 @@ export function ServiceMap({ return ( <> - +
{data.charts.map((chart) => ( - + ) : ( - + + + )} @@ -171,7 +173,7 @@ export function ServiceNodeMetrics({ {data.charts.map((chart) => ( - + - + @@ -63,7 +63,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { - + @@ -84,7 +84,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { )} - + @@ -101,7 +101,7 @@ export function ServiceOverview({ serviceName }: ServiceOverviewProps) { {!isRumAgent && ( - + diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index 8513e0835d373b..719409b0f97ffe 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -228,7 +228,7 @@ export function ServiceOverviewInstancesChartAndTable({ /> - + +

{i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', { diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/index.tsx index 3d3ce3262f13b5..6f5b95b103f6b7 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/index.tsx @@ -83,13 +83,13 @@ export function WaterfallWithSummmary({ /> ); - return {content}; + return {content}; } const entryTransaction = entryWaterfallTransaction.doc; return ( - + diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx index b9508b2d303a26..3cac05ba2d96a8 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/index.tsx @@ -76,11 +76,13 @@ export function TransactionDetails() { return ( <> + +

{transactionName}

- + @@ -88,7 +90,7 @@ export function TransactionDetails() { - + - +

Transactions

diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx index ce4f36ced79038..0ad4be17e35cbd 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx @@ -104,7 +104,7 @@ export function InstancesLatencyDistributionChart({ }; return ( - +

{i18n.translate('xpack.apm.instancesLatencyDistributionChartTitle', { diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx index 978604c4c96ece..40c5e39589fb1f 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_breakdown_chart/index.tsx @@ -22,7 +22,7 @@ export function TransactionBreakdownChart({ const { timeseries } = data; return ( - + diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx index 3f868ae272e3a1..019a25b1e9ed37 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx @@ -37,13 +37,13 @@ export function TransactionCharts() { - + - + {i18n.translate( diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index 7eceaf5ca8e5da..96cb7c49a67104 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -135,7 +135,7 @@ export function TransactionErrorRateChart({ ]; return ( - +

{i18n.translate('xpack.apm.errorRate', { From c9678f29e2df52ac19eb45067d6c072865389660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Casper=20H=C3=BCbertz?= Date: Wed, 16 Jun 2021 09:43:35 +0200 Subject: [PATCH 06/32] [UX] Update panels to use border not shadow (#102262) --- .../components/app/RumDashboard/ClientMetrics/index.tsx | 2 +- .../components/app/RumDashboard/ImpactfulMetrics/index.tsx | 2 +- .../components/app/RumDashboard/Panels/PageLoadAndViews.tsx | 4 ++-- .../components/app/RumDashboard/Panels/VisitorBreakdowns.tsx | 4 ++-- .../public/components/app/RumDashboard/UXMetrics/index.tsx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx index add6ac1b08b281..c525a71ea45891 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx @@ -24,7 +24,7 @@ export function ClientMetrics() { } = useUrlParams(); return ( - + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/index.tsx index 175b40f85d64b0..b696a46f59bd12 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ImpactfulMetrics/index.tsx @@ -11,7 +11,7 @@ import { JSErrors } from './JSErrors'; export function ImpactfulMetrics() { return ( - + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx index b51e2559b7f15d..9dd83fd1c8fd1f 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/PageLoadAndViews.tsx @@ -14,12 +14,12 @@ export function PageLoadAndViews() { return ( - + - + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx index 0433988ecfa21a..ff79feaa924f37 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/Panels/VisitorBreakdowns.tsx @@ -14,12 +14,12 @@ export function VisitorBreakdownsPanel() { return ( - + - + diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx index 44212fed989876..a665b6560c7e95 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/index.tsx @@ -62,7 +62,7 @@ export function UXMetrics() { ); return ( - + From b96914235e8112cc3cc4f3d7a197560f684a2d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Casper=20H=C3=BCbertz?= Date: Wed, 16 Jun 2021 09:43:58 +0200 Subject: [PATCH 07/32] [Observability] Add border to section container panels (#102259) --- .../observability/public/components/app/section/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/components/app/section/index.tsx b/x-pack/plugins/observability/public/components/app/section/index.tsx index 191a1b2890ada3..cbe0c45bbd169c 100644 --- a/x-pack/plugins/observability/public/components/app/section/index.tsx +++ b/x-pack/plugins/observability/public/components/app/section/index.tsx @@ -25,7 +25,7 @@ interface Props { export function SectionContainer({ title, appLink, children, hasError }: Props) { const { core } = usePluginContext(); return ( - + Date: Wed, 16 Jun 2021 10:00:05 +0200 Subject: [PATCH 08/32] [Security Solution][Timeline] Fix User not able to scroll down and access Alert table on adding long content in Timeline's Description (#101486) * Add LineClamp component to timeline description * Truncate timeline description on timeline table * Fix StyledLineClamp styled component performance issue Read more: https://styled-components.com/docs/faqs#why-should-i-avoid-declaring-styled-components-in-the-render-method --- .../common/components/line_clamp/index.tsx | 29 ++++++++++++------- .../components/flyout/header/index.tsx | 11 +++---- .../timelines_table/common_columns.tsx | 14 +++++++-- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/line_clamp/index.tsx b/x-pack/plugins/security_solution/public/common/components/line_clamp/index.tsx index 896b0ec5fd8df8..d8895490d1e0ff 100644 --- a/x-pack/plugins/security_solution/public/common/components/line_clamp/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/line_clamp/index.tsx @@ -13,15 +13,6 @@ import * as i18n from './translations'; const LINE_CLAMP = 3; const LINE_CLAMP_HEIGHT = 5.5; -const StyledLineClamp = styled.div` - display: -webkit-box; - -webkit-line-clamp: ${LINE_CLAMP}; - -webkit-box-orient: vertical; - overflow: hidden; - max-height: ${`${LINE_CLAMP_HEIGHT}em`}; - height: ${`${LINE_CLAMP_HEIGHT}em`}; -`; - const ReadMore = styled(EuiButtonEmpty)` span.euiButtonContent { padding: 0; @@ -35,7 +26,19 @@ const ExpandedContent = styled.div` overflow-y: auto; `; -const LineClampComponent: React.FC<{ content?: string | null }> = ({ content }) => { +const StyledLineClamp = styled.div<{ lineClampHeight: number }>` + display: -webkit-box; + -webkit-line-clamp: ${LINE_CLAMP}; + -webkit-box-orient: vertical; + overflow: hidden; + max-height: ${({ lineClampHeight }) => lineClampHeight}em; + height: ${({ lineClampHeight }) => lineClampHeight}em; +`; + +const LineClampComponent: React.FC<{ + content?: string | null; + lineClampHeight?: number; +}> = ({ content, lineClampHeight = LINE_CLAMP_HEIGHT }) => { const [isOverflow, setIsOverflow] = useState(null); const [isExpanded, setIsExpanded] = useState(null); const descriptionRef = useRef(null); @@ -71,7 +74,11 @@ const LineClampComponent: React.FC<{ content?: string | null }> = ({ content })

{content}

) : isOverflow == null || isOverflow === true ? ( - + {content} ) : ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index da45579b347739..dd8cdb818cad75 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -52,6 +52,7 @@ import * as i18n from './translations'; import * as commonI18n from '../../timeline/properties/translations'; import { getTimelineStatusByIdSelector } from './selectors'; import { TimelineKPIs } from './kpis'; +import { LineClamp } from '../../../../common/components/line_clamp'; // to hide side borders const StyledPanel = styled(EuiPanel)` @@ -206,13 +207,13 @@ const TimelineDescriptionComponent: React.FC = ({ timelineId (state) => (getTimeline(state, timelineId) ?? timelineDefaults).description ); - const content = useMemo(() => (description.length ? description : commonI18n.DESCRIPTION), [ - description, - ]); - return ( - {content} + {description.length ? ( + + ) : ( + commonI18n.DESCRIPTION + )} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.tsx index 98d678a25b4c68..65963c96093209 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/common_columns.tsx @@ -10,7 +10,7 @@ import { EuiButtonIcon, EuiLink } from '@elastic/eui'; import { omit } from 'lodash/fp'; import React from 'react'; - +import styled from 'styled-components'; import { ACTION_COLUMN_WIDTH } from './common_styles'; import { isUntitled } from '../helpers'; import { NotePreviews } from '../note_previews'; @@ -20,6 +20,14 @@ import { getEmptyTagValue } from '../../../../common/components/empty_value'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; import { TimelineType } from '../../../../../common/types/timeline'; +const DescriptionCell = styled.span` + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 5; + -webkit-box-orient: vertical; + overflow: hidden; +`; + /** * Returns the column definitions (passed as the `columns` prop to * `EuiBasicTable`) that are common to the compact `Open Timeline` modal view, @@ -85,9 +93,9 @@ export const getCommonColumns = ({ field: 'description', name: i18n.DESCRIPTION, render: (description: string) => ( - + {description != null && description.trim().length > 0 ? description : getEmptyTagValue()} - + ), sortable: false, }, From 4180a026b736058727466915801c109aa58fdd10 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 16 Jun 2021 10:22:26 +0200 Subject: [PATCH 09/32] [Lens] Formula overall functions (#99461) --- ...ns-public.expressionfunctiondefinitions.md | 1 + ...ssionfunctiondefinitions.overall_metric.md | 11 + ...ns-server.expressionfunctiondefinitions.md | 1 + ...ssionfunctiondefinitions.overall_metric.md | 11 + .../expression_functions/specs/index.ts | 1 + .../specs/overall_metric.ts | 168 +++++++ .../specs/tests/overall_metric.test.ts | 450 ++++++++++++++++++ .../common/expression_functions/types.ts | 2 + .../common/service/expressions_services.ts | 2 + src/plugins/expressions/public/public.api.md | 4 + src/plugins/expressions/server/server.api.md | 4 + .../dimension_panel/dimension_editor.tsx | 5 + .../definitions/calculations/index.ts | 10 + .../calculations/overall_metric.tsx | 224 +++++++++ .../definitions/calculations/utils.ts | 32 ++ .../operations/definitions/index.ts | 24 + .../operations/index.ts | 4 + .../operations/operations.test.ts | 16 + 18 files changed, 970 insertions(+) create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md create mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md create mode 100644 src/plugins/expressions/common/expression_functions/specs/overall_metric.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/overall_metric.tsx diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md index c6e00842a31e6a..2c03db82ba683a 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md @@ -21,6 +21,7 @@ export interface ExpressionFunctionDefinitions | [derivative](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.derivative.md) | ExpressionFunctionDerivative | | | [font](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | | [moving\_average](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.moving_average.md) | ExpressionFunctionMovingAverage | | +| [overall\_metric](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md) | ExpressionFunctionOverallMetric | | | [theme](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.theme.md) | ExpressionFunctionTheme | | | [var\_set](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var_set.md) | ExpressionFunctionVarSet | | | [var](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.var.md) | ExpressionFunctionVar | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md new file mode 100644 index 00000000000000..8685788a2f3512 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.md) > [overall\_metric](./kibana-plugin-plugins-expressions-public.expressionfunctiondefinitions.overall_metric.md) + +## ExpressionFunctionDefinitions.overall\_metric property + +Signature: + +```typescript +overall_metric: ExpressionFunctionOverallMetric; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md index 219678244951b4..f55fed99e1d3d4 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md @@ -21,6 +21,7 @@ export interface ExpressionFunctionDefinitions | [derivative](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.derivative.md) | ExpressionFunctionDerivative | | | [font](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.font.md) | ExpressionFunctionFont | | | [moving\_average](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.moving_average.md) | ExpressionFunctionMovingAverage | | +| [overall\_metric](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md) | ExpressionFunctionOverallMetric | | | [theme](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.theme.md) | ExpressionFunctionTheme | | | [var\_set](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var_set.md) | ExpressionFunctionVarSet | | | [var](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.var.md) | ExpressionFunctionVar | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md new file mode 100644 index 00000000000000..b8564a696e6e48 --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [ExpressionFunctionDefinitions](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.md) > [overall\_metric](./kibana-plugin-plugins-expressions-server.expressionfunctiondefinitions.overall_metric.md) + +## ExpressionFunctionDefinitions.overall\_metric property + +Signature: + +```typescript +overall_metric: ExpressionFunctionOverallMetric; +``` diff --git a/src/plugins/expressions/common/expression_functions/specs/index.ts b/src/plugins/expressions/common/expression_functions/specs/index.ts index 20a6f9aac45674..c6d89f41d0e0d3 100644 --- a/src/plugins/expressions/common/expression_functions/specs/index.ts +++ b/src/plugins/expressions/common/expression_functions/specs/index.ts @@ -12,6 +12,7 @@ export * from './var_set'; export * from './var'; export * from './theme'; export * from './cumulative_sum'; +export * from './overall_metric'; export * from './derivative'; export * from './moving_average'; export * from './ui_setting'; diff --git a/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts b/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts new file mode 100644 index 00000000000000..e42112d3a23ed9 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts @@ -0,0 +1,168 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../types'; +import { Datatable } from '../../expression_types'; +import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; + +export interface OverallMetricArgs { + by?: string[]; + inputColumnId: string; + outputColumnId: string; + outputColumnName?: string; + metric: 'sum' | 'min' | 'max' | 'average'; +} + +export type ExpressionFunctionOverallMetric = ExpressionFunctionDefinition< + 'overall_metric', + Datatable, + OverallMetricArgs, + Datatable +>; + +function getValueAsNumberArray(value: unknown) { + if (Array.isArray(value)) { + return value.map((innerVal) => Number(innerVal)); + } else { + return [Number(value)]; + } +} + +/** + * Calculates the overall metric of a specified column in the data table. + * + * Also supports multiple series in a single data table - use the `by` argument + * to specify the columns to split the calculation by. + * For each unique combination of all `by` columns a separate overall metric will be calculated. + * The order of rows won't be changed - this function is not modifying any existing columns, it's only + * adding the specified `outputColumnId` column to every row of the table without adding or removing rows. + * + * Behavior: + * * Will write the overall metric of `inputColumnId` into `outputColumnId` + * * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId` + * * Each cell will contain the calculated metric based on the values of all cells belonging to the current series. + * + * Edge cases: + * * Will return the input table if `inputColumnId` does not exist + * * Will throw an error if `outputColumnId` exists already in provided data table + * * If the row value contains `null` or `undefined`, it will be ignored and overwritten with the overall metric of + * all cells of the same series. + * * For all values besides `null` and `undefined`, the value will be cast to a number before it's added to the + * overall metric of the current series - if this results in `NaN` (like in case of objects), all cells of the + * current series will be set to `NaN`. + * * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings + * before comparison. If the values are objects, the return value of their `toString` method will be used for comparison. + * Missing values (`null` and `undefined`) will be treated as empty strings. + */ +export const overallMetric: ExpressionFunctionOverallMetric = { + name: 'overall_metric', + type: 'datatable', + + inputTypes: ['datatable'], + + help: i18n.translate('expressions.functions.overallMetric.help', { + defaultMessage: 'Calculates the overall sum, min, max or average of a column in a data table', + }), + + args: { + by: { + help: i18n.translate('expressions.functions.overallMetric.args.byHelpText', { + defaultMessage: 'Column to split the overall calculation by', + }), + multi: true, + types: ['string'], + required: false, + }, + metric: { + help: i18n.translate('expressions.functions.overallMetric.metricHelpText', { + defaultMessage: 'Metric to calculate', + }), + types: ['string'], + options: ['sum', 'min', 'max', 'average'], + }, + inputColumnId: { + help: i18n.translate('expressions.functions.overallMetric.args.inputColumnIdHelpText', { + defaultMessage: 'Column to calculate the overall metric of', + }), + types: ['string'], + required: true, + }, + outputColumnId: { + help: i18n.translate('expressions.functions.overallMetric.args.outputColumnIdHelpText', { + defaultMessage: 'Column to store the resulting overall metric in', + }), + types: ['string'], + required: true, + }, + outputColumnName: { + help: i18n.translate('expressions.functions.overallMetric.args.outputColumnNameHelpText', { + defaultMessage: 'Name of the column to store the resulting overall metric in', + }), + types: ['string'], + required: false, + }, + }, + + fn(input, { by, inputColumnId, outputColumnId, outputColumnName, metric }) { + const resultColumns = buildResultColumns( + input, + outputColumnId, + inputColumnId, + outputColumnName + ); + + if (!resultColumns) { + return input; + } + + const accumulators: Partial> = {}; + const valueCounter: Partial> = {}; + input.rows.forEach((row) => { + const bucketIdentifier = getBucketIdentifier(row, by); + const accumulatorValue = accumulators[bucketIdentifier] ?? 0; + + const currentValue = row[inputColumnId]; + if (currentValue != null) { + const currentNumberValues = getValueAsNumberArray(currentValue); + switch (metric) { + case 'average': + valueCounter[bucketIdentifier] = + (valueCounter[bucketIdentifier] ?? 0) + currentNumberValues.length; + case 'sum': + accumulators[bucketIdentifier] = + accumulatorValue + currentNumberValues.reduce((a, b) => a + b, 0); + break; + case 'min': + accumulators[bucketIdentifier] = Math.min(accumulatorValue, ...currentNumberValues); + break; + case 'max': + accumulators[bucketIdentifier] = Math.max(accumulatorValue, ...currentNumberValues); + break; + } + } + }); + if (metric === 'average') { + Object.keys(accumulators).forEach((bucketIdentifier) => { + accumulators[bucketIdentifier] = + accumulators[bucketIdentifier]! / valueCounter[bucketIdentifier]!; + }); + } + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + const bucketIdentifier = getBucketIdentifier(row, by); + newRow[outputColumnId] = accumulators[bucketIdentifier]; + + return newRow; + }), + }; + }, +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts new file mode 100644 index 00000000000000..30354c4e54dc76 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts @@ -0,0 +1,450 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { functionWrapper } from './utils'; +import { ExecutionContext } from '../../../execution/types'; +import { Datatable } from '../../../expression_types/specs/datatable'; +import { overallMetric, OverallMetricArgs } from '../overall_metric'; + +describe('interpreter/functions#overall_metric', () => { + const fn = functionWrapper(overallMetric); + const runFn = (input: Datatable, args: OverallMetricArgs) => + fn(input, args, {} as ExecutionContext) as Datatable; + + it('calculates overall sum', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: 7 }, { val: 3 }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'sum' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([17, 17, 17, 17]); + }); + + it('ignores null or undefined', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{}, { val: null }, { val: undefined }, { val: 1 }, { val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'average' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([3, 3, 3, 3, 3]); + }); + + it('calculates overall sum for multiple series', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3, split: 'B' }, + { val: 4, split: 'A' }, + { val: 5, split: 'A' }, + { val: 6, split: 'A' }, + { val: 7, split: 'B' }, + { val: 8, split: 'B' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], metric: 'sum' } + ); + + expect(result.rows.map((row) => row.output)).toEqual([ + 1 + 4 + 5 + 6, + 2 + 3 + 7 + 8, + 2 + 3 + 7 + 8, + 1 + 4 + 5 + 6, + 1 + 4 + 5 + 6, + 1 + 4 + 5 + 6, + 2 + 3 + 7 + 8, + 2 + 3 + 7 + 8, + ]); + }); + + it('treats missing split column as separate series', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3 }, + { val: 4, split: 'A' }, + { val: 5 }, + { val: 6, split: 'A' }, + { val: 7, split: 'B' }, + { val: 8, split: 'B' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], metric: 'sum' } + ); + expect(result.rows.map((row) => row.output)).toEqual([ + 1 + 4 + 6, + 2 + 7 + 8, + 3 + 5, + 1 + 4 + 6, + 3 + 5, + 1 + 4 + 6, + 2 + 7 + 8, + 2 + 7 + 8, + ]); + }); + + it('treats null like undefined and empty string for split columns', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: 2, split: 'B' }, + { val: 3 }, + { val: 4, split: 'A' }, + { val: 5 }, + { val: 6, split: 'A' }, + { val: 7, split: null }, + { val: 8, split: 'B' }, + { val: 9, split: '' }, + ], + }; + + const result = runFn(table, { + inputColumnId: 'val', + outputColumnId: 'output', + by: ['split'], + metric: 'sum', + }); + expect(result.rows.map((row) => row.output)).toEqual([ + 1 + 4 + 6, + 2 + 8, + 3 + 5 + 7 + 9, + 1 + 4 + 6, + 3 + 5 + 7 + 9, + 1 + 4 + 6, + 3 + 5 + 7 + 9, + 2 + 8, + 3 + 5 + 7 + 9, + ]); + + const result2 = runFn(table, { + inputColumnId: 'val', + outputColumnId: 'output', + by: ['split'], + metric: 'max', + }); + expect(result2.rows.map((row) => row.output)).toEqual([6, 8, 9, 6, 9, 6, 9, 8, 9]); + }); + + it('handles array values', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: [7, 10] }, { val: [3, 1] }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'sum' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([28, 28, 28, 28]); + }); + + it('takes array values into account for average calculation', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: [3, 4] }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'average' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { type: 'number' }, + }); + expect(result.rows.map((row) => row.output)).toEqual([3, 3]); + }); + + it('handles array values for split columns', () => { + const table: Datatable = { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A' }, + { val: [2, 11], split: 'B' }, + { val: 3 }, + { val: 4, split: 'A' }, + { val: 5 }, + { val: 6, split: 'A' }, + { val: 7, split: null }, + { val: 8, split: 'B' }, + { val: [9, 99], split: '' }, + ], + }; + + const result = runFn(table, { + inputColumnId: 'val', + outputColumnId: 'output', + by: ['split'], + metric: 'sum', + }); + expect(result.rows.map((row) => row.output)).toEqual([ + 1 + 4 + 6, + 2 + 11 + 8, + 3 + 5 + 7 + 9 + 99, + 1 + 4 + 6, + 3 + 5 + 7 + 9 + 99, + 1 + 4 + 6, + 3 + 5 + 7 + 9 + 99, + 2 + 11 + 8, + 3 + 5 + 7 + 9 + 99, + ]); + + const result2 = runFn(table, { + inputColumnId: 'val', + outputColumnId: 'output', + by: ['split'], + metric: 'max', + }); + expect(result2.rows.map((row) => row.output)).toEqual([6, 11, 99, 6, 99, 6, 99, 11, 99]); + }); + + it('calculates cumulative sum for multiple series by multiple split columns', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + { id: 'split2', name: 'split2', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: 'A', split2: 'C' }, + { val: 2, split: 'B', split2: 'C' }, + { val: 3, split2: 'C' }, + { val: 4, split: 'A', split2: 'C' }, + { val: 5 }, + { val: 6, split: 'A', split2: 'D' }, + { val: 7, split: 'B', split2: 'D' }, + { val: 8, split: 'B', split2: 'D' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split', 'split2'], metric: 'sum' } + ); + expect(result.rows.map((row) => row.output)).toEqual([1 + 4, 2, 3, 1 + 4, 5, 6, 7 + 8, 7 + 8]); + }); + + it('splits separate series by the string representation of the cell values', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { id: 'val', name: 'val', meta: { type: 'number' } }, + { id: 'split', name: 'split', meta: { type: 'string' } }, + ], + rows: [ + { val: 1, split: { anObj: 3 } }, + { val: 2, split: { anotherObj: 5 } }, + { val: 10, split: 5 }, + { val: 11, split: '5' }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], metric: 'sum' } + ); + + expect(result.rows.map((row) => row.output)).toEqual([1 + 2, 1 + 2, 10 + 11, 10 + 11]); + }); + + it('casts values to number before calculating cumulative sum', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: '7' }, { val: '3' }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'max' } + ); + expect(result.rows.map((row) => row.output)).toEqual([7, 7, 7, 7]); + }); + + it('casts values to number before calculating metric for NaN like values', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [{ val: 5 }, { val: '7' }, { val: {} }, { val: 2 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'min' } + ); + expect(result.rows.map((row) => row.output)).toEqual([NaN, NaN, NaN, NaN]); + }); + + it('skips undefined and null values', () => { + const result = runFn( + { + type: 'datatable', + columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], + rows: [ + { val: null }, + { val: 7 }, + { val: undefined }, + { val: undefined }, + { val: undefined }, + { val: undefined }, + { val: '3' }, + { val: 2 }, + { val: null }, + ], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'average' } + ); + expect(result.rows.map((row) => row.output)).toEqual([4, 4, 4, 4, 4, 4, 4, 4, 4]); + }); + + it('copies over meta information from the source column', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + + field: 'afield', + index: 'anindex', + params: { id: 'number', params: { pattern: '000' } }, + source: 'synthetic', + sourceParams: { + some: 'params', + }, + }, + }, + ], + rows: [{ val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'output', metric: 'sum' } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'output', + meta: { + type: 'number', + + field: 'afield', + index: 'anindex', + params: { id: 'number', params: { pattern: '000' } }, + source: 'synthetic', + sourceParams: { + some: 'params', + }, + }, + }); + }); + + it('sets output name on output column if specified', () => { + const result = runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }, + { + inputColumnId: 'val', + outputColumnId: 'output', + outputColumnName: 'Output name', + metric: 'min', + } + ); + expect(result.columns).toContainEqual({ + id: 'output', + name: 'Output name', + meta: { type: 'number' }, + }); + }); + + it('returns source table if input column does not exist', () => { + const input: Datatable = { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }; + expect( + runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output', metric: 'sum' }) + ).toBe(input); + }); + + it('throws an error if output column exists already', () => { + expect(() => + runFn( + { + type: 'datatable', + columns: [ + { + id: 'val', + name: 'val', + meta: { + type: 'number', + }, + }, + ], + rows: [{ val: 5 }], + }, + { inputColumnId: 'val', outputColumnId: 'val', metric: 'max' } + ) + ).toThrow(); + }); +}); diff --git a/src/plugins/expressions/common/expression_functions/types.ts b/src/plugins/expressions/common/expression_functions/types.ts index e1378a27bdfc29..0ec61b39608a05 100644 --- a/src/plugins/expressions/common/expression_functions/types.ts +++ b/src/plugins/expressions/common/expression_functions/types.ts @@ -18,6 +18,7 @@ import { ExpressionFunctionCumulativeSum, ExpressionFunctionDerivative, ExpressionFunctionMovingAverage, + ExpressionFunctionOverallMetric, } from './specs'; import { ExpressionAstFunction } from '../ast'; import { PersistableStateDefinition } from '../../../kibana_utils/common'; @@ -119,6 +120,7 @@ export interface ExpressionFunctionDefinitions { var: ExpressionFunctionVar; theme: ExpressionFunctionTheme; cumulative_sum: ExpressionFunctionCumulativeSum; + overall_metric: ExpressionFunctionOverallMetric; derivative: ExpressionFunctionDerivative; moving_average: ExpressionFunctionMovingAverage; } diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index a8839c9b0d71e1..f7afc12aa96bad 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -29,6 +29,7 @@ import { derivative, movingAverage, mapColumn, + overallMetric, math, } from '../expression_functions'; @@ -340,6 +341,7 @@ export class ExpressionsService implements PersistableStateService { return Object.values(operationDefinitionMap) .filter(({ hidden }) => !hidden) + .filter( + (operationDefinition) => + !('selectionStyle' in operationDefinition) || + operationDefinition.selectionStyle !== 'hidden' + ) .filter(({ type }) => fieldByOperation[type]?.size || operationWithoutField.has(type)) .sort((op1, op2) => { return op1.displayName.localeCompare(op2.displayName); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts index 815acb8c4169fb..a7741bc60d6469 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/index.ts @@ -9,3 +9,13 @@ export { counterRateOperation, CounterRateIndexPatternColumn } from './counter_r export { cumulativeSumOperation, CumulativeSumIndexPatternColumn } from './cumulative_sum'; export { derivativeOperation, DerivativeIndexPatternColumn } from './differences'; export { movingAverageOperation, MovingAverageIndexPatternColumn } from './moving_average'; +export { + overallSumOperation, + OverallSumIndexPatternColumn, + overallMinOperation, + OverallMinIndexPatternColumn, + overallMaxOperation, + OverallMaxIndexPatternColumn, + overallAverageOperation, + OverallAverageIndexPatternColumn, +} from './overall_metric'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/overall_metric.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/overall_metric.tsx new file mode 100644 index 00000000000000..21ec5387b38539 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/overall_metric.tsx @@ -0,0 +1,224 @@ +/* + * 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 { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '../column_types'; +import { optionallHistogramBasedOperationToExpression } from './utils'; +import { OperationDefinition } from '..'; +import { getFormatFromPreviousColumn } from '../helpers'; + +type OverallMetricIndexPatternColumn = FormattedIndexPatternColumn & + ReferenceBasedIndexPatternColumn & { + operationType: T; + }; + +export type OverallSumIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_sum'>; +export type OverallMinIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_min'>; +export type OverallMaxIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_max'>; +export type OverallAverageIndexPatternColumn = OverallMetricIndexPatternColumn<'overall_average'>; + +function buildOverallMetricOperation>({ + type, + displayName, + ofName, + description, + metric, +}: { + type: T['operationType']; + displayName: string; + ofName: (name?: string) => string; + description: string; + metric: string; +}): OperationDefinition { + return { + type, + priority: 1, + displayName, + input: 'fullReference', + selectionStyle: 'hidden', + requiredReferences: [ + { + input: ['field', 'managedReference', 'fullReference'], + validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, + }, + ], + getPossibleOperation: () => { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + }, + getDefaultLabel: (column, indexPattern, columns) => { + const ref = columns[column.references[0]]; + return ofName( + ref && 'sourceField' in ref + ? indexPattern.getFieldByName(ref.sourceField)?.displayName + : undefined + ); + }, + toExpression: (layer, columnId) => { + return optionallHistogramBasedOperationToExpression(layer, columnId, 'overall_metric', { + metric: [metric], + }); + }, + buildColumn: ({ referenceIds, previousColumn, layer, indexPattern }, columnParams) => { + const ref = layer.columns[referenceIds[0]]; + return { + label: ofName( + ref && 'sourceField' in ref + ? indexPattern.getFieldByName(ref.sourceField)?.displayName + : undefined + ), + dataType: 'number', + operationType: 'overall_sum', + isBucketed: false, + scale: 'ratio', + references: referenceIds, + params: getFormatFromPreviousColumn(previousColumn), + } as T; + }, + isTransferable: () => { + return true; + }, + filterable: false, + shiftable: false, + documentation: { + section: 'calculation', + signature: i18n.translate('xpack.lens.indexPattern.overall_metric', { + defaultMessage: 'metric: number', + }), + description, + }, + }; +} + +export const overallSumOperation = buildOverallMetricOperation({ + type: 'overall_sum', + displayName: i18n.translate('xpack.lens.indexPattern.overallSum', { + defaultMessage: 'Overall sum', + }), + ofName: (name?: string) => { + return i18n.translate('xpack.lens.indexPattern.overallSumOf', { + defaultMessage: 'Overall sum of {name}', + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.incompleteOperation', { + defaultMessage: '(incomplete)', + }), + }, + }); + }, + metric: 'sum', + description: i18n.translate('xpack.lens.indexPattern.overall_sum.documentation', { + defaultMessage: ` +Calculates the sum of a metric of all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. +Other dimensions breaking down the data like top values or filter are treated as separate series. + +If no date histograms or interval functions are used in the current chart, \`overall_sum\` is calculating the sum over all dimensions no matter the used function. + +Example: Percentage of total +\`sum(bytes) / overall_sum(sum(bytes))\` + `, + }), +}); + +export const overallMinOperation = buildOverallMetricOperation({ + type: 'overall_min', + displayName: i18n.translate('xpack.lens.indexPattern.overallMin', { + defaultMessage: 'Overall min', + }), + ofName: (name?: string) => { + return i18n.translate('xpack.lens.indexPattern.overallMinOf', { + defaultMessage: 'Overall min of {name}', + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.incompleteOperation', { + defaultMessage: '(incomplete)', + }), + }, + }); + }, + metric: 'min', + description: i18n.translate('xpack.lens.indexPattern.overall_min.documentation', { + defaultMessage: ` +Calculates the minimum of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. +Other dimensions breaking down the data like top values or filter are treated as separate series. + +If no date histograms or interval functions are used in the current chart, \`overall_min\` is calculating the minimum over all dimensions no matter the used function + +Example: Percentage of range +\`(sum(bytes) - overall_min(sum(bytes)) / (overall_max(bytes) - overall_min(bytes))\` + `, + }), +}); + +export const overallMaxOperation = buildOverallMetricOperation({ + type: 'overall_max', + displayName: i18n.translate('xpack.lens.indexPattern.overallMax', { + defaultMessage: 'Overall max', + }), + ofName: (name?: string) => { + return i18n.translate('xpack.lens.indexPattern.overallMaxOf', { + defaultMessage: 'Overall max of {name}', + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.incompleteOperation', { + defaultMessage: '(incomplete)', + }), + }, + }); + }, + metric: 'max', + description: i18n.translate('xpack.lens.indexPattern.overall_max.documentation', { + defaultMessage: ` +Calculates the maximum of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. +Other dimensions breaking down the data like top values or filter are treated as separate series. + +If no date histograms or interval functions are used in the current chart, \`overall_max\` is calculating the maximum over all dimensions no matter the used function + +Example: Percentage of range +\`(sum(bytes) - overall_min(sum(bytes)) / (overall_max(bytes) - overall_min(bytes))\` + `, + }), +}); + +export const overallAverageOperation = buildOverallMetricOperation( + { + type: 'overall_average', + displayName: i18n.translate('xpack.lens.indexPattern.overallMax', { + defaultMessage: 'Overall max', + }), + ofName: (name?: string) => { + return i18n.translate('xpack.lens.indexPattern.overallAverageOf', { + defaultMessage: 'Overall average of {name}', + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.incompleteOperation', { + defaultMessage: '(incomplete)', + }), + }, + }); + }, + metric: 'average', + description: i18n.translate('xpack.lens.indexPattern.overall_average.documentation', { + defaultMessage: ` +Calculates the average of a metric for all data points of a series in the current chart. A series is defined by a dimension using a date histogram or interval function. +Other dimensions breaking down the data like top values or filter are treated as separate series. + +If no date histograms or interval functions are used in the current chart, \`overall_average\` is calculating the average over all dimensions no matter the used function + +Example: Divergence from the mean: +\`sum(bytes) - overall_average(sum(bytes))\` + `, + }), + } +); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts index 1f4f097c6a7fb5..03b9d6c07709c5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.ts @@ -134,3 +134,35 @@ export function dateBasedOperationToExpression( }, ]; } + +/** + * Creates an expression ast for a date based operation (cumulative sum, derivative, moving average, counter rate) + */ +export function optionallHistogramBasedOperationToExpression( + layer: IndexPatternLayer, + columnId: string, + functionName: string, + additionalArgs: Record = {} +): ExpressionFunctionAST[] { + const currentColumn = (layer.columns[columnId] as unknown) as ReferenceBasedIndexPatternColumn; + const buckets = layer.columnOrder.filter((colId) => layer.columns[colId].isBucketed); + const nonHistogramColumns = buckets.filter( + (colId) => + layer.columns[colId].operationType !== 'date_histogram' && + layer.columns[colId].operationType !== 'range' + )!; + + return [ + { + type: 'function', + function: functionName, + arguments: { + by: nonHistogramColumns.length === buckets.length ? [] : nonHistogramColumns, + inputColumnId: [currentColumn.references[0]], + outputColumnId: [columnId], + outputColumnName: [currentColumn.label], + ...additionalArgs, + }, + }, + ]; +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index a7bf415817797d..c38475f85f47e5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -33,6 +33,14 @@ import { DerivativeIndexPatternColumn, movingAverageOperation, MovingAverageIndexPatternColumn, + OverallSumIndexPatternColumn, + overallSumOperation, + OverallMinIndexPatternColumn, + overallMinOperation, + OverallMaxIndexPatternColumn, + overallMaxOperation, + OverallAverageIndexPatternColumn, + overallAverageOperation, } from './calculations'; import { countOperation, CountIndexPatternColumn } from './count'; import { @@ -71,6 +79,10 @@ export type IndexPatternColumn = | CountIndexPatternColumn | LastValueIndexPatternColumn | CumulativeSumIndexPatternColumn + | OverallSumIndexPatternColumn + | OverallMinIndexPatternColumn + | OverallMaxIndexPatternColumn + | OverallAverageIndexPatternColumn | CounterRateIndexPatternColumn | DerivativeIndexPatternColumn | MovingAverageIndexPatternColumn @@ -98,6 +110,10 @@ export { CounterRateIndexPatternColumn, DerivativeIndexPatternColumn, MovingAverageIndexPatternColumn, + OverallSumIndexPatternColumn, + OverallMinIndexPatternColumn, + OverallMaxIndexPatternColumn, + OverallAverageIndexPatternColumn, } from './calculations'; export { CountIndexPatternColumn } from './count'; export { LastValueIndexPatternColumn } from './last_value'; @@ -126,6 +142,10 @@ const internalOperationDefinitions = [ movingAverageOperation, mathOperation, formulaOperation, + overallSumOperation, + overallMinOperation, + overallMaxOperation, + overallAverageOperation, ]; export { termsOperation } from './terms'; @@ -141,6 +161,10 @@ export { counterRateOperation, derivativeOperation, movingAverageOperation, + overallSumOperation, + overallAverageOperation, + overallMaxOperation, + overallMinOperation, } from './calculations'; export { formulaOperation } from './formula/formula'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts index aa46dd765bd8b8..d55c5d3c00f179 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts @@ -31,4 +31,8 @@ export { CounterRateIndexPatternColumn, DerivativeIndexPatternColumn, MovingAverageIndexPatternColumn, + OverallSumIndexPatternColumn, + OverallMinIndexPatternColumn, + OverallMaxIndexPatternColumn, + OverallAverageIndexPatternColumn, } from './definitions'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts index 7df096c27d9a0d..2ed6e2b3a7bcb0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts @@ -319,6 +319,22 @@ describe('getOperationTypesForField', () => { "operationType": "moving_average", "type": "fullReference", }, + Object { + "operationType": "overall_sum", + "type": "fullReference", + }, + Object { + "operationType": "overall_min", + "type": "fullReference", + }, + Object { + "operationType": "overall_max", + "type": "fullReference", + }, + Object { + "operationType": "overall_average", + "type": "fullReference", + }, Object { "field": "bytes", "operationType": "min", From a946f9bd3da37e44da480e61dd963bcf8b9f2a80 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 16 Jun 2021 10:23:31 +0200 Subject: [PATCH 10/32] add description and owner to kibana.json (#102238) --- x-pack/plugins/lens/kibana.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index a5c19911f60b94..c5b3e72f39159d 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -36,5 +36,10 @@ "kibanaUtils", "kibanaReact", "embeddable" - ] + ], + "owner": { + "name": "Kibana App", + "githubTeam": "kibana-app" + }, + "description": "Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana." } From aea2a384fd49e187dfc5edcf365faa0726d0b7ce Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 16 Jun 2021 10:24:13 +0200 Subject: [PATCH 11/32] Do not send other bucket request on exhaustive terms list (#102097) --- .../_terms_other_bucket_helper.test.ts | 62 +++++++++++++++++++ .../buckets/_terms_other_bucket_helper.ts | 6 +- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts index 2aa0d346afe343..523bbe1f010181 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.test.ts @@ -174,6 +174,57 @@ const nestedTermResponse = { status: 200, }; +const exhaustiveNestedTermResponse = { + took: 10, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: 14005, + max_score: 0, + hits: [], + }, + aggregations: { + '1': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 8325, + buckets: [ + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'ios', doc_count: 2850 }, + { key: 'win xp', doc_count: 2830 }, + { key: '__missing__', doc_count: 1430 }, + ], + }, + key: 'US-with-dash', + doc_count: 2850, + }, + { + '2': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'ios', doc_count: 1850 }, + { key: 'win xp', doc_count: 1830 }, + { key: '__missing__', doc_count: 130 }, + ], + }, + key: 'IN-with-dash', + doc_count: 2830, + }, + ], + }, + }, + status: 200, +}; + const nestedTermResponseNoResults = { took: 10, timed_out: false, @@ -326,6 +377,17 @@ describe('Terms Agg Other bucket helper', () => { } }); + test('does not build query if sum_other_doc_count is 0 (exhaustive terms)', () => { + const aggConfigs = getAggConfigs(nestedTerm.aggs); + expect( + buildOtherBucketAgg( + aggConfigs, + aggConfigs.aggs[1] as IBucketAggConfig, + exhaustiveNestedTermResponse + ) + ).toBeFalsy(); + }); + test('excludes exists filter for scripted fields', () => { const aggConfigs = getAggConfigs(nestedTerm.aggs); aggConfigs.aggs[1].params.field.scripted = true; diff --git a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts index 372d487bcf7a39..2a1cd873f62822 100644 --- a/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/plugins/data/common/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -156,6 +156,7 @@ export const buildOtherBucketAgg = ( }; let noAggBucketResults = false; + let exhaustiveBuckets = true; // recursively create filters for all parent aggregation buckets const walkBucketTree = ( @@ -175,6 +176,9 @@ export const buildOtherBucketAgg = ( const newAggIndex = aggIndex + 1; const newAgg = bucketAggs[newAggIndex]; const currentAgg = bucketAggs[aggIndex]; + if (aggIndex === index && agg && agg.sum_other_doc_count > 0) { + exhaustiveBuckets = false; + } if (aggIndex < index) { each(agg.buckets, (bucket: any, bucketObjKey) => { const bucketKey = currentAgg.getKey( @@ -223,7 +227,7 @@ export const buildOtherBucketAgg = ( walkBucketTree(0, response.aggregations, bucketAggs[0].id, [], ''); // bail if there were no bucket results - if (noAggBucketResults) { + if (noAggBucketResults || exhaustiveBuckets) { return false; } From 5c90bacce543f4549b57252f7efa8ea0925c09a5 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 16 Jun 2021 10:55:22 +0200 Subject: [PATCH 12/32] do not throw execa error when building ts refs (#102154) --- src/dev/typescript/build_ts_refs.ts | 16 ++++++++++++---- src/dev/typescript/run_type_check_cli.ts | 6 +++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/dev/typescript/build_ts_refs.ts b/src/dev/typescript/build_ts_refs.ts index 2e25827996e453..26425b7a3e61df 100644 --- a/src/dev/typescript/build_ts_refs.ts +++ b/src/dev/typescript/build_ts_refs.ts @@ -13,12 +13,20 @@ import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; export const REF_CONFIG_PATHS = [Path.resolve(REPO_ROOT, 'tsconfig.refs.json')]; -export async function buildAllTsRefs(log: ToolingLog) { +export async function buildAllTsRefs(log: ToolingLog): Promise<{ failed: boolean }> { for (const path of REF_CONFIG_PATHS) { const relative = Path.relative(REPO_ROOT, path); log.debug(`Building TypeScript projects refs for ${relative}...`); - await execa(require.resolve('typescript/bin/tsc'), ['-b', relative, '--pretty'], { - cwd: REPO_ROOT, - }); + const { failed, stdout } = await execa( + require.resolve('typescript/bin/tsc'), + ['-b', relative, '--pretty'], + { + cwd: REPO_ROOT, + reject: false, + } + ); + log.info(stdout); + if (failed) return { failed }; } + return { failed: false }; } diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index f95c230f44b9e4..d9e9eb036fe0f2 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -69,7 +69,11 @@ export async function runTypeCheckCli() { process.exit(); } - await buildAllTsRefs(log); + const { failed } = await buildAllTsRefs(log); + if (failed) { + log.error('Unable to build TS project refs'); + process.exit(1); + } const tscArgs = [ // composite project cannot be used with --noEmit From aa97040bb6acb32b67b2a4c005ca905c50c5a5c9 Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Wed, 16 Jun 2021 10:01:43 +0100 Subject: [PATCH 13/32] [APM-UI][e2e] discard CI builds more often (#102217) --- .ci/end2end.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 87b64437deafcd..f1095f8035b6c4 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -13,12 +13,12 @@ pipeline { BASE_DIR = 'src/github.com/elastic/kibana' HOME = "${env.WORKSPACE}" E2E_DIR = 'x-pack/plugins/apm/e2e' - PIPELINE_LOG_LEVEL = 'DEBUG' + PIPELINE_LOG_LEVEL = 'INFO' KBN_OPTIMIZER_THEMES = 'v7light' } options { timeout(time: 1, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '40', artifactNumToKeepStr: '20', daysToKeepStr: '30')) + buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10', daysToKeepStr: '30')) timestamps() ansiColor('xterm') disableResume() From 037d7aeb8f11057e0b4696e9f6aeafd321b32220 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Wed, 16 Jun 2021 12:23:10 +0200 Subject: [PATCH 14/32] Enhance cases bulk deletion action dialog message (#101403) Differentiate the dialog message on the deletion of one item from the deletion of multiple items. Simplifies CasesTableUtilityBar by handling the selection of multiple and single cases in the same way. --- x-pack/plugins/cases/common/ui/types.ts | 2 +- .../cases/public/common/translations.ts | 12 +++---- .../public/components/all_cases/actions.tsx | 4 +-- .../public/components/all_cases/columns.tsx | 1 - .../components/all_cases/utility_bar.tsx | 33 +++++-------------- .../components/case_action_bar/actions.tsx | 3 +- .../components/confirm_delete_case/index.tsx | 18 ++++------ .../confirm_delete_case/translations.ts | 26 +++++---------- .../containers/use_delete_cases.test.tsx | 6 ++-- 9 files changed, 36 insertions(+), 69 deletions(-) diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index 284f5e706292cc..1dbb633e32adf0 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -153,7 +153,7 @@ export interface ActionLicense { export interface DeleteCase { id: string; type: CaseType | null; - title?: string; + title: string; } export interface FieldMappings { diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index 85cfb60b1d6b81..f1bfde4cc44851 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -30,13 +30,11 @@ export const CANCEL = i18n.translate('xpack.cases.caseView.cancel', { defaultMessage: 'Cancel', }); -export const DELETE_CASE = i18n.translate('xpack.cases.confirmDeleteCase.deleteCase', { - defaultMessage: 'Delete case', -}); - -export const DELETE_CASES = i18n.translate('xpack.cases.confirmDeleteCase.deleteCases', { - defaultMessage: 'Delete cases', -}); +export const DELETE_CASE = (quantity: number = 1) => + i18n.translate('xpack.cases.confirmDeleteCase.deleteCase', { + values: { quantity }, + defaultMessage: `Delete {quantity, plural, =1 {case} other {cases}}`, + }); export const NAME = i18n.translate('xpack.cases.caseView.name', { defaultMessage: 'Name', diff --git a/x-pack/plugins/cases/public/components/all_cases/actions.tsx b/x-pack/plugins/cases/public/components/all_cases/actions.tsx index 8742b8fea23a42..4820b10308934f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/actions.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/actions.tsx @@ -80,9 +80,9 @@ export const getActions = ({ makeInProgressAction, closeCaseAction, { - description: i18n.DELETE_CASE, + description: i18n.DELETE_CASE(), icon: 'trash', - name: i18n.DELETE_CASE, + name: i18n.DELETE_CASE(), onClick: deleteCaseOnClick, type: 'icon', 'data-test-subj': 'action-delete', diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx index 947d405d188cf0..a5a299851d975a 100644 --- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx @@ -306,7 +306,6 @@ export const useCasesColumns = ({ diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index d0981c38385e96..a2b4c14c0278a8 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -41,12 +41,8 @@ export const CasesTableUtilityBar: FunctionComponent = ({ refreshCases, selectedCases, }) => { - const [deleteBulk, setDeleteBulk] = useState([]); - const [deleteThisCase, setDeleteThisCase] = useState({ - title: '', - id: '', - type: null, - }); + const [deleteCases, setDeleteCases] = useState([]); + // Delete case const { dispatchResetIsDeleted, @@ -86,24 +82,15 @@ export const CasesTableUtilityBar: FunctionComponent = ({ const toggleBulkDeleteModal = useCallback( (cases: Case[]) => { handleToggleModal(); - if (cases.length === 1) { - const singleCase = cases[0]; - if (singleCase) { - return setDeleteThisCase({ - id: singleCase.id, - title: singleCase.title, - type: singleCase.type, - }); - } - } + const convertToDeleteCases: DeleteCase[] = cases.map(({ id, title, type }) => ({ id, title, type, })); - setDeleteBulk(convertToDeleteCases); + setDeleteCases(convertToDeleteCases); }, - [setDeleteBulk, handleToggleModal] + [setDeleteCases, handleToggleModal] ); const handleUpdateCaseStatus = useCallback( @@ -128,6 +115,7 @@ export const CasesTableUtilityBar: FunctionComponent = ({ ), [selectedCases, filterOptions.status, toggleBulkDeleteModal, handleUpdateCaseStatus] ); + return ( @@ -159,14 +147,11 @@ export const CasesTableUtilityBar: FunctionComponent = ({ 0} + caseQuantity={deleteCases.length} onCancel={handleToggleModal} - onConfirm={handleOnDeleteConfirm.bind( - null, - deleteBulk.length > 0 ? deleteBulk : [deleteThisCase] - )} + onConfirm={handleOnDeleteConfirm.bind(null, deleteCases)} /> ); diff --git a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx index 922ffd09aaac9d..c2578dc3debdb6 100644 --- a/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx +++ b/x-pack/plugins/cases/public/components/case_action_bar/actions.tsx @@ -41,7 +41,7 @@ const ActionsComponent: React.FC = ({ { disabled, iconType: 'trash', - label: i18n.DELETE_CASE, + label: i18n.DELETE_CASE(), onClick: handleToggleModal, }, ...(currentExternalIncident != null && !isEmpty(currentExternalIncident?.externalUrl) @@ -67,7 +67,6 @@ const ActionsComponent: React.FC = ({ void; onConfirm: () => void; } @@ -20,7 +20,7 @@ interface ConfirmDeleteCaseModalProps { const ConfirmDeleteCaseModalComp: React.FC = ({ caseTitle, isModalVisible, - isPlural, + caseQuantity = 1, onCancel, onConfirm, }) => { @@ -31,20 +31,14 @@ const ConfirmDeleteCaseModalComp: React.FC = ({ - {isPlural ? i18n.CONFIRM_QUESTION_PLURAL : i18n.CONFIRM_QUESTION} + {i18n.CONFIRM_QUESTION(caseQuantity)} ); }; diff --git a/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts b/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts index 0400c4c7fef413..f8e4ab2a83a738 100644 --- a/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts +++ b/x-pack/plugins/cases/public/components/confirm_delete_case/translations.ts @@ -14,23 +14,15 @@ export const DELETE_TITLE = (caseTitle: string) => defaultMessage: 'Delete "{caseTitle}"', }); -export const DELETE_THIS_CASE = (caseTitle: string) => - i18n.translate('xpack.cases.confirmDeleteCase.deleteThisCase', { - defaultMessage: 'Delete this case', +export const DELETE_SELECTED_CASES = (quantity: number, title: string) => + i18n.translate('xpack.cases.confirmDeleteCase.selectedCases', { + values: { quantity, title }, + defaultMessage: 'Delete "{quantity, plural, =1 {{title}} other {Selected {quantity} cases}}"', }); -export const CONFIRM_QUESTION = i18n.translate('xpack.cases.confirmDeleteCase.confirmQuestion', { - defaultMessage: - 'By deleting this case, all related case data will be permanently removed and you will no longer be able to push data to an external incident management system. Are you sure you wish to proceed?', -}); -export const DELETE_SELECTED_CASES = i18n.translate('xpack.cases.confirmDeleteCase.selectedCases', { - defaultMessage: 'Delete selected cases', -}); - -export const CONFIRM_QUESTION_PLURAL = i18n.translate( - 'xpack.cases.confirmDeleteCase.confirmQuestionPlural', - { +export const CONFIRM_QUESTION = (quantity: number) => + i18n.translate('xpack.cases.confirmDeleteCase.confirmQuestion', { + values: { quantity }, defaultMessage: - 'By deleting these cases, all related case data will be permanently removed and you will no longer be able to push data to an external incident management system. Are you sure you wish to proceed?', - } -); + 'By deleting {quantity, plural, =1 {this case} other {these cases}}, all related case data will be permanently removed and you will no longer be able to push data to an external incident management system. Are you sure you wish to proceed?', + }); diff --git a/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx index e86ed0c036974a..691af580b333a8 100644 --- a/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_delete_cases.test.tsx @@ -17,9 +17,9 @@ jest.mock('../common/lib/kibana'); describe('useDeleteCases', () => { const abortCtrl = new AbortController(); const deleteObj = [ - { id: '1', type: CaseType.individual }, - { id: '2', type: CaseType.individual }, - { id: '3', type: CaseType.individual }, + { id: '1', type: CaseType.individual, title: 'case 1' }, + { id: '2', type: CaseType.individual, title: 'case 2' }, + { id: '3', type: CaseType.individual, title: 'case 3' }, ]; const deleteArr = ['1', '2', '3']; it('init', async () => { From 35e10ba77013715e381a2373790d67d94df825f1 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Wed, 16 Jun 2021 11:31:10 +0100 Subject: [PATCH 15/32] [ML] Adds optimizations for Logs UI anomaly detection jobs (#102191) * [ML] Adds optimizations for Logs UI anomaly detection jobs * [ML] Increment version for log-entry-categories-count job Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../containers/logs/log_analysis/api/ml_setup_module_api.ts | 4 ++++ .../modules/log_entry_categories/module_descriptor.ts | 1 + .../log_analysis/modules/log_entry_rate/module_descriptor.ts | 1 + .../logs_ui_categories/ml/log_entry_categories_count.json | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts index ea1567d6056f1d..6304471e818fa9 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts @@ -21,6 +21,7 @@ interface RequestArgs { jobOverrides?: SetupMlModuleJobOverrides[]; datafeedOverrides?: SetupMlModuleDatafeedOverrides[]; query?: object; + useDedicatedIndex?: boolean; } export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { @@ -34,6 +35,7 @@ export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: Http jobOverrides = [], datafeedOverrides = [], query, + useDedicatedIndex = false, } = requestArgs; const response = await fetch(`/api/ml/modules/setup/${moduleId}`, { @@ -48,6 +50,7 @@ export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: Http jobOverrides, datafeedOverrides, query, + useDedicatedIndex, }) ), }); @@ -78,6 +81,7 @@ const setupMlModuleRequestParamsRT = rt.intersection([ startDatafeed: rt.boolean, jobOverrides: rt.array(setupMlModuleJobOverridesRT), datafeedOverrides: rt.array(setupMlModuleDatafeedOverridesRT), + useDedicatedIndex: rt.boolean, }), rt.exact( rt.partial({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts index af2bd1802042a4..6823ed173a740c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts @@ -124,6 +124,7 @@ const setUpModule = async ( jobOverrides, datafeedOverrides, query, + useDedicatedIndex: true, }, fetch ); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts index 9704afd80e9ea6..c4c939d0ebb9d5 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts @@ -116,6 +116,7 @@ const setUpModule = async ( jobOverrides, datafeedOverrides, query, + useDedicatedIndex: true, }, fetch ); diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json index ad7da3330bb6cd..90f88275cb6d0b 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/logs_ui_categories/ml/log_entry_categories_count.json @@ -22,7 +22,7 @@ ], "per_partition_categorization": { "enabled": true, - "stop_on_warn": false + "stop_on_warn": true } }, "analysis_limits": { @@ -38,6 +38,6 @@ }, "custom_settings": { "created_by": "ml-module-logs-ui-categories", - "job_revision": 1 + "job_revision": 2 } } From d3c1f7c54d83eb5dbb27f699af16eb8637e82a2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Casper=20H=C3=BCbertz?= Date: Wed, 16 Jun 2021 14:38:04 +0200 Subject: [PATCH 16/32] [Observability] Updating header menu links across Observability apps (#101472) * [Observability] POC aligning menu links across apps * [APM] Changed guttersize * [APM] Replace placeholder button * [Uptime] Remove icon from Settings header link * [APM] Reordered anomaly detection and alerts * [Logs] Remove icon from settings and change guttersize * [Metrics] Remove icon from settings and change guttersize * [Logs] Change button style of `isStreaming` state Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../app/RumDashboard/ActionMenu/index.tsx | 21 +++++++++++++++++-- .../alerting_popover_flyout.tsx | 3 ++- .../anomaly_detection_setup_link.tsx | 9 ++++---- .../shared/apm_header_action_menu/index.tsx | 10 +++------ .../components/metrics_alert_dropdown.tsx | 8 ++++++- .../components/alert_dropdown.tsx | 8 ++++++- .../components/logging/log_datepicker.tsx | 15 +++++-------- .../infra/public/pages/logs/page_content.tsx | 4 ++-- .../infra/public/pages/metrics/index.tsx | 4 ++-- .../anomaly_detection_flyout.tsx | 2 ++ .../common/header/action_menu_content.tsx | 9 ++++---- .../alerts/toggle_alert_flyout_button.tsx | 2 ++ 12 files changed, 61 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx index 6d04996b5f24c5..20d930d28599f9 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ActionMenu/index.tsx @@ -55,26 +55,43 @@ export function UXActionMenu({ http?.basePath.get() ); + const kibana = useKibana(); + return ( {ANALYZE_MESSAGE}

}> {ANALYZE_DATA}
+ + + {i18n.translate('xpack.apm.addDataButtonLabel', { + defaultMessage: 'Add data', + })} + +
); diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx index 95acc55196c543..5b4f4e24af44d5 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/alerting_popover_flyout.tsx @@ -66,7 +66,8 @@ export function AlertingPopoverAndFlyout({ const button = ( setPopoverOpen((prevState) => !prevState)} diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx index ade49bc7e3aa4f..28c000310346d5 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/anomaly_detection_setup_link.tsx @@ -42,14 +42,15 @@ export function AnomalyDetectionSetupLink() { return ( {canGetJobs && hasValidLicense ? ( ) : ( - + )} {ANOMALY_DETECTION_LINK_LABEL} @@ -64,7 +65,7 @@ export function MissingJobsAlert({ environment }: { environment?: string }) { anomalyDetectionJobsStatus, } = useAnomalyDetectionJobsContext(); - const defaultIcon = ; + const defaultIcon = ; if (anomalyDetectionJobsStatus === FETCH_STATUS.LOADING) { return ; @@ -92,7 +93,7 @@ export function MissingJobsAlert({ environment }: { environment?: string }) { return ( - + ); } diff --git a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx index 134941990a0f4c..86f0d3fde1cd5d 100644 --- a/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/apm_header_action_menu/index.tsx @@ -40,16 +40,13 @@ export function ApmHeaderActionMenu() { } return ( - - + + {i18n.translate('xpack.apm.settingsLinkLabel', { defaultMessage: 'Settings', })} + {canAccessML && } {isAlertingAvailable && ( )} - {canAccessML && } { panelPaddingSize="none" anchorPosition="downLeft" button={ - + } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx index 7cd6295cdcf408..66c77fbf875a45 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx @@ -83,7 +83,13 @@ export const AlertDropdown = () => { + } diff --git a/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx b/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx index b146da53caf6f6..4f396ca7da4951 100644 --- a/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_datepicker.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiButtonEmpty } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSuperDatePicker, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; interface LogDatepickerProps { @@ -49,24 +49,19 @@ export const LogDatepicker: React.FC = ({
{isStreaming ? ( - + - + ) : ( - + - + )}
diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index 35e24700619f8d..c7b145b4b01431 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -76,9 +76,9 @@ export const LogsPageContent: React.FunctionComponent = () => { {setHeaderActionMenu && ( - + - + {settingsTabTitle} diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index cda72e96012fe4..e52d1e90d7efd2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -84,9 +84,9 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { {setHeaderActionMenu && ( - + - + {settingsTabTitle} diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx index 5438209ae9c6b5..d2cd4f87a53422 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomaly_detection_flyout.tsx @@ -51,6 +51,8 @@ export const AnomalyDetectionFlyout = () => { return ( <> + @@ -72,12 +72,13 @@ export function ActionMenuContent(): React.ReactElement { {ANALYZE_MESSAGE}

}> {ANALYZE_DATA} diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index fe507236569ec3..a1b745d07924ef 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -124,6 +124,8 @@ export const ToggleAlertFlyoutButtonComponent: React.FC = ({ Date: Wed, 16 Jun 2021 15:15:46 +0200 Subject: [PATCH 17/32] [Security solutions][Endpoint] Break long names on remove trusted apps/event filters dialog (#102307) * Break long names on remove trusted apps/event filters dialog. * Removes wrong class --- .../components/event_filter_delete_modal.tsx | 2 +- .../trusted_app_deletion_dialog.test.tsx.snap | 18 ++++++++++++------ .../view/trusted_app_deletion_dialog.tsx | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.tsx index 74a023965a57d5..653469d304978c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.tsx @@ -91,7 +91,7 @@ export const EventFilterDeleteModal = memo<{}>(() => { {eventFilter?.name} }} + values={{ name: {eventFilter?.name} }} />

diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap index 5ab58914ff8b1c..0343ab62b9773d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/__snapshots__/trusted_app_deletion_dialog.test.tsx.snap @@ -56,9 +56,11 @@ exports[`TrustedAppDeletionDialog renders correctly when deletion failed 1`] = ` >

You are removing trusted application " - + trusted app 3 - + ".

@@ -158,9 +160,11 @@ exports[`TrustedAppDeletionDialog renders correctly when deletion is in progress >

You are removing trusted application " - + trusted app 3 - + ".

@@ -265,9 +269,11 @@ exports[`TrustedAppDeletionDialog renders correctly when dialog started 1`] = ` >

You are removing trusted application " - + trusted app 3 - + ".

diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx index bffd9806103721..3afa2642eba121 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_app_deletion_dialog.tsx @@ -45,7 +45,7 @@ const getTranslations = (entry: Immutable | undefined) => ({ {entry?.name} }} + values={{ name: {entry?.name} }} /> ), subMessage: ( From 78b803be2b6ce88f79af145c3dffed8a2f7c1206 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 16 Jun 2021 16:16:56 +0300 Subject: [PATCH 18/32] Add telemetry for editor clicks events (#100664) * Track editor clicks events Closes: #98949 * add create and open telemetries * add telemetry for dashboard * remove hardcoded originatingApp for lens * DashboardConstants.DASHBOARDS_ID -> DashboardConstants.DASHBOARD_ID Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...ble-public.addpanelaction._constructor_.md | 3 +- ...lugins-embeddable-public.addpanelaction.md | 2 +- ...ns-embeddable-public.openaddpanelflyout.md | 3 +- .../application/top_nav/dashboard_top_nav.tsx | 6 ++- .../application/top_nav/editor_menu.tsx | 2 +- .../public/lib/panel/embeddable_panel.tsx | 5 +- .../add_panel/add_panel_action.ts | 5 +- .../add_panel/add_panel_flyout.tsx | 32 +++++++++++-- .../add_panel/open_add_panel_flyout.tsx | 4 ++ src/plugins/embeddable/public/public.api.md | 5 +- .../public/finder/saved_object_finder.tsx | 1 + .../visualize_embeddable_factory.tsx | 3 ++ .../vis_types/vis_type_alias_registry.ts | 2 + .../public/wizard/new_vis_modal.tsx | 2 +- src/plugins/visualize/kibana.json | 3 +- .../visualize/public/application/types.ts | 46 +++++++++++-------- .../application/utils/get_table_columns.tsx | 17 ++++++- .../application/utils/get_top_nav_config.tsx | 19 +++++++- src/plugins/visualize/public/plugin.ts | 43 ++++++++++------- src/plugins/visualize/public/services.ts | 10 +++- x-pack/plugins/lens/public/app_plugin/app.tsx | 1 + .../lens/public/app_plugin/mounter.tsx | 3 +- .../app_plugin/save_modal_container.tsx | 8 ++++ .../plugins/lens/public/app_plugin/types.ts | 2 + x-pack/plugins/lens/public/plugin.ts | 3 +- x-pack/plugins/lens/public/vis_type_alias.ts | 1 + 26 files changed, 175 insertions(+), 56 deletions(-) diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md index 388f0e064d8661..e51c465e912e68 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `AddPanelAction` class Signature: ```typescript -constructor(getFactory: EmbeddableStart['getEmbeddableFactory'], getAllFactories: EmbeddableStart['getEmbeddableFactories'], overlays: OverlayStart, notifications: NotificationsStart, SavedObjectFinder: React.ComponentType); +constructor(getFactory: EmbeddableStart['getEmbeddableFactory'], getAllFactories: EmbeddableStart['getEmbeddableFactories'], overlays: OverlayStart, notifications: NotificationsStart, SavedObjectFinder: React.ComponentType, reportUiCounter?: ((appName: string, type: import("@kbn/analytics").UiCounterMetricType, eventNames: string | string[], count?: number | undefined) => void) | undefined); ``` ## Parameters @@ -21,4 +21,5 @@ constructor(getFactory: EmbeddableStart['getEmbeddableFactory'], getAllFactories | overlays | OverlayStart | | | notifications | NotificationsStart | | | SavedObjectFinder | React.ComponentType<any> | | +| reportUiCounter | ((appName: string, type: import("@kbn/analytics").UiCounterMetricType, eventNames: string | string[], count?: number | undefined) => void) | undefined | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md index 74a6c2b2183a2e..947e506f72b435 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.addpanelaction.md @@ -14,7 +14,7 @@ export declare class AddPanelAction implements Action | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(getFactory, getAllFactories, overlays, notifications, SavedObjectFinder)](./kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md) | | Constructs a new instance of the AddPanelAction class | +| [(constructor)(getFactory, getAllFactories, overlays, notifications, SavedObjectFinder, reportUiCounter)](./kibana-plugin-plugins-embeddable-public.addpanelaction._constructor_.md) | | Constructs a new instance of the AddPanelAction class | ## Properties diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md index 90caaa3035b348..db45b691b446eb 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.openaddpanelflyout.md @@ -15,6 +15,7 @@ export declare function openAddPanelFlyout(options: { notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; + reportUiCounter?: UsageCollectionStart['reportUiCounter']; }): OverlayRef; ``` @@ -22,7 +23,7 @@ export declare function openAddPanelFlyout(options: { | Parameter | Type | Description | | --- | --- | --- | -| options | {
embeddable: IContainer;
getFactory: EmbeddableStart['getEmbeddableFactory'];
getAllFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: OverlayStart;
notifications: NotificationsStart;
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
} | | +| options | {
embeddable: IContainer;
getFactory: EmbeddableStart['getEmbeddableFactory'];
getAllFactories: EmbeddableStart['getEmbeddableFactories'];
overlays: OverlayStart;
notifications: NotificationsStart;
SavedObjectFinder: React.ComponentType<any>;
showCreateNewMenu?: boolean;
reportUiCounter?: UsageCollectionStart['reportUiCounter'];
} | | Returns: diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 1cfa39d5e0e79b..e5f89bd6a8e909 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -132,7 +132,7 @@ export function DashboardTopNav({ const trackUiMetric = usageCollection?.reportUiCounter.bind( usageCollection, - DashboardConstants.DASHBOARDS_ID + DashboardConstants.DASHBOARD_ID ); useEffect(() => { @@ -163,6 +163,7 @@ export function DashboardTopNav({ notifications: core.notifications, overlays: core.overlays, SavedObjectFinder: getSavedObjectFinder(core.savedObjects, uiSettings), + reportUiCounter: usageCollection?.reportUiCounter, }), })); } @@ -174,6 +175,7 @@ export function DashboardTopNav({ core.savedObjects, core.overlays, uiSettings, + usageCollection, ]); const createNewVisType = useCallback( @@ -183,7 +185,7 @@ export function DashboardTopNav({ if (visType) { if (trackUiMetric) { - trackUiMetric(METRIC_TYPE.CLICK, visType.name); + trackUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); } if ('aliasPath' in visType) { diff --git a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx index 90cf0fcd571a15..74d725bb4d1045 100644 --- a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx @@ -51,7 +51,7 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { const trackUiMetric = usageCollection?.reportUiCounter.bind( usageCollection, - DashboardConstants.DASHBOARDS_ID + DashboardConstants.DASHBOARD_ID ); const createNewAggsBasedVis = useCallback( diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 1214625fe530f2..8cf2de8c807439 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -14,6 +14,7 @@ import deepEqual from 'fast-deep-equal'; import { buildContextMenuForActions, UiActionsService, Action } from '../ui_actions'; import { CoreStart, OverlayStart } from '../../../../../core/public'; import { toMountPoint } from '../../../../kibana_react/public'; +import { UsageCollectionStart } from '../../../../usage_collection/public'; import { Start as InspectorStartContract } from '../inspector'; import { @@ -62,6 +63,7 @@ interface Props { SavedObjectFinder: React.ComponentType; stateTransfer?: EmbeddableStateTransfer; hideHeader?: boolean; + reportUiCounter?: UsageCollectionStart['reportUiCounter']; } interface State { @@ -312,7 +314,8 @@ export class EmbeddablePanel extends React.Component { this.props.getAllEmbeddableFactories, this.props.overlays, this.props.notifications, - this.props.SavedObjectFinder + this.props.SavedObjectFinder, + this.props.reportUiCounter ), inspectPanel: new InspectPanelAction(this.props.inspector), removePanel: new RemovePanelAction(), diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts index 8b6f81a199c445..49be1c3ce01233 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_action.ts @@ -13,6 +13,7 @@ import { EmbeddableStart } from 'src/plugins/embeddable/public/plugin'; import { ViewMode } from '../../../../types'; import { openAddPanelFlyout } from './open_add_panel_flyout'; import { IContainer } from '../../../../containers'; +import { UsageCollectionStart } from '../../../../../../../usage_collection/public'; export const ACTION_ADD_PANEL = 'ACTION_ADD_PANEL'; @@ -29,7 +30,8 @@ export class AddPanelAction implements Action { private readonly getAllFactories: EmbeddableStart['getEmbeddableFactories'], private readonly overlays: OverlayStart, private readonly notifications: NotificationsStart, - private readonly SavedObjectFinder: React.ComponentType + private readonly SavedObjectFinder: React.ComponentType, + private readonly reportUiCounter?: UsageCollectionStart['reportUiCounter'] ) {} public getDisplayName() { @@ -60,6 +62,7 @@ export class AddPanelAction implements Action { overlays: this.overlays, notifications: this.notifications, SavedObjectFinder: this.SavedObjectFinder, + reportUiCounter: this.reportUiCounter, }); } } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx index 6d6a68d7e5e2aa..eb4f0b30c51102 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/add_panel_flyout.tsx @@ -9,15 +9,17 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { ReactElement } from 'react'; -import { CoreSetup } from 'src/core/public'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { CoreSetup, SavedObjectAttributes, SimpleSavedObject } from 'src/core/public'; import { EuiContextMenuItem, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; -import { EmbeddableStart } from 'src/plugins/embeddable/public'; +import { EmbeddableFactory, EmbeddableStart } from 'src/plugins/embeddable/public'; import { IContainer } from '../../../../containers'; import { EmbeddableFactoryNotFoundError } from '../../../../errors'; import { SavedObjectFinderCreateNew } from './saved_object_finder_create_new'; import { SavedObjectEmbeddableInput } from '../../../../embeddables'; +import { UsageCollectionStart } from '../../../../../../../usage_collection/public'; interface Props { onClose: () => void; @@ -27,6 +29,7 @@ interface Props { notifications: CoreSetup['notifications']; SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; + reportUiCounter?: UsageCollectionStart['reportUiCounter']; } interface State { @@ -84,7 +87,12 @@ export class AddPanelFlyout extends React.Component { } }; - public onAddPanel = async (savedObjectId: string, savedObjectType: string, name: string) => { + public onAddPanel = async ( + savedObjectId: string, + savedObjectType: string, + name: string, + so: SimpleSavedObject + ) => { const factoryForSavedObjectType = [...this.props.getAllFactories()].find( (factory) => factory.savedObjectMetaData && factory.savedObjectMetaData.type === savedObjectType @@ -98,9 +106,27 @@ export class AddPanelFlyout extends React.Component { { savedObjectId } ); + this.doTelemetryForAddEvent(this.props.container.type, factoryForSavedObjectType, so); + this.showToast(name); }; + private doTelemetryForAddEvent( + appName: string, + factoryForSavedObjectType: EmbeddableFactory, + so: SimpleSavedObject + ) { + const { reportUiCounter } = this.props; + + if (reportUiCounter) { + const type = factoryForSavedObjectType.savedObjectMetaData?.getSavedObjectSubType + ? factoryForSavedObjectType.savedObjectMetaData.getSavedObjectSubType(so) + : factoryForSavedObjectType.type; + + reportUiCounter(appName, METRIC_TYPE.CLICK, `${type}:add`); + } + } + private getCreateMenuItems(): ReactElement[] { return [...this.props.getAllFactories()] .filter( diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx index f0c6e81644b3d0..fe54b3d134aa0b 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/add_panel/open_add_panel_flyout.tsx @@ -12,6 +12,7 @@ import { EmbeddableStart } from '../../../../../plugin'; import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IContainer } from '../../../../containers'; import { AddPanelFlyout } from './add_panel_flyout'; +import { UsageCollectionStart } from '../../../../../../../usage_collection/public'; export function openAddPanelFlyout(options: { embeddable: IContainer; @@ -21,6 +22,7 @@ export function openAddPanelFlyout(options: { notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; + reportUiCounter?: UsageCollectionStart['reportUiCounter']; }): OverlayRef { const { embeddable, @@ -30,6 +32,7 @@ export function openAddPanelFlyout(options: { notifications, SavedObjectFinder, showCreateNewMenu, + reportUiCounter, } = options; const flyoutSession = overlays.openFlyout( toMountPoint( @@ -43,6 +46,7 @@ export function openAddPanelFlyout(options: { getFactory={getFactory} getAllFactories={getAllFactories} notifications={notifications} + reportUiCounter={reportUiCounter} SavedObjectFinder={SavedObjectFinder} showCreateNewMenu={showCreateNewMenu} /> diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index 2a577e6167be5f..af708f9a5e6592 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -63,6 +63,7 @@ import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiComponent } from 'src/plugins/kibana_utils/public'; +import { UiCounterMetricType } from '@kbn/analytics'; import { UnregisterCallback } from 'history'; import { URL } from 'url'; import { UserProvidedValues } from 'src/core/server/types'; @@ -95,7 +96,7 @@ export interface Adapters { // @public (undocumented) export class AddPanelAction implements Action_3 { // Warning: (ae-forgotten-export) The symbol "React" needs to be exported by the entry point index.d.ts - constructor(getFactory: EmbeddableStart_2['getEmbeddableFactory'], getAllFactories: EmbeddableStart_2['getEmbeddableFactories'], overlays: OverlayStart_2, notifications: NotificationsStart_2, SavedObjectFinder: React_2.ComponentType); + constructor(getFactory: EmbeddableStart_2['getEmbeddableFactory'], getAllFactories: EmbeddableStart_2['getEmbeddableFactories'], overlays: OverlayStart_2, notifications: NotificationsStart_2, SavedObjectFinder: React_2.ComponentType, reportUiCounter?: ((appName: string, type: import("@kbn/analytics").UiCounterMetricType, eventNames: string | string[], count?: number | undefined) => void) | undefined); // (undocumented) execute(context: ActionExecutionContext_2): Promise; // (undocumented) @@ -729,6 +730,7 @@ export function openAddPanelFlyout(options: { notifications: NotificationsStart_2; SavedObjectFinder: React.ComponentType; showCreateNewMenu?: boolean; + reportUiCounter?: UsageCollectionStart['reportUiCounter']; }): OverlayRef_2; // Warning: (ae-missing-release-tag) "OutputSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -890,6 +892,7 @@ export const withEmbeddableSubscription: { getIconForSavedObject(savedObject: SimpleSavedObject): IconType; getTooltipForSavedObject?(savedObject: SimpleSavedObject): string; showSavedObject?(savedObject: SimpleSavedObject): boolean; + getSavedObjectSubType?(savedObject: SimpleSavedObject): string; includeFields?: string[]; } diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 3ccdfb7e47d70b..872132416352f5 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -104,6 +104,9 @@ export class VisualizeEmbeddableFactory } return visType.stage !== 'experimental'; }, + getSavedObjectSubType: (savedObject) => { + return JSON.parse(savedObject.attributes.visState).type; + }, }; constructor(private readonly deps: VisualizeEmbeddableFactoryDeps) {} diff --git a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts index 2be9358e28d1ac..a8b00b15a1ede1 100644 --- a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts +++ b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts @@ -7,6 +7,7 @@ */ import { SavedObject } from '../../../../core/types/saved_objects'; +import { BaseVisType } from './base_vis_type'; export type VisualizationStage = 'experimental' | 'beta' | 'production'; @@ -23,6 +24,7 @@ export interface VisualizationListItem { getSupportedTriggers?: () => string[]; typeTitle: string; image?: string; + type?: BaseVisType | string; } export interface VisualizationsAppExtension { diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 317f9d1bb363db..2620ae01aa15a7 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -153,7 +153,7 @@ class NewVisModal extends React.Component { + const usageCollection = getUsageCollector(); + + if (usageCollection && visType) { + usageCollection.reportUiCounter(APP_NAME, METRIC_TYPE.CLICK, `${visType}:add`); + } +}; const getBadge = (item: VisualizationListItem) => { if (item.stage === 'beta') { @@ -82,12 +93,16 @@ export const getTableColumns = ( defaultMessage: 'Title', }), sortable: true, - render: (field: string, { editApp, editUrl, title, error }: VisualizationListItem) => + render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) => // In case an error occurs i.e. the vis has wrong type, we render the vis but without the link !error ? ( + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} { + doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name); + }} data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`} > {field} diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index b7c7d63cef98fc..da01f9d44879bb 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; import { Capabilities } from 'src/core/public'; import { TopNavMenuData } from 'src/plugins/navigation/public'; @@ -29,7 +30,7 @@ import { VisualizeAppStateContainer, VisualizeEditorVisInstance, } from '../types'; -import { VisualizeConstants } from '../visualize_constants'; +import { APP_NAME, VisualizeConstants } from '../visualize_constants'; import { getEditBreadcrumbs } from './breadcrumbs'; import { EmbeddableStateTransfer } from '../../../../embeddable/public'; @@ -92,10 +93,22 @@ export const getTopNavConfig = ( dashboard, savedObjectsTagging, presentationUtil, + usageCollection, }: VisualizeServices ) => { const { vis, embeddableHandler } = visInstance; const savedVis = visInstance.savedVis; + + const doTelemetryForSaveEvent = (visType: string) => { + if (usageCollection) { + usageCollection.reportUiCounter( + originatingApp ?? APP_NAME, + METRIC_TYPE.CLICK, + `${visType}:save` + ); + } + }; + /** * Called when the user clicks "Save" button. */ @@ -394,6 +407,8 @@ export const getTopNavConfig = ( return { id: true }; } + doTelemetryForSaveEvent(vis.type.name); + // We're adding the viz to a library so we need to save it and then // add to a dashboard if necessary const response = await doSave(saveOptions); @@ -503,6 +518,8 @@ export const getTopNavConfig = ( } }, run: async () => { + doTelemetryForSaveEvent(vis.type.name); + if (!savedVis?.id) { return createVisReference(); } diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index 4b369e8be86eee..b5ddbdf6d10a3c 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -6,10 +6,11 @@ * Side Public License, v 1. */ -import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; -import { filter, map } from 'rxjs/operators'; import { createHashHistory } from 'history'; +import { BehaviorSubject } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; + import { AppMountParameters, AppUpdater, @@ -18,29 +19,33 @@ import { Plugin, PluginInitializerContext, ScopedHistory, -} from 'kibana/public'; + DEFAULT_APP_CATEGORIES, +} from '../../../core/public'; -import { PresentationUtilPluginStart } from '../../../../src/plugins/presentation_util/public'; import { Storage, createKbnUrlTracker, createKbnUrlStateStorage, withNotifyOnErrors, } from '../../kibana_utils/public'; -import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; -import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; -import { SharePluginStart, SharePluginSetup } from '../../share/public'; -import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; -import { VisualizationsStart } from '../../visualizations/public'; + import { VisualizeConstants } from './application/visualize_constants'; +import { DataPublicPluginStart, DataPublicPluginSetup, esFilters } from '../../data/public'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; -import { VisualizeServices } from './application/types'; -import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; -import { SavedObjectsStart } from '../../saved_objects/public'; -import { EmbeddableStart } from '../../embeddable/public'; -import { DashboardStart } from '../../dashboard/public'; + +import type { PresentationUtilPluginStart } from '../../../../src/plugins/presentation_util/public'; +import type { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; +import type { SharePluginStart, SharePluginSetup } from '../../share/public'; +import type { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/public'; +import type { VisualizationsStart } from '../../visualizations/public'; +import type { VisualizeServices } from './application/types'; +import type { SavedObjectsStart } from '../../saved_objects/public'; +import type { EmbeddableStart } from '../../embeddable/public'; +import type { DashboardStart } from '../../dashboard/public'; import type { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public'; -import { setVisEditorsRegistry, setUISettings } from './services'; +import type { UsageCollectionStart } from '../../usage_collection/public'; + +import { setVisEditorsRegistry, setUISettings, setUsageCollector } from './services'; import { createVisEditorsRegistry, VisEditorsRegistry } from './vis_editors_registry'; export interface VisualizePluginStartDependencies { @@ -54,6 +59,7 @@ export interface VisualizePluginStartDependencies { dashboard: DashboardStart; savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart; presentationUtil: PresentationUtilPluginStart; + usageCollection?: UsageCollectionStart; } export interface VisualizePluginSetupDependencies { @@ -202,6 +208,7 @@ export class VisualizePlugin setHeaderActionMenu: params.setHeaderActionMenu, savedObjectsTagging: pluginsStart.savedObjectsTaggingOss?.getTaggingApi(), presentationUtil: pluginsStart.presentationUtil, + usageCollection: pluginsStart.usageCollection, }; params.element.classList.add('visAppWrapper'); @@ -238,8 +245,12 @@ export class VisualizePlugin } as VisualizePluginSetup; } - public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { + public start(core: CoreStart, { usageCollection }: VisualizePluginStartDependencies) { setVisEditorsRegistry(this.visEditorsRegistry); + + if (usageCollection) { + setUsageCollector(usageCollection); + } } stop() { diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index 192aac3547eb27..97ff7923379b72 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -6,12 +6,18 @@ * Side Public License, v 1. */ -import { IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; -import { VisEditorsRegistry } from './vis_editors_registry'; + +import type { IUiSettingsClient } from '../../../core/public'; +import type { VisEditorsRegistry } from './vis_editors_registry'; +import type { UsageCollectionStart } from '../../usage_collection/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); +export const [getUsageCollector, setUsageCollector] = createGetterSetter( + 'UsageCollection' +); + export const [ getVisEditorsRegistry, setVisEditorsRegistry, diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 2256a05c51e120..95068204d5eb41 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -222,6 +222,7 @@ export function App({ persistedDoc: appState.persistedDoc, onAppLeave, redirectTo, + originatingApp: incomingState?.originatingApp, ...lensAppServices, }, saveProps, diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 46d2009756d2ce..8e59f90c958f91 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -52,7 +52,7 @@ export async function getLensServices( startDependencies: LensPluginStartDependencies, attributeService: () => Promise ): Promise { - const { data, navigation, embeddable, savedObjectsTagging } = startDependencies; + const { data, navigation, embeddable, savedObjectsTagging, usageCollection } = startDependencies; const storage = new Storage(localStorage); const stateTransfer = embeddable?.getStateTransfer(); @@ -63,6 +63,7 @@ export async function getLensServices( storage, navigation, stateTransfer, + usageCollection, savedObjectsTagging, attributeService: await attributeService(), http: coreStart.http, diff --git a/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx b/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx index 27e8031f5fb6b8..a65c8e6732e448 100644 --- a/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx +++ b/x-pack/plugins/lens/public/app_plugin/save_modal_container.tsx @@ -9,6 +9,7 @@ import React, { useEffect, useState } from 'react'; import { ChromeStart, NotificationsStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { partition, uniq } from 'lodash'; +import { METRIC_TYPE } from '@kbn/analytics'; import { SaveModal } from './save_modal'; import { LensAppProps, LensAppServices } from './types'; import type { SaveProps } from './app'; @@ -112,6 +113,7 @@ export function SaveModalContainer({ attributeService, redirectTo, redirectToOrigin, + originatingApp, getIsByValueMode: () => false, onAppLeave: () => {}, }, @@ -178,6 +180,7 @@ export const runSaveLensVisualization = async ( lastKnownDoc?: Document; getIsByValueMode: () => boolean; persistedDoc?: Document; + originatingApp?: string; } & ExtraProps & LensAppServices, saveProps: SaveProps, @@ -190,6 +193,7 @@ export const runSaveLensVisualization = async ( const { chrome, initialInput, + originatingApp, lastKnownDoc, persistedDoc, savedObjectsClient, @@ -197,6 +201,7 @@ export const runSaveLensVisualization = async ( notifications, stateTransfer, attributeService, + usageCollection, savedObjectsTagging, getIsByValueMode, redirectToOrigin, @@ -209,6 +214,9 @@ export const runSaveLensVisualization = async ( persistedDoc && savedObjectsTagging ? savedObjectsTagging.ui.getTagIdsFromReferences(persistedDoc.references) : []; + if (usageCollection) { + usageCollection.reportUiCounter(originatingApp || 'visualize', METRIC_TYPE.CLICK, 'lens:save'); + } let references = lastKnownDoc.references; if (savedObjectsTagging) { diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index d1e2d1cbdfc637..b253e76aa14071 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -18,6 +18,7 @@ import { SavedObjectsStart, } from '../../../../../src/core/public'; import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import { UsageCollectionStart } from '../../../../../src/plugins/usage_collection/public'; import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; import { LensEmbeddableInput } from '../editor_frame_service/embeddable/embeddable'; import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; @@ -97,6 +98,7 @@ export interface LensAppServices { uiSettings: IUiSettingsClient; application: ApplicationStart; notifications: NotificationsStart; + usageCollection?: UsageCollectionStart; stateTransfer: EmbeddableStateTransfer; navigation: NavigationPublicPluginStart; attributeService: LensAttributeService; diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index 6d90691e2173ae..328bea5def557e 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -6,7 +6,7 @@ */ import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { UsageCollectionSetup, UsageCollectionStart } from 'src/plugins/usage_collection/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { DashboardStart } from '../../../../src/plugins/dashboard/public'; @@ -81,6 +81,7 @@ export interface LensPluginStartDependencies { savedObjectsTagging?: SavedObjectTaggingPluginStart; presentationUtil: PresentationUtilPluginStart; indexPatternFieldEditor: IndexPatternFieldEditorStart; + usageCollection?: UsageCollectionStart; } export interface LensPublicStart { diff --git a/x-pack/plugins/lens/public/vis_type_alias.ts b/x-pack/plugins/lens/public/vis_type_alias.ts index b9a526c71180cf..5b48ef8b31923a 100644 --- a/x-pack/plugins/lens/public/vis_type_alias.ts +++ b/x-pack/plugins/lens/public/vis_type_alias.ts @@ -42,6 +42,7 @@ export const getLensAliasConfig = (): VisTypeAlias => ({ icon: 'lensApp', stage: 'production', savedObjectType: type, + type: 'lens', typeTitle: i18n.translate('xpack.lens.visTypeAlias.type', { defaultMessage: 'Lens' }), }; }, From a7b0391702c85f7262026a380763845be73d7fa7 Mon Sep 17 00:00:00 2001 From: Stacey Gammon Date: Wed, 16 Jun 2021 09:28:50 -0400 Subject: [PATCH 19/32] Use export type instead of export to reduce bundle size (#101796) * Use export type instead of export to reduce bundle size * Update legacy docs * update docs again Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...s-data-public.dataplugin._constructor_.md} | 4 +- ...a-plugin-plugins-data-public.dataplugin.md | 26 ++++++++ ...n-plugins-data-public.dataplugin.setup.md} | 4 +- ...n-plugins-data-public.dataplugin.start.md} | 4 +- ...in-plugins-data-public.dataplugin.stop.md} | 4 +- .../kibana-plugin-plugins-data-public.md | 2 +- src/plugins/data/public/index.ts | 54 ++++++++-------- src/plugins/data/public/mocks.ts | 6 +- src/plugins/data/public/public.api.md | 64 +++++++++---------- x-pack/plugins/graph/public/application.ts | 2 +- 10 files changed, 100 insertions(+), 70 deletions(-) rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.plugin._constructor_.md => kibana-plugin-plugins-data-public.dataplugin._constructor_.md} (68%) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.plugin.setup.md => kibana-plugin-plugins-data-public.dataplugin.setup.md} (76%) rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.plugin.start.md => kibana-plugin-plugins-data-public.dataplugin.start.md} (70%) rename docs/development/plugins/data/public/{kibana-plugin-plugins-data-public.plugin.stop.md => kibana-plugin-plugins-data-public.dataplugin.stop.md} (52%) diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin._constructor_.md similarity index 68% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin._constructor_.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin._constructor_.md index 64108a7c7be33a..3eaf2176edf261 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin._constructor_.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Plugin](./kibana-plugin-plugins-data-public.plugin.md) > [(constructor)](./kibana-plugin-plugins-data-public.plugin._constructor_.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) > [(constructor)](./kibana-plugin-plugins-data-public.dataplugin._constructor_.md) -## Plugin.(constructor) +## DataPlugin.(constructor) Constructs a new instance of the `DataPublicPlugin` class diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md new file mode 100644 index 00000000000000..4b2cad7b428821 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) + +## DataPlugin class + +Signature: + +```typescript +export declare class DataPublicPlugin implements Plugin +``` + +## Constructors + +| Constructor | Modifiers | Description | +| --- | --- | --- | +| [(constructor)(initializerContext)](./kibana-plugin-plugins-data-public.dataplugin._constructor_.md) | | Constructs a new instance of the DataPublicPlugin class | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [setup(core, { bfetch, expressions, uiActions, usageCollection, inspector })](./kibana-plugin-plugins-data-public.dataplugin.setup.md) | | | +| [start(core, { uiActions })](./kibana-plugin-plugins-data-public.dataplugin.start.md) | | | +| [stop()](./kibana-plugin-plugins-data-public.dataplugin.stop.md) | | | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.setup.md similarity index 76% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.setup.md index 20181a5208b522..ab1f90c1ac1049 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.setup.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.setup.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Plugin](./kibana-plugin-plugins-data-public.plugin.md) > [setup](./kibana-plugin-plugins-data-public.plugin.setup.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) > [setup](./kibana-plugin-plugins-data-public.dataplugin.setup.md) -## Plugin.setup() method +## DataPlugin.setup() method Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.start.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md similarity index 70% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.start.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md index 56934e8a29edd0..4ea7ec8cd4f65f 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.start.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.start.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Plugin](./kibana-plugin-plugins-data-public.plugin.md) > [start](./kibana-plugin-plugins-data-public.plugin.start.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) > [start](./kibana-plugin-plugins-data-public.dataplugin.start.md) -## Plugin.start() method +## DataPlugin.start() method Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.stop.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.stop.md similarity index 52% rename from docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.stop.md rename to docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.stop.md index 8b8b63db4e03a2..b7067a01b44679 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.plugin.stop.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.dataplugin.stop.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [Plugin](./kibana-plugin-plugins-data-public.plugin.md) > [stop](./kibana-plugin-plugins-data-public.plugin.stop.md) +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) > [stop](./kibana-plugin-plugins-data-public.dataplugin.stop.md) -## Plugin.stop() method +## DataPlugin.stop() method Signature: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 7f5a042e0ab818..7c023e756ebd5e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -11,6 +11,7 @@ | [AggConfig](./kibana-plugin-plugins-data-public.aggconfig.md) | | | [AggConfigs](./kibana-plugin-plugins-data-public.aggconfigs.md) | | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | +| [DataPlugin](./kibana-plugin-plugins-data-public.dataplugin.md) | | | [DuplicateIndexPatternError](./kibana-plugin-plugins-data-public.duplicateindexpatternerror.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | @@ -19,7 +20,6 @@ | [IndexPatternsService](./kibana-plugin-plugins-data-public.indexpatternsservice.md) | | | [OptionedParamType](./kibana-plugin-plugins-data-public.optionedparamtype.md) | | | [PainlessError](./kibana-plugin-plugins-data-public.painlesserror.md) | | -| [Plugin](./kibana-plugin-plugins-data-public.plugin.md) | | | [SearchInterceptor](./kibana-plugin-plugins-data-public.searchinterceptor.md) | | | [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) | \* | | [SearchTimeoutError](./kibana-plugin-plugins-data-public.searchtimeouterror.md) | Request Failure - When an entire multi request fails | diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index ba873952c9841f..078dd3a9b7c5ab 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -276,9 +276,8 @@ export { DuplicateIndexPatternError } from '../common/index_patterns/errors'; * Autocomplete query suggestions: */ -export { +export type { QuerySuggestion, - QuerySuggestionTypes, QuerySuggestionGetFn, QuerySuggestionGetFnArgs, QuerySuggestionBasic, @@ -286,6 +285,7 @@ export { AutocompleteStart, } from './autocomplete'; +export { QuerySuggestionTypes } from './autocomplete'; /* * Search: */ @@ -320,25 +320,23 @@ import { tabifyGetColumns, } from '../common'; -export { +export { AggGroupLabels, AggGroupNames, METRIC_TYPES, BUCKET_TYPES } from '../common'; + +export type { // aggs AggConfigSerialized, - AggGroupLabels, AggGroupName, - AggGroupNames, AggFunctionsMapping, AggParam, AggParamOption, AggParamType, AggConfigOptions, - BUCKET_TYPES, EsaggsExpressionFunctionDefinition, IAggConfig, IAggConfigs, IAggType, IFieldParamType, IMetricAggType, - METRIC_TYPES, OptionedParamType, OptionedValueProp, ParsedInterval, @@ -352,30 +350,23 @@ export { export type { AggConfigs, AggConfig } from '../common'; -export { +export type { // search ES_SEARCH_STRATEGY, EsQuerySortValue, - extractSearchSourceReferences, - getEsPreference, - getSearchParamsFromRequest, IEsSearchRequest, IEsSearchResponse, IKibanaSearchRequest, IKibanaSearchResponse, - injectSearchSourceReferences, ISearchSetup, ISearchStart, ISearchStartSearchSource, ISearchGeneric, ISearchSource, - parseSearchSourceJSON, SearchInterceptor, SearchInterceptorDeps, SearchRequest, SearchSourceFields, - SortDirection, - SearchSessionState, // expression functions and types EsdslExpressionFunctionDefinition, EsRawResponseExpressionTypeDefinition, @@ -386,11 +377,21 @@ export { TimeoutErrorMode, PainlessError, Reason, + WaitUntilNextSessionCompletesOptions, +} from './search'; + +export { + parseSearchSourceJSON, + injectSearchSourceReferences, + extractSearchSourceReferences, + getEsPreference, + getSearchParamsFromRequest, noSearchSessionStorageCapabilityMessage, SEARCH_SESSIONS_MANAGEMENT_ID, waitUntilNextSessionCompletes$, - WaitUntilNextSessionCompletesOptions, isEsError, + SearchSessionState, + SortDirection, } from './search'; export type { @@ -438,33 +439,36 @@ export const search = { * UI components */ -export { - SearchBar, +export type { SearchBarProps, StatefulSearchBarProps, IndexPatternSelectProps, - QueryStringInput, QueryStringInputProps, } from './ui'; +export { QueryStringInput, SearchBar } from './ui'; + /** * Types to be shared externally * @public */ -export { Filter, Query, RefreshInterval, TimeRange } from '../common'; +export type { Filter, Query, RefreshInterval, TimeRange } from '../common'; export { createSavedQueryService, connectToQueryState, syncQueryStateWithUrl, - QueryState, getDefaultQuery, FilterManager, + TimeHistory, +} from './query'; + +export type { + QueryState, SavedQuery, SavedQueryService, SavedQueryTimeFilter, InputTimeRange, - TimeHistory, TimefilterContract, TimeHistoryContract, QueryStateChange, @@ -472,7 +476,7 @@ export { AutoRefreshDoneFn, } from './query'; -export { AggsStart } from './search/aggs'; +export type { AggsStart } from './search/aggs'; export { getTime, @@ -496,7 +500,7 @@ export function plugin(initializerContext: PluginInitializerContext>; -export type Start = jest.Mocked>; +export type Setup = jest.Mocked>; +export type Start = jest.Mocked>; const autocompleteSetupMock: jest.Mocked = { getQuerySuggestions: jest.fn(), diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 67534577d99fcf..13352d183370bd 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -67,7 +67,7 @@ import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; import { PeerCertificate } from 'tls'; -import { Plugin as Plugin_2 } from 'src/core/public'; +import { Plugin } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_3 } from 'kibana/public'; import { PopoverAnchorPosition } from '@elastic/eui'; @@ -621,6 +621,22 @@ export type CustomFilter = Filter & { query: any; }; +// Warning: (ae-forgotten-export) The symbol "DataSetupDependencies" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "DataStartDependencies" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DataPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class DataPlugin implements Plugin { + // Warning: (ae-forgotten-export) The symbol "ConfigSchema" needs to be exported by the entry point index.d.ts + constructor(initializerContext: PluginInitializerContext_2); + // (undocumented) + setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector }: DataSetupDependencies): DataPublicPluginSetup; + // (undocumented) + start(core: CoreStart_2, { uiActions }: DataStartDependencies): DataPublicPluginStart; + // (undocumented) + stop(): void; + } + // Warning: (ae-missing-release-tag) "DataPublicPluginSetup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -2004,27 +2020,11 @@ export type PhrasesFilter = Filter & { meta: PhrasesFilterMeta; }; -// Warning: (ae-forgotten-export) The symbol "DataSetupDependencies" needs to be exported by the entry point index.d.ts -// Warning: (ae-forgotten-export) The symbol "DataStartDependencies" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "DataPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export class Plugin implements Plugin_2 { - // Warning: (ae-forgotten-export) The symbol "ConfigSchema" needs to be exported by the entry point index.d.ts - constructor(initializerContext: PluginInitializerContext_2); - // (undocumented) - setup(core: CoreSetup, { bfetch, expressions, uiActions, usageCollection, inspector }: DataSetupDependencies): DataPublicPluginSetup; - // (undocumented) - start(core: CoreStart_2, { uiActions }: DataStartDependencies): DataPublicPluginStart; - // (undocumented) - stop(): void; - } - // Warning: (ae-forgotten-export) The symbol "PluginInitializerContext" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "plugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function plugin(initializerContext: PluginInitializerContext): Plugin; +export function plugin(initializerContext: PluginInitializerContext): DataPlugin; // Warning: (ae-missing-release-tag) "Query" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2772,20 +2772,20 @@ export interface WaitUntilNextSessionCompletesOptions { // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:238:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:427:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:430:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:431:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:434:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:408:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:408:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:408:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "IpAddress" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:427:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:428:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:431:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:432:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:435:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:56:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts diff --git a/x-pack/plugins/graph/public/application.ts b/x-pack/plugins/graph/public/application.ts index 0b80e18f3fdb27..26e86bbc3d886b 100644 --- a/x-pack/plugins/graph/public/application.ts +++ b/x-pack/plugins/graph/public/application.ts @@ -31,7 +31,7 @@ import { } from 'kibana/public'; // @ts-ignore import { initGraphApp } from './app'; -import { Plugin as DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; +import { DataPlugin, IndexPatternsContract } from '../../../../src/plugins/data/public'; import { LicensingPluginStart } from '../../licensing/public'; import { checkLicense } from '../common/check_license'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; From 3236f3fafa83447c99271a4260dd996b2424d128 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 16 Jun 2021 06:30:31 -0700 Subject: [PATCH 20/32] skip flaky suite (#102283) --- x-pack/test/api_integration/apis/ml/modules/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/test/api_integration/apis/ml/modules/index.ts b/x-pack/test/api_integration/apis/ml/modules/index.ts index dae0044c47ccac..f6c36c61b998ca 100644 --- a/x-pack/test/api_integration/apis/ml/modules/index.ts +++ b/x-pack/test/api_integration/apis/ml/modules/index.ts @@ -13,6 +13,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { const fleetPackages = ['apache-0.5.0', 'nginx-0.5.0']; // Failing: See https://github.com/elastic/kibana/issues/102282 + // Failing: See https://github.com/elastic/kibana/issues/102283 describe.skip('modules', function () { before(async () => { for (const fleetPackage of fleetPackages) { From 6b99e662cfced09f9cf28b056a1540b658457ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Wed, 16 Jun 2021 15:32:26 +0200 Subject: [PATCH 21/32] Updates app_id to use integrations one instead of fleet for back button link (#102312) --- .../components/fleet_event_filters_card.tsx | 10 ++++++---- .../components/fleet_trusted_apps_card.tsx | 12 +++++++----- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx index be3cba5eb43181..5588cdbe81e3ed 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_event_filters_card.tsx @@ -20,7 +20,7 @@ import { GetExceptionSummaryResponse, ListPageRouteState, } from '../../../../../../../../common/endpoint/types'; -import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; +import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../../fleet/common'; import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; @@ -68,19 +68,21 @@ export const FleetEventFiltersCard = memo( }, [eventFiltersApi, toasts]); const eventFiltersRouteState = useMemo(() => { - const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; + const fleetPackageCustomUrlPath = `#${ + pagePathGetters.integration_details_custom({ pkgkey })[1] + }`; return { backButtonLabel: i18n.translate( 'xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel', { defaultMessage: 'Back to Endpoint Integration' } ), onBackButtonNavigateTo: [ - FLEET_PLUGIN_ID, + INTEGRATIONS_PLUGIN_ID, { path: fleetPackageCustomUrlPath, }, ], - backButtonUrl: getUrlForApp(FLEET_PLUGIN_ID, { + backButtonUrl: getUrlForApp(INTEGRATIONS_PLUGIN_ID, { path: fleetPackageCustomUrlPath, }), }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index ed3ba10c1e62bb..f1c9cb13a27dc5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -20,7 +20,7 @@ import { ListPageRouteState, GetExceptionSummaryResponse, } from '../../../../../../../../common/endpoint/types'; -import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; +import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../../fleet/common'; import { MANAGEMENT_APP_ID } from '../../../../../../common/constants'; import { useToasts } from '../../../../../../../common/lib/kibana'; import { LinkWithIcon } from './link_with_icon'; @@ -68,24 +68,26 @@ export const FleetTrustedAppsCard = memo(( const trustedAppsListUrlPath = getTrustedAppsListPath(); const trustedAppRouteState = useMemo(() => { - const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; + const fleetPackageCustomUrlPath = `#${ + pagePathGetters.integration_details_custom({ pkgkey })[1] + }`; + return { backButtonLabel: i18n.translate( 'xpack.securitySolution.endpoint.fleetCustomExtension.backButtonLabel', { defaultMessage: 'Back to Endpoint Integration' } ), onBackButtonNavigateTo: [ - FLEET_PLUGIN_ID, + INTEGRATIONS_PLUGIN_ID, { path: fleetPackageCustomUrlPath, }, ], - backButtonUrl: getUrlForApp(FLEET_PLUGIN_ID, { + backButtonUrl: getUrlForApp(INTEGRATIONS_PLUGIN_ID, { path: fleetPackageCustomUrlPath, }), }; }, [getUrlForApp, pkgkey]); - return ( From 1b7a5a99cbf0f3ef0c4f656cf44e105fd1d4473d Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 16 Jun 2021 09:59:51 -0400 Subject: [PATCH 22/32] [Security Solution][Endpoint] Show Endpoint Host Isolation status on endpoint list (#101961) * Add endpoint isolation status for when multiple actions of different types are pending * Refactored List to break out Agent status code to separate component * Generator improvements for how actions are generated for Endpoints * Add HTTP mock for fleet EPM packages (to silence console errors) * new `.updateCommonInfo()` method to generator (to regenerate stateful data) --- .../data_generators/base_data_generator.ts | 11 ++- .../data_generators/fleet_action_generator.ts | 18 +++- .../common/endpoint/generate_data.ts | 10 ++- .../common/endpoint/index_data.ts | 85 +++++++++++++++--- .../endpoint_host_isolation_status.test.tsx | 56 ++++++++++++ .../endpoint_host_isolation_status.tsx | 66 +++++++++++--- .../endpoint_pending_actions.test.ts | 37 ++++++++ .../lib/endpoint_pending_actions/mocks.ts | 63 +++++++++++++ .../endpoint/http_handler_mock_factory.ts | 30 ++++--- .../management/pages/endpoint_hosts/mocks.ts | 34 ++++++- .../pages/endpoint_hosts/store/action.ts | 7 +- .../pages/endpoint_hosts/store/builders.ts | 3 +- .../pages/endpoint_hosts/store/index.test.ts | 4 + .../endpoint_hosts/store/middleware.test.ts | 76 +++++++++++----- .../pages/endpoint_hosts/store/middleware.ts | 80 +++++++++++++---- .../pages/endpoint_hosts/store/reducer.ts | 17 +++- .../pages/endpoint_hosts/store/selectors.ts | 39 ++++++++ .../management/pages/endpoint_hosts/types.ts | 11 ++- .../components/endpoint_agent_status.test.tsx | 90 +++++++++++++++++++ .../view/components/endpoint_agent_status.tsx | 59 ++++++++++++ .../view/details/endpoint_details.tsx | 18 +--- .../pages/endpoint_hosts/view/index.tsx | 22 ++--- 22 files changed, 718 insertions(+), 118 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/endpoint_pending_actions.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/mocks.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx create mode 100644 x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts index 35c976fbdfb1d1..1f3d4307197f8a 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/base_data_generator.ts @@ -48,9 +48,14 @@ export class BaseDataGenerator { return new Date(now - this.randomChoice(DAY_OFFSETS)).toISOString(); } - /** Generate either `true` or `false` */ - protected randomBoolean(): boolean { - return this.random() < 0.5; + /** + * Generate either `true` or `false`. By default, the boolean is calculated by determining if a + * float is less than `0.5`, but that can be adjusted via the input argument + * + * @param isLessThan + */ + protected randomBoolean(isLessThan: number = 0.5): boolean { + return this.random() < isLessThan; } /** generate random OS family value */ diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts index af799de782f48c..6cc5ab7f084476 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/fleet_action_generator.ts @@ -13,7 +13,7 @@ import { EndpointAction, EndpointActionResponse, ISOLATION_ACTIONS } from '../ty const ISOLATION_COMMANDS: ISOLATION_ACTIONS[] = ['isolate', 'unisolate']; export class FleetActionGenerator extends BaseDataGenerator { - /** Generate an Action */ + /** Generate a random endpoint Action (isolate or unisolate) */ generate(overrides: DeepPartial = {}): EndpointAction { const timeStamp = new Date(this.randomPastDate()); @@ -35,6 +35,14 @@ export class FleetActionGenerator extends BaseDataGenerator { ); } + generateIsolateAction(overrides: DeepPartial = {}): EndpointAction { + return merge(this.generate({ data: { command: 'isolate' } }), overrides); + } + + generateUnIsolateAction(overrides: DeepPartial = {}): EndpointAction { + return merge(this.generate({ data: { command: 'unisolate' } }), overrides); + } + /** Generates an action response */ generateResponse(overrides: DeepPartial = {}): EndpointActionResponse { const timeStamp = new Date(); @@ -56,6 +64,14 @@ export class FleetActionGenerator extends BaseDataGenerator { ); } + randomFloat(): number { + return this.random(); + } + + randomN(max: number): number { + return super.randomN(max); + } + protected randomIsolateCommand() { return this.randomChoice(ISOLATION_COMMANDS); } diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index 7e03d9b61fc10e..b08d5649540db7 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -422,6 +422,14 @@ export class EndpointDocGenerator extends BaseDataGenerator { this.commonInfo.Endpoint.policy.applied.status = this.randomChoice(POLICY_RESPONSE_STATUSES); } + /** + * Update the common host metadata - essentially creating an entire new endpoint metadata record + * when the `.generateHostMetadata()` is subsequently called + */ + public updateCommonInfo() { + this.commonInfo = this.createHostData(); + } + /** * Parses an index and returns the data stream fields extracted from the index. * @@ -439,7 +447,7 @@ export class EndpointDocGenerator extends BaseDataGenerator { private createHostData(): HostInfo { const hostName = this.randomHostname(); - const isIsolated = this.randomBoolean(); + const isIsolated = this.randomBoolean(0.3); return { agent: { diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index 4996d90288ca9a..959db0d964aaec 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -13,6 +13,8 @@ import { AxiosResponse } from 'axios'; import { EndpointDocGenerator, Event, TreeOptions } from './generate_data'; import { firstNonNullValue } from './models/ecs_safety_helpers'; import { + AGENT_ACTIONS_INDEX, + AGENT_ACTIONS_RESULTS_INDEX, AGENT_POLICY_API_ROUTES, CreateAgentPolicyRequest, CreateAgentPolicyResponse, @@ -25,7 +27,7 @@ import { PACKAGE_POLICY_API_ROUTES, } from '../../../fleet/common'; import { policyFactory as policyConfigFactory } from './models/policy_config'; -import { HostMetadata } from './types'; +import { EndpointAction, HostMetadata } from './types'; import { KbnClientWithApiKeySupport } from '../../scripts/endpoint/kbn_client_with_api_key_support'; import { FleetAgentGenerator } from './data_generators/fleet_agent_generator'; import { FleetActionGenerator } from './data_generators/fleet_action_generator'; @@ -409,36 +411,97 @@ const indexFleetActionsForHost = async ( ): Promise => { const ES_INDEX_OPTIONS = { headers: { 'X-elastic-product-origin': 'fleet' } }; const agentId = endpointHost.elastic.agent.id; + const total = fleetActionGenerator.randomN(5); - for (let i = 0; i < 5; i++) { + for (let i = 0; i < total; i++) { // create an action - const isolateAction = fleetActionGenerator.generate({ + const action = fleetActionGenerator.generate({ data: { comment: 'data generator: this host is bad' }, }); - isolateAction.agents = [agentId]; + action.agents = [agentId]; await esClient.index( { - index: '.fleet-actions', - body: isolateAction, + index: AGENT_ACTIONS_INDEX, + body: action, }, ES_INDEX_OPTIONS ); // Create an action response for the above - const unIsolateAction = fleetActionGenerator.generateResponse({ - action_id: isolateAction.action_id, + const actionResponse = fleetActionGenerator.generateResponse({ + action_id: action.action_id, agent_id: agentId, - action_data: isolateAction.data, + action_data: action.data, }); await esClient.index( { - index: '.fleet-actions-results', - body: unIsolateAction, + index: AGENT_ACTIONS_RESULTS_INDEX, + body: actionResponse, }, ES_INDEX_OPTIONS ); } + + // Add edge cases (maybe) + if (fleetActionGenerator.randomFloat() < 0.3) { + const randomFloat = fleetActionGenerator.randomFloat(); + + // 60% of the time just add either an Isoalte -OR- an UnIsolate action + if (randomFloat < 0.6) { + let action: EndpointAction; + + if (randomFloat < 0.3) { + // add a pending isolation + action = fleetActionGenerator.generateIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + } else { + // add a pending UN-isolation + action = fleetActionGenerator.generateUnIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + } + + action.agents = [agentId]; + + await esClient.index( + { + index: AGENT_ACTIONS_INDEX, + body: action, + }, + ES_INDEX_OPTIONS + ); + } else { + // Else (40% of the time) add a pending isolate AND pending un-isolate + const action1 = fleetActionGenerator.generateIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + const action2 = fleetActionGenerator.generateUnIsolateAction({ + '@timestamp': new Date().toISOString(), + }); + + action1.agents = [agentId]; + action2.agents = [agentId]; + + await Promise.all([ + esClient.index( + { + index: AGENT_ACTIONS_INDEX, + body: action1, + }, + ES_INDEX_OPTIONS + ), + esClient.index( + { + index: AGENT_ACTIONS_INDEX, + body: action2, + }, + ES_INDEX_OPTIONS + ), + ]); + } + } }; diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx new file mode 100644 index 00000000000000..44405748b6373b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.test.tsx @@ -0,0 +1,56 @@ +/* + * 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 { + EndpointHostIsolationStatus, + EndpointHostIsolationStatusProps, +} from './endpoint_host_isolation_status'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../../mock/endpoint'; + +describe('when using the EndpointHostIsolationStatus component', () => { + let render: ( + renderProps?: Partial + ) => ReturnType; + + beforeEach(() => { + const appContext = createAppRootMockRenderer(); + render = (renderProps = {}) => + appContext.render( + + ); + }); + + it('should render `null` if not isolated and nothing is pending', () => { + const renderResult = render(); + expect(renderResult.container.textContent).toBe(''); + }); + + it('should show `Isolated` when no pending actions and isolated', () => { + const { getByTestId } = render({ isIsolated: true }); + expect(getByTestId('test').textContent).toBe('Isolated'); + }); + + it.each([ + ['Isolating pending', { pendingIsolate: 2 }], + ['Unisolating pending', { pendingUnIsolate: 2 }], + ['4 actions pending', { isIsolated: true, pendingUnIsolate: 2, pendingIsolate: 2 }], + ])('should show %s}', (expectedLabel, componentProps) => { + const { getByTestId } = render(componentProps); + expect(getByTestId('test').textContent).toBe(expectedLabel); + // Validate that the text color is set to `subdued` + expect(getByTestId('test-pending').classList.contains('euiTextColor--subdued')).toBe(true); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx index 5cde22de697386..0fe3a8e4337cb2 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/host_isolation/endpoint_host_isolation_status.tsx @@ -6,8 +6,9 @@ */ import React, { memo, useMemo } from 'react'; -import { EuiBadge, EuiTextColor } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiTextColor, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useTestIdGenerator } from '../../../../management/components/hooks/use_test_id_generator'; export interface EndpointHostIsolationStatusProps { isIsolated: boolean; @@ -15,6 +16,7 @@ export interface EndpointHostIsolationStatusProps { pendingIsolate?: number; /** the count of pending unisoalte actions */ pendingUnIsolate?: number; + 'data-test-subj'?: string; } /** @@ -23,7 +25,9 @@ export interface EndpointHostIsolationStatusProps { * (`null` is returned) */ export const EndpointHostIsolationStatus = memo( - ({ isIsolated, pendingIsolate = 0, pendingUnIsolate = 0 }) => { + ({ isIsolated, pendingIsolate = 0, pendingUnIsolate = 0, 'data-test-subj': dataTestSubj }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + return useMemo(() => { // If nothing is pending and host is not currently isolated, then render nothing if (!isIsolated && !pendingIsolate && !pendingUnIsolate) { @@ -33,7 +37,7 @@ export const EndpointHostIsolationStatus = memo + + +

+ +
+ + + + + {pendingIsolate} + + + + + + {pendingUnIsolate} + +

+ } + > + + + + + + ); + } // Show 'pending [un]isolate' depending on what's pending return ( - - + + {pendingIsolate ? ( ); - }, [isIsolated, pendingIsolate, pendingUnIsolate]); + }, [dataTestSubj, getTestId, isIsolated, pendingIsolate, pendingUnIsolate]); } ); diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/endpoint_pending_actions.test.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/endpoint_pending_actions.test.ts new file mode 100644 index 00000000000000..a90f9a3508cd81 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/endpoint_pending_actions.test.ts @@ -0,0 +1,37 @@ +/* + * 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 { KibanaServices } from '../kibana'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { fetchPendingActionsByAgentId } from './endpoint_pending_actions'; +import { pendingActionsHttpMock, pendingActionsResponseMock } from './mocks'; +import { ACTION_STATUS_ROUTE } from '../../../../common/endpoint/constants'; + +jest.mock('../kibana'); + +describe('when using endpoint pending actions api service', () => { + let coreHttp: ReturnType['http']; + + beforeEach(() => { + const coreStartMock = coreMock.createStart(); + coreHttp = coreStartMock.http; + pendingActionsHttpMock(coreHttp); + (KibanaServices.get as jest.Mock).mockReturnValue(coreStartMock); + }); + + it('should call the endpont pending action status API', async () => { + const agentIdList = ['111-111', '222-222']; + const response = await fetchPendingActionsByAgentId(agentIdList); + + expect(response).toEqual(pendingActionsResponseMock()); + expect(coreHttp.get).toHaveBeenCalledWith(ACTION_STATUS_ROUTE, { + query: { + agent_ids: agentIdList, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/mocks.ts b/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/mocks.ts new file mode 100644 index 00000000000000..4c3822b07d88c2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/lib/endpoint_pending_actions/mocks.ts @@ -0,0 +1,63 @@ +/* + * 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 { + PendingActionsRequestQuery, + PendingActionsResponse, +} from '../../../../common/endpoint/types'; +import { + httpHandlerMockFactory, + ResponseProvidersInterface, +} from '../../mock/endpoint/http_handler_mock_factory'; +import { ACTION_STATUS_ROUTE } from '../../../../common/endpoint/constants'; + +export const pendingActionsResponseMock = (): PendingActionsResponse => ({ + data: [ + { + agent_id: '111-111', + pending_actions: {}, + }, + { + agent_id: '222-222', + pending_actions: { + isolate: 1, + }, + }, + ], +}); + +export type PendingActionsHttpMockInterface = ResponseProvidersInterface<{ + pendingActions: () => PendingActionsResponse; +}>; + +export const pendingActionsHttpMock = httpHandlerMockFactory([ + { + id: 'pendingActions', + method: 'get', + path: ACTION_STATUS_ROUTE, + /** Will build a response based on the number of agent ids received. */ + handler: (options) => { + const agentIds = (options.query as PendingActionsRequestQuery).agent_ids as string[]; + + if (agentIds.length) { + return { + data: agentIds.map((id, index) => ({ + agent_id: id, + pending_actions: + index % 2 // index's of the array that are not divisible by 2 will will have `isolate: 1` + ? { + isolate: 1, + } + : {}, + })), + }; + } + + return pendingActionsResponseMock(); + }, + }, +]); diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts b/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts index 2df16fc1e21b0a..dc93ea8168a3f6 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/http_handler_mock_factory.ts @@ -7,12 +7,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { - HttpFetchOptions, - HttpFetchOptionsWithPath, - HttpHandler, - HttpStart, -} from 'kibana/public'; +import type { HttpFetchOptions, HttpFetchOptionsWithPath, HttpStart } from 'kibana/public'; import { merge } from 'lodash'; import { act } from '@testing-library/react'; @@ -102,7 +97,7 @@ interface RouteMock) => any; + handler: (options: HttpFetchOptionsWithPath) => any; /** * A function that returns a promise. The API response will be delayed until this promise is * resolved. This can be helpful when wanting to test an intermediate UI state while the API @@ -203,14 +198,25 @@ export const httpHandlerMockFactory = pathMatchesPattern(handler.path, path)); if (routeMock) { - markApiCallAsHandled(responseProvider[routeMock.id].mockDelay); - - await responseProvider[routeMock.id].mockDelay(); - // Use the handler defined for the HTTP Mocked interface (not the one passed on input to // the factory) for retrieving the response value because that one could have had its // response value manipulated by the individual test case. - return responseProvider[routeMock.id](...args); + + markApiCallAsHandled(responseProvider[routeMock.id].mockDelay); + await responseProvider[routeMock.id].mockDelay(); + + const fetchOptions: HttpFetchOptionsWithPath = isHttpFetchOptionsWithPath(args[0]) + ? args[0] + : { + // Ignore below is needed because the http service methods are defined via an overloaded interface. + // If the first argument is NOT fetch with options, then we know that its a string and `args` has + // a potential for being of `.length` 2. + // @ts-ignore + ...(args[1] || {}), + path: args[0], + }; + + return responseProvider[routeMock.id](fetchOptions); } else if (priorMockedFunction) { return priorMockedFunction(...args); } diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts index 3a3ad47f9f5754..de05fa949b487e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/mocks.ts @@ -23,7 +23,16 @@ import { HOST_METADATA_GET_ROUTE, HOST_METADATA_LIST_ROUTE, } from '../../../../common/endpoint/constants'; -import { AGENT_POLICY_API_ROUTES, GetAgentPoliciesResponse } from '../../../../../fleet/common'; +import { + AGENT_POLICY_API_ROUTES, + EPM_API_ROUTES, + GetAgentPoliciesResponse, + GetPackagesResponse, +} from '../../../../../fleet/common'; +import { + PendingActionsHttpMockInterface, + pendingActionsHttpMock, +} from '../../../common/lib/endpoint_pending_actions/mocks'; type EndpointMetadataHttpMocksInterface = ResponseProvidersInterface<{ metadataList: () => HostResultList; @@ -40,11 +49,15 @@ export const endpointMetadataHttpMocks = httpHandlerMockFactory { - return { + const endpoint = { metadata: generator.generateHostMetadata(), host_status: HostStatus.UNHEALTHY, query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, }; + + generator.updateCommonInfo(); + + return endpoint; }), total: 10, request_page_size: 10, @@ -88,6 +101,7 @@ export const endpointPolicyResponseHttpMock = httpHandlerMockFactory GetAgentPoliciesResponse; + packageList: () => GetPackagesResponse; }>; export const fleetApisHttpMock = httpHandlerMockFactory([ { @@ -113,11 +127,24 @@ export const fleetApisHttpMock = httpHandlerMockFactory & { + payload: EndpointState['endpointPendingActions']; +}; + export type EndpointAction = | ServerReturnedEndpointList | ServerFailedToReturnEndpointList @@ -186,4 +190,5 @@ export type EndpointAction = | ServerFailedToReturnAgenstWithEndpointsTotal | ServerFailedToReturnEndpointsTotal | EndpointIsolationRequest - | EndpointIsolationRequestStateChange; + | EndpointIsolationRequestStateChange + | EndpointPendingActionsStateChanged; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts index 273b4279851fd3..d43f361a0e6bb8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/builders.ts @@ -7,7 +7,7 @@ import { Immutable } from '../../../../../common/endpoint/types'; import { DEFAULT_POLL_INTERVAL } from '../../../common/constants'; -import { createUninitialisedResourceState } from '../../../state'; +import { createLoadedResourceState, createUninitialisedResourceState } from '../../../state'; import { EndpointState } from '../types'; export const initialEndpointPageState = (): Immutable => { @@ -53,5 +53,6 @@ export const initialEndpointPageState = (): Immutable => { policyVersionInfo: undefined, hostStatus: undefined, isolationRequestState: createUninitialisedResourceState(), + endpointPendingActions: createLoadedResourceState(new Map()), }; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index 455c6538bcdf26..7f7c5f84f8bffd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -77,6 +77,10 @@ describe('EndpointList store concerns', () => { isolationRequestState: { type: 'UninitialisedResourceState', }, + endpointPendingActions: { + data: new Map(), + type: 'LoadedResourceState', + }, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index 130f8a56fd0267..52da30fabf95a1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -43,6 +43,7 @@ import { hostIsolationResponseMock, } from '../../../../common/lib/endpoint_isolation/mocks'; import { FleetActionGenerator } from '../../../../../common/endpoint/data_generators/fleet_action_generator'; +import { endpointPageHttpMock } from '../mocks'; jest.mock('../../policy/store/services/ingest', () => ({ sendGetAgentConfigList: () => Promise.resolve({ items: [] }), @@ -55,6 +56,7 @@ jest.mock('../../../../common/lib/kibana'); type EndpointListStore = Store, Immutable>; describe('endpoint list middleware', () => { + const getKibanaServicesMock = KibanaServices.get as jest.Mock; let fakeCoreStart: jest.Mocked; let depsStart: DepsStartMock; let fakeHttpServices: jest.Mocked; @@ -69,6 +71,17 @@ describe('endpoint list middleware', () => { return mockEndpointResultList({ request_page_size: 1, request_page_index: 1, total: 10 }); }; + const dispatchUserChangedUrlToEndpointList = (locationOverrides: Partial = {}) => { + dispatch({ + type: 'userChangedUrl', + payload: { + ...history.location, + pathname: getEndpointListPath({ name: 'endpointList' }), + ...locationOverrides, + }, + }); + }; + beforeEach(() => { fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); depsStart = depsStartMock(); @@ -81,6 +94,7 @@ describe('endpoint list middleware', () => { getState = store.getState; dispatch = store.dispatch; history = createBrowserHistory(); + getKibanaServicesMock.mockReturnValue(fakeCoreStart); }); it('handles `userChangedUrl`', async () => { @@ -88,13 +102,7 @@ describe('endpoint list middleware', () => { fakeHttpServices.post.mockResolvedValue(apiResponse); expect(fakeHttpServices.post).not.toHaveBeenCalled(); - dispatch({ - type: 'userChangedUrl', - payload: { - ...history.location, - pathname: getEndpointListPath({ name: 'endpointList' }), - }, - }); + dispatchUserChangedUrlToEndpointList(); await waitForAction('serverReturnedEndpointList'); expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/metadata', { body: JSON.stringify({ @@ -111,13 +119,7 @@ describe('endpoint list middleware', () => { expect(fakeHttpServices.post).not.toHaveBeenCalled(); // First change the URL - dispatch({ - type: 'userChangedUrl', - payload: { - ...history.location, - pathname: getEndpointListPath({ name: 'endpointList' }), - }, - }); + dispatchUserChangedUrlToEndpointList(); await waitForAction('serverReturnedEndpointList'); // Then request the Endpoint List @@ -135,7 +137,6 @@ describe('endpoint list middleware', () => { }); describe('handling of IsolateEndpointHost action', () => { - const getKibanaServicesMock = KibanaServices.get as jest.Mock; const dispatchIsolateEndpointHost = (action: ISOLATION_ACTIONS = 'isolate') => { dispatch({ type: 'endpointIsolationRequest', @@ -149,7 +150,6 @@ describe('endpoint list middleware', () => { beforeEach(() => { isolateApiResponseHandlers = hostIsolationHttpMocks(fakeHttpServices); - getKibanaServicesMock.mockReturnValue(fakeCoreStart); }); it('should set Isolation state to loading', async () => { @@ -224,14 +224,7 @@ describe('endpoint list middleware', () => { selected_endpoint: endpointList.hosts[0].metadata.agent.id, }); const dispatchUserChangedUrl = () => { - dispatch({ - type: 'userChangedUrl', - payload: { - ...history.location, - pathname: '/endpoints', - search: `?${search.split('?').pop()}`, - }, - }); + dispatchUserChangedUrlToEndpointList({ search: `?${search.split('?').pop()}` }); }; const fleetActionGenerator = new FleetActionGenerator(Math.random().toString()); @@ -300,4 +293,39 @@ describe('endpoint list middleware', () => { expect(activityLogData).toEqual(getMockEndpointActivityLog()); }); }); + + describe('handle Endpoint Pending Actions state actions', () => { + let mockedApis: ReturnType; + + beforeEach(() => { + mockedApis = endpointPageHttpMock(fakeHttpServices); + }); + + it('should include all agents ids from the list when calling API', async () => { + const loadingPendingActions = waitForAction('endpointPendingActionsStateChanged', { + validate: (action) => isLoadedResourceState(action.payload), + }); + + dispatchUserChangedUrlToEndpointList(); + await loadingPendingActions; + + expect(mockedApis.responseProvider.pendingActions).toHaveBeenCalledWith({ + path: expect.any(String), + query: { + agent_ids: [ + '6db499e5-4927-4350-abb8-d8318e7d0eec', + 'c082dda9-1847-4997-8eda-f1192d95bec3', + '8aa1cd61-cc25-4783-afb5-0eefc4919c07', + '47fe24c1-7370-419a-9732-3ff38bf41272', + '0d2b2fa7-a9cd-49fc-ad5f-0252c642290e', + 'f480092d-0445-4bf3-9c96-8a3d5cb97824', + '3850e676-0940-4c4b-aaca-571bd1bc66d9', + '46efcc7a-086a-47a3-8f09-c4ecd6d2d917', + 'afa55826-b81b-4440-a2ac-0644d77a3fc6', + '25b49e50-cb5c-43df-824f-67b8cf697d9d', + ], + }, + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index aa0afe5ec980a3..4f96223e8b7897 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -34,8 +34,9 @@ import { getActivityLogData, getActivityLogDataPaging, getLastLoadedActivityLogData, + detailsData, } from './selectors'; -import { EndpointState, PolicyIds } from '../types'; +import { AgentIdsPendingActions, EndpointState, PolicyIds } from '../types'; import { sendGetEndpointSpecificPackagePolicies, sendGetEndpointSecurityPackage, @@ -59,9 +60,13 @@ import { isolateHost, unIsolateHost } from '../../../../common/lib/endpoint_isol import { AppAction } from '../../../../common/store/actions'; import { resolvePathVariables } from '../../../../common/utils/resolve_path_variables'; import { ServerReturnedEndpointPackageInfo } from './action'; +import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions'; type EndpointPageStore = ImmutableMiddlewareAPI; +// eslint-disable-next-line no-console +const logError = console.error; + export const endpointMiddlewareFactory: ImmutableMiddlewareFactory = ( coreStart, depsStart @@ -110,6 +115,8 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory => { }) ).total; } catch (error) { - // eslint-disable-next-line no-console - console.error(`error while trying to check for total endpoints`); - // eslint-disable-next-line no-console - console.error(error); + logError(`error while trying to check for total endpoints`); + logError(error); } return 0; }; @@ -524,10 +528,8 @@ const doEndpointsExist = async (http: HttpStart): Promise => { try { return (await endpointsTotal(http)) > 0; } catch (error) { - // eslint-disable-next-line no-console - console.error(`error while trying to check if endpoints exist`); - // eslint-disable-next-line no-console - console.error(error); + logError(`error while trying to check if endpoints exist`); + logError(error); } return false; }; @@ -586,7 +588,51 @@ async function getEndpointPackageInfo( }); } catch (error) { // Ignore Errors, since this should not hinder the user's ability to use the UI - // eslint-disable-next-line no-console - console.error(error); + logError(error); } } + +/** + * retrieves the Endpoint pending actions for all of the existing endpoints being displayed on the list + * or the details tab. + * + * @param store + */ +const loadEndpointsPendingActions = async ({ + getState, + dispatch, +}: EndpointPageStore): Promise => { + const state = getState(); + const detailsEndpoint = detailsData(state); + const listEndpoints = listData(state); + const agentsIds = new Set(); + + // get all agent ids for the endpoints in the list + if (detailsEndpoint) { + agentsIds.add(detailsEndpoint.elastic.agent.id); + } + + for (const endpointInfo of listEndpoints) { + agentsIds.add(endpointInfo.metadata.elastic.agent.id); + } + + if (agentsIds.size === 0) { + return; + } + + try { + const { data: pendingActions } = await fetchPendingActionsByAgentId(Array.from(agentsIds)); + const agentIdToPendingActions: AgentIdsPendingActions = new Map(); + + for (const pendingAction of pendingActions) { + agentIdToPendingActions.set(pendingAction.agent_id, pendingAction.pending_actions); + } + + dispatch({ + type: 'endpointPendingActionsStateChanged', + payload: createLoadedResourceState(agentIdToPendingActions), + }); + } catch (error) { + logError(error); + } +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index b580664512eb66..9460c27dfe705d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { EndpointDetailsActivityLogChanged } from './action'; +import { EndpointDetailsActivityLogChanged, EndpointPendingActionsStateChanged } from './action'; import { isOnEndpointPage, hasSelectedEndpoint, @@ -41,6 +41,19 @@ const handleEndpointDetailsActivityLogChanged: CaseReducer = ( + state, + action +) => { + if (isOnEndpointPage(state)) { + return { + ...state, + endpointPendingActions: action.payload, + }; + } + return state; +}; + /* eslint-disable-next-line complexity */ export const endpointListReducer: StateReducer = (state = initialEndpointPageState(), action) => { if (action.type === 'serverReturnedEndpointList') { @@ -141,6 +154,8 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta }; } else if (action.type === 'endpointDetailsActivityLogChanged') { return handleEndpointDetailsActivityLogChanged(state, action); + } else if (action.type === 'endpointPendingActionsStateChanged') { + return handleEndpointPendingActionsStateChanged(state, action); } else if (action.type === 'serverReturnedPoliciesForOnboarding') { return { ...state, diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index 2b567d1ad53b58..d9be85377c81d7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -18,6 +18,7 @@ import { MetadataQueryStrategyVersions, HostStatus, ActivityLog, + HostMetadata, } from '../../../../../common/endpoint/types'; import { EndpointState, EndpointIndexUIQueryParams } from '../types'; import { extractListPaginationParams } from '../../../common/routing'; @@ -36,6 +37,7 @@ import { import { ServerApiError } from '../../../../common/types'; import { isEndpointHostIsolated } from '../../../../common/utils/validators'; +import { EndpointHostIsolationStatusProps } from '../../../../common/components/endpoint/host_isolation'; export const listData = (state: Immutable) => state.hosts; @@ -412,3 +414,40 @@ export const getActivityLogError: ( export const getIsEndpointHostIsolated = createSelector(detailsData, (details) => { return (details && isEndpointHostIsolated(details)) || false; }); + +export const getEndpointPendingActionsState = ( + state: Immutable +): Immutable => { + return state.endpointPendingActions; +}; + +/** + * Returns a function (callback) that can be used to retrieve the props for the `EndpointHostIsolationStatus` + * component for a given Endpoint + */ +export const getEndpointHostIsolationStatusPropsCallback: ( + state: Immutable +) => (endpoint: HostMetadata) => EndpointHostIsolationStatusProps = createSelector( + getEndpointPendingActionsState, + (pendingActionsState) => { + return (endpoint: HostMetadata) => { + let pendingIsolate = 0; + let pendingUnIsolate = 0; + + if (isLoadedResourceState(pendingActionsState)) { + const endpointPendingActions = pendingActionsState.data.get(endpoint.elastic.agent.id); + + if (endpointPendingActions) { + pendingIsolate = endpointPendingActions?.isolate ?? 0; + pendingUnIsolate = endpointPendingActions?.unisolate ?? 0; + } + } + + return { + isIsolated: isEndpointHostIsolated(endpoint), + pendingIsolate, + pendingUnIsolate, + }; + }; + } +); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index eed2182d41809d..59aa2bd15dd74a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -16,6 +16,7 @@ import { MetadataQueryStrategyVersions, HostStatus, HostIsolationResponse, + EndpointPendingActions, } from '../../../../common/endpoint/types'; import { ServerApiError } from '../../../common/types'; import { GetPackagesResponse } from '../../../../../fleet/common'; @@ -94,10 +95,18 @@ export interface EndpointState { policyVersionInfo?: HostInfo['policy_info']; /** The status of the host, which is mapped to the Elastic Agent status in Fleet */ hostStatus?: HostStatus; - /* Host isolation state */ + /** Host isolation request state for a single endpoint */ isolationRequestState: AsyncResourceState; + /** + * Holds a map of `agentId` to `EndpointPendingActions` that is used by both the list and details view + * Getting pending endpoint actions is "supplemental" data, so there is no need to show other Async + * states other than Loaded + */ + endpointPendingActions: AsyncResourceState; } +export type AgentIdsPendingActions = Map; + /** * packagePolicy contains a list of Package Policy IDs (received via Endpoint metadata policy response) mapped to a boolean whether they exist or not. * agentPolicy contains a list of existing Package Policy Ids mapped to an associated Fleet parent Agent Config. diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx new file mode 100644 index 00000000000000..9010bb5785c1d4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.test.tsx @@ -0,0 +1,90 @@ +/* + * 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 { + AppContextTestRender, + createAppRootMockRenderer, +} from '../../../../../common/mock/endpoint'; +import { endpointPageHttpMock } from '../../mocks'; +import { act } from '@testing-library/react'; +import { EndpointAgentStatus, EndpointAgentStatusProps } from './endpoint_agent_status'; +import { HostMetadata, HostStatus } from '../../../../../../common/endpoint/types'; +import { isLoadedResourceState } from '../../../../state'; +import { KibanaServices } from '../../../../../common/lib/kibana'; + +jest.mock('../../../../../common/lib/kibana'); + +describe('When using the EndpointAgentStatus component', () => { + let render: ( + props: EndpointAgentStatusProps + ) => Promise>; + let waitForAction: AppContextTestRender['middlewareSpy']['waitForAction']; + let renderResult: ReturnType; + let httpMocks: ReturnType; + let endpointMeta: HostMetadata; + + beforeEach(() => { + const mockedContext = createAppRootMockRenderer(); + + (KibanaServices.get as jest.Mock).mockReturnValue(mockedContext.startServices); + httpMocks = endpointPageHttpMock(mockedContext.coreStart.http); + waitForAction = mockedContext.middlewareSpy.waitForAction; + endpointMeta = httpMocks.responseProvider.metadataList().hosts[0].metadata; + render = async (props: EndpointAgentStatusProps) => { + renderResult = mockedContext.render(); + return renderResult; + }; + + act(() => { + mockedContext.history.push('/endpoints'); + }); + }); + + it.each([ + ['Healthy', 'healthy'], + ['Unhealthy', 'unhealthy'], + ['Updating', 'updating'], + ['Offline', 'offline'], + ['Inactive', 'inactive'], + ['Unhealthy', 'someUnknownValueHere'], + ])('should show agent status of %s', async (expectedLabel, hostStatus) => { + await render({ hostStatus: hostStatus as HostStatus, endpointMetadata: endpointMeta }); + expect(renderResult.getByTestId('rowHostStatus').textContent).toEqual(expectedLabel); + }); + + describe('and host is isolated or pending isolation', () => { + beforeEach(async () => { + // Ensure pending action api sets pending action for the test endpoint metadata + const pendingActionsResponseProvider = httpMocks.responseProvider.pendingActions.getMockImplementation(); + httpMocks.responseProvider.pendingActions.mockImplementation((...args) => { + const response = pendingActionsResponseProvider!(...args); + response.data.some((pendingAction) => { + if (pendingAction.agent_id === endpointMeta.elastic.agent.id) { + pendingAction.pending_actions.isolate = 1; + return true; + } + return false; + }); + return response; + }); + + const loadingPendingActions = waitForAction('endpointPendingActionsStateChanged', { + validate: (action) => isLoadedResourceState(action.payload), + }); + + await render({ hostStatus: HostStatus.HEALTHY, endpointMetadata: endpointMeta }); + await loadingPendingActions; + }); + + it('should show host pending action', () => { + expect(renderResult.getByTestId('rowIsolationStatus').textContent).toEqual( + 'Isolating pending' + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx new file mode 100644 index 00000000000000..94db233972d670 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/components/endpoint_agent_status.tsx @@ -0,0 +1,59 @@ +/* + * 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 { EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import styled from 'styled-components'; +import { HostInfo, HostMetadata } from '../../../../../../common/endpoint/types'; +import { HOST_STATUS_TO_BADGE_COLOR } from '../host_constants'; +import { EndpointHostIsolationStatus } from '../../../../../common/components/endpoint/host_isolation'; +import { useEndpointSelector } from '../hooks'; +import { getEndpointHostIsolationStatusPropsCallback } from '../../store/selectors'; + +const EuiFlexGroupStyled = styled(EuiFlexGroup)` + .isolation-status { + margin-left: ${({ theme }) => theme.eui.paddingSizes.s}; + } +`; + +export interface EndpointAgentStatusProps { + hostStatus: HostInfo['host_status']; + endpointMetadata: HostMetadata; +} +export const EndpointAgentStatus = memo( + ({ endpointMetadata, hostStatus }) => { + const getEndpointIsolationStatusProps = useEndpointSelector( + getEndpointHostIsolationStatusPropsCallback + ); + + return ( + + + + + + + + + + + ); + } +); + +EndpointAgentStatus.displayName = 'EndpointAgentStatus'; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx index 38404a5c6c11ff..64ea575c37d798 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx @@ -23,7 +23,7 @@ import { isPolicyOutOfDate } from '../../utils'; import { HostInfo, HostMetadata, HostStatus } from '../../../../../../common/endpoint/types'; import { useEndpointSelector } from '../hooks'; import { policyResponseStatus, uiQueryParams } from '../../store/selectors'; -import { POLICY_STATUS_TO_BADGE_COLOR, HOST_STATUS_TO_BADGE_COLOR } from '../host_constants'; +import { POLICY_STATUS_TO_BADGE_COLOR } from '../host_constants'; import { FormattedDateAndTime } from '../../../../../common/components/endpoint/formatted_date_time'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { getEndpointDetailsPath } from '../../../../common/routing'; @@ -31,6 +31,7 @@ import { SecurityPageName } from '../../../../../app/types'; import { useFormatUrl } from '../../../../../common/components/link_to'; import { EndpointPolicyLink } from '../components/endpoint_policy_link'; import { OutOfDate } from '../components/out_of_date'; +import { EndpointAgentStatus } from '../components/endpoint_agent_status'; const HostIds = styled(EuiListGroupItem)` margin-top: 0; @@ -88,20 +89,7 @@ export const EndpointDetails = memo( title: i18n.translate('xpack.securitySolution.endpoint.details.agentStatus', { defaultMessage: 'Agent Status', }), - description: ( - - - - - - ), + description: , }, { title: i18n.translate('xpack.securitySolution.endpoint.details.lastSeen', { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 4d1ab0f3de8252..410afb4684cd54 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -31,11 +31,7 @@ import { EndpointDetailsFlyout } from './details'; import * as selectors from '../store/selectors'; import { useEndpointSelector } from './hooks'; import { isPolicyOutOfDate } from '../utils'; -import { - HOST_STATUS_TO_BADGE_COLOR, - POLICY_STATUS_TO_BADGE_COLOR, - POLICY_STATUS_TO_TEXT, -} from './host_constants'; +import { POLICY_STATUS_TO_BADGE_COLOR, POLICY_STATUS_TO_TEXT } from './host_constants'; import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { CreateStructuredSelector } from '../../../../common/store'; import { Immutable, HostInfo } from '../../../../../common/endpoint/types'; @@ -59,6 +55,7 @@ import { AdministrationListPage } from '../../../components/administration_list_ import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; import { TableRowActions } from './components/table_row_actions'; +import { EndpointAgentStatus } from './components/endpoint_agent_status'; const MAX_PAGINATED_ITEM = 9999; @@ -97,6 +94,7 @@ const EndpointListNavLink = memo<{ }); EndpointListNavLink.displayName = 'EndpointListNavLink'; +// FIXME: this needs refactoring - we are pulling in all selectors from endpoint, which includes many more than what the list uses const selector = (createStructuredSelector as CreateStructuredSelector)(selectors); export const EndpointList = () => { const history = useHistory(); @@ -279,19 +277,9 @@ export const EndpointList = () => { defaultMessage: 'Agent Status', }), // eslint-disable-next-line react/display-name - render: (hostStatus: HostInfo['host_status']) => { + render: (hostStatus: HostInfo['host_status'], endpointInfo) => { return ( - - - + ); }, }, From 036c157f10081a39ba0a260f191c37794ed90a2a Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 16 Jun 2021 16:00:43 +0200 Subject: [PATCH 23/32] [Lens] Unload canvas test properly (#102101) --- x-pack/test/functional/apps/canvas/lens.ts | 4 ++++ .../feature_controls/index_patterns_security.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/x-pack/test/functional/apps/canvas/lens.ts b/x-pack/test/functional/apps/canvas/lens.ts index ed1bf246fae650..67ba40a99684ec 100644 --- a/x-pack/test/functional/apps/canvas/lens.ts +++ b/x-pack/test/functional/apps/canvas/lens.ts @@ -22,6 +22,10 @@ export default function canvasLensTest({ getService, getPageObjects }: FtrProvid }); }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/canvas/lens'); + }); + it('renders lens visualization', async () => { await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts index 04f251d247d1b5..52fcac769955c5 100644 --- a/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts +++ b/x-pack/test/functional/apps/index_patterns/feature_controls/index_patterns_security.ts @@ -20,6 +20,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { describe('security', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/empty_kibana'); + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); }); after(async () => { From a84293b7433d64f4070f886af0c9004e860c0019 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Wed, 16 Jun 2021 10:26:19 -0400 Subject: [PATCH 24/32] [Fleet + Integrations UI] Address UI Regressions in Fleet/Integrations (#102250) * Fix active tabs in integrations UI Fixes #101771 * Remove duplicate base breadcrumb Fixes #101785 * Fix i18n --- .../integrations/hooks/use_breadcrumbs.tsx | 9 +-------- .../sections/epm/screens/home/index.tsx | 18 ++++++++++-------- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx index 5c1745be0c9e48..19f72fdc69bba5 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_breadcrumbs.tsx @@ -22,14 +22,7 @@ const BASE_BREADCRUMB: ChromeBreadcrumb = { const breadcrumbGetters: { [key in Page]?: (values: DynamicPagePathValues) => ChromeBreadcrumb[]; } = { - integrations: () => [ - BASE_BREADCRUMB, - { - text: i18n.translate('xpack.fleet.breadcrumbs.integrationsPageTitle', { - defaultMessage: 'Integrations', - }), - }, - ], + integrations: () => [BASE_BREADCRUMB], integrations_all: () => [ BASE_BREADCRUMB, { diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 6c635d5d0c9c00..fbd6e07e07bbdb 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -22,16 +22,18 @@ import { CategoryFacets } from './category_facets'; export const EPMHomePage: React.FC = memo(() => { return ( - - - + + + - - + + + + - - - + + + ); }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c67dd383a2ea2c..fcb5710f8208f9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8939,7 +8939,6 @@ "xpack.fleet.breadcrumbs.editPackagePolicyPageTitle": "統合の編集", "xpack.fleet.breadcrumbs.enrollmentTokensPageTitle": "登録トークン", "xpack.fleet.breadcrumbs.installedIntegrationsPageTitle": "インストール済み", - "xpack.fleet.breadcrumbs.integrationsPageTitle": "統合", "xpack.fleet.breadcrumbs.overviewPageTitle": "概要", "xpack.fleet.breadcrumbs.policiesPageTitle": "ポリシー", "xpack.fleet.config.invalidPackageVersionError": "有効なサーバーまたはキーワード「latest」でなければなりません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 23cde5dd1fcff4..8c865b62d9d1ab 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9019,7 +9019,6 @@ "xpack.fleet.breadcrumbs.editPackagePolicyPageTitle": "编辑集成", "xpack.fleet.breadcrumbs.enrollmentTokensPageTitle": "注册令牌", "xpack.fleet.breadcrumbs.installedIntegrationsPageTitle": "已安装", - "xpack.fleet.breadcrumbs.integrationsPageTitle": "集成", "xpack.fleet.breadcrumbs.overviewPageTitle": "概览", "xpack.fleet.breadcrumbs.policiesPageTitle": "策略", "xpack.fleet.config.invalidPackageVersionError": "必须是有效的 semver 或关键字 `latest`", From c4af30845e033250cc13f32f1a143a49a29ec289 Mon Sep 17 00:00:00 2001 From: Pete Hampton Date: Wed, 16 Jun 2021 15:33:27 +0100 Subject: [PATCH 25/32] Add additional collection item to security allow list filter. (#102192) --- .../plugins/security_solution/server/lib/telemetry/sender.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts index baf4fb2d2cfd0d..2b3c002a9b2aeb 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/sender.ts @@ -347,6 +347,9 @@ const allowlistBaseEventFields: AllowlistFields = { direction: true, }, registry: { + data: { + strings: true, + }, hive: true, key: true, path: true, From bdc87409ba3c376fb964983f22b2a6405e8828e9 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 16 Jun 2021 10:35:55 -0400 Subject: [PATCH 26/32] [Lens] Create mathColumn function to improve performance (#101908) * [Lens] Create mathColumn function to improve performance * Fix empty formula case * Fix tinymath memoization Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../canvas/canvas-function-reference.asciidoc | 57 +++++++-- packages/kbn-tinymath/src/index.js | 9 +- .../expression_functions/specs/index.ts | 1 + .../expression_functions/specs/math_column.ts | 111 ++++++++++++++++++ .../specs/tests/math_column.test.ts | 74 ++++++++++++ .../common/service/expressions_services.ts | 2 + .../definitions/formula/formula.tsx | 21 +--- .../operations/definitions/formula/math.tsx | 19 +-- 8 files changed, 248 insertions(+), 46 deletions(-) create mode 100644 src/plugins/expressions/common/expression_functions/specs/math_column.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts diff --git a/docs/canvas/canvas-function-reference.asciidoc b/docs/canvas/canvas-function-reference.asciidoc index 272cd524c2c200..ac7cbba6e9933a 100644 --- a/docs/canvas/canvas-function-reference.asciidoc +++ b/docs/canvas/canvas-function-reference.asciidoc @@ -71,7 +71,7 @@ Alias: `condition` [[alterColumn_fn]] === `alterColumn` -Converts between core types, including `string`, `number`, `null`, `boolean`, and `date`, and renames columns. See also <> and <>. +Converts between core types, including `string`, `number`, `null`, `boolean`, and `date`, and renames columns. See also <>, <>, and <>. *Expression syntax* [source,js] @@ -1717,11 +1717,16 @@ Adds a column calculated as the result of other columns. Changes are made only w |=== |Argument |Type |Description +|`id` + +|`string`, `null` +|An optional id of the resulting column. When no id is provided, the id will be looked up from the existing column by the provided name argument. If no column with this name exists yet, a new column with this name and an identical id will be added to the table. + |_Unnamed_ *** Aliases: `column`, `name` |`string` -|The name of the resulting column. +|The name of the resulting column. Names are not required to be unique. |`expression` *** @@ -1729,11 +1734,6 @@ Aliases: `exp`, `fn`, `function` |`boolean`, `number`, `string`, `null` |A Canvas expression that is passed to each row as a single row `datatable`. -|`id` - -|`string`, `null` -|An optional id of the resulting column. When not specified or `null` the name argument is used as id. - |`copyMetaFrom` |`string`, `null` @@ -1808,6 +1808,47 @@ Default: `"throw"` *Returns:* `number` | `boolean` | `null` +[float] +[[mathColumn_fn]] +=== `mathColumn` + +Adds a column by evaluating `TinyMath` on each row. This function is optimized for math, so it performs better than the <> with a <>. +*Accepts:* `datatable` + +[cols="3*^<"] +|=== +|Argument |Type |Description + +|id *** +|`string` +|id of the resulting column. Must be unique. + +|name *** +|`string` +|The name of the resulting column. Names are not required to be unique. + +|_Unnamed_ + +Alias: `expression` +|`string` +|A `TinyMath` expression evaluated on each row. See https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html. + +|`onError` + +|`string` +|In case the `TinyMath` evaluation fails or returns NaN, the return value is specified by onError. For example, `"null"`, `"zero"`, `"false"`, `"throw"`. When `"throw"`, it will throw an exception, terminating expression execution. + +Default: `"throw"` + +|`copyMetaFrom` + +|`string`, `null` +|If set, the meta object from the specified column id is copied over to the specified target column. Throws an exception if the column doesn't exist +|=== + +*Returns:* `datatable` + + [float] [[metric_fn]] === `metric` @@ -2581,7 +2622,7 @@ Default: `false` [[staticColumn_fn]] === `staticColumn` -Adds a column with the same static value in every row. See also <> and <>. +Adds a column with the same static value in every row. See also <>, <>, and <>. *Accepts:* `datatable` diff --git a/packages/kbn-tinymath/src/index.js b/packages/kbn-tinymath/src/index.js index 9f1bb7b8514634..6fde4c202e2a77 100644 --- a/packages/kbn-tinymath/src/index.js +++ b/packages/kbn-tinymath/src/index.js @@ -7,12 +7,11 @@ */ const { get } = require('lodash'); +const memoizeOne = require('memoize-one'); // eslint-disable-next-line import/no-unresolved const { parse: parseFn } = require('../grammar'); const { functions: includedFunctions } = require('./functions'); -module.exports = { parse, evaluate, interpret }; - function parse(input, options) { if (input == null) { throw new Error('Missing expression'); @@ -29,9 +28,11 @@ function parse(input, options) { } } +const memoizedParse = memoizeOne(parse); + function evaluate(expression, scope = {}, injectedFunctions = {}) { scope = scope || {}; - return interpret(parse(expression), scope, injectedFunctions); + return interpret(memoizedParse(expression), scope, injectedFunctions); } function interpret(node, scope, injectedFunctions) { @@ -79,3 +80,5 @@ function isOperable(args) { return typeof arg === 'number' && !isNaN(arg); }); } + +module.exports = { parse: memoizedParse, evaluate, interpret }; diff --git a/src/plugins/expressions/common/expression_functions/specs/index.ts b/src/plugins/expressions/common/expression_functions/specs/index.ts index c6d89f41d0e0d3..e808021f751800 100644 --- a/src/plugins/expressions/common/expression_functions/specs/index.ts +++ b/src/plugins/expressions/common/expression_functions/specs/index.ts @@ -18,3 +18,4 @@ export * from './moving_average'; export * from './ui_setting'; export { mapColumn, MapColumnArguments } from './map_column'; export { math, MathArguments, MathInput } from './math'; +export { mathColumn, MathColumnArguments } from './math_column'; diff --git a/src/plugins/expressions/common/expression_functions/specs/math_column.ts b/src/plugins/expressions/common/expression_functions/specs/math_column.ts new file mode 100644 index 00000000000000..0ff8faf3ce55a1 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/math_column.ts @@ -0,0 +1,111 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../types'; +import { math, MathArguments } from './math'; +import { Datatable, DatatableColumn, getType } from '../../expression_types'; + +export type MathColumnArguments = MathArguments & { + id: string; + name?: string; + copyMetaFrom?: string | null; +}; + +export const mathColumn: ExpressionFunctionDefinition< + 'mathColumn', + Datatable, + MathColumnArguments, + Datatable +> = { + name: 'mathColumn', + type: 'datatable', + inputTypes: ['datatable'], + help: i18n.translate('expressions.functions.mathColumnHelpText', { + defaultMessage: + 'Adds a column calculated as the result of other columns. ' + + 'Changes are made only when you provide arguments.' + + 'See also {alterColumnFn} and {staticColumnFn}.', + values: { + alterColumnFn: '`alterColumn`', + staticColumnFn: '`staticColumn`', + }, + }), + args: { + ...math.args, + id: { + types: ['string'], + help: i18n.translate('expressions.functions.mathColumn.args.idHelpText', { + defaultMessage: 'id of the resulting column. Must be unique.', + }), + required: true, + }, + name: { + types: ['string'], + aliases: ['_', 'column'], + help: i18n.translate('expressions.functions.mathColumn.args.nameHelpText', { + defaultMessage: 'The name of the resulting column. Names are not required to be unique.', + }), + required: true, + }, + copyMetaFrom: { + types: ['string', 'null'], + help: i18n.translate('expressions.functions.mathColumn.args.copyMetaFromHelpText', { + defaultMessage: + "If set, the meta object from the specified column id is copied over to the specified target column. If the column doesn't exist it silently fails.", + }), + required: false, + default: null, + }, + }, + fn: (input, args, context) => { + const columns = [...input.columns]; + const existingColumnIndex = columns.findIndex(({ id }) => { + return id === args.id; + }); + if (existingColumnIndex > -1) { + throw new Error('ID must be unique'); + } + + const newRows = input.rows.map((row) => { + return { + ...row, + [args.id]: math.fn( + { + type: 'datatable', + columns: input.columns, + rows: [row], + }, + { + expression: args.expression, + onError: args.onError, + }, + context + ), + }; + }); + const type = newRows.length ? getType(newRows[0][args.id]) : 'null'; + const newColumn: DatatableColumn = { + id: args.id, + name: args.name ?? args.id, + meta: { type, params: { id: type } }, + }; + if (args.copyMetaFrom) { + const metaSourceFrom = columns.find(({ id }) => id === args.copyMetaFrom); + newColumn.meta = { ...newColumn.meta, ...(metaSourceFrom?.meta || {}) }; + } + + columns.push(newColumn); + + return { + type: 'datatable', + columns, + rows: newRows, + } as Datatable; + }, +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts new file mode 100644 index 00000000000000..bc6699a2b689bf --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts @@ -0,0 +1,74 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mathColumn } from '../math_column'; +import { functionWrapper, testTable } from './utils'; + +describe('mathColumn', () => { + const fn = functionWrapper(mathColumn); + + it('throws if the id is used', () => { + expect(() => fn(testTable, { id: 'price', name: 'price', expression: 'price * 2' })).toThrow( + `ID must be unique` + ); + }); + + it('applies math to each row by id', () => { + const result = fn(testTable, { id: 'output', name: 'output', expression: 'quantity * price' }); + expect(result.columns).toEqual([ + ...testTable.columns, + { id: 'output', name: 'output', meta: { params: { id: 'number' }, type: 'number' } }, + ]); + expect(result.rows[0]).toEqual({ + in_stock: true, + name: 'product1', + output: 60500, + price: 605, + quantity: 100, + time: 1517842800950, + }); + }); + + it('handles onError', () => { + const args = { + id: 'output', + name: 'output', + expression: 'quantity / 0', + }; + expect(() => fn(testTable, args)).toThrowError(`Cannot divide by 0`); + expect(() => fn(testTable, { ...args, onError: 'throw' })).toThrow(); + expect(fn(testTable, { ...args, onError: 'zero' }).rows[0].output).toEqual(0); + expect(fn(testTable, { ...args, onError: 'false' }).rows[0].output).toEqual(false); + expect(fn(testTable, { ...args, onError: 'null' }).rows[0].output).toEqual(null); + }); + + it('should copy over the meta information from the specified column', async () => { + const result = await fn( + { + ...testTable, + columns: [ + ...testTable.columns, + { + id: 'myId', + name: 'myName', + meta: { type: 'date', params: { id: 'number', params: { digits: 2 } } }, + }, + ], + rows: testTable.rows.map((row) => ({ ...row, myId: Date.now() })), + }, + { id: 'output', name: 'name', copyMetaFrom: 'myId', expression: 'price + 2' } + ); + + expect(result.type).toBe('datatable'); + expect(result.columns[result.columns.length - 1]).toEqual({ + id: 'output', + name: 'name', + meta: { type: 'date', params: { id: 'number', params: { digits: 2 } } }, + }); + }); +}); diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index f7afc12aa96bad..b3c01672626614 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -31,6 +31,7 @@ import { mapColumn, overallMetric, math, + mathColumn, } from '../expression_functions'; /** @@ -344,6 +345,7 @@ export class ExpressionsService implements PersistableStateService Date: Wed, 16 Jun 2021 11:02:55 -0400 Subject: [PATCH 27/32] [Watcher] Migrate to use new page layout (#101956) --- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../watcher/public/application/app.tsx | 44 +++--- .../components/page_error/page_error.tsx | 2 +- .../page_error/page_error_forbidden.tsx | 3 +- .../page_error/page_error_not_exist.tsx | 22 ++- .../json_watch_edit/json_watch_edit.tsx | 64 +++----- .../monitoring_watch_edit.tsx | 56 ++----- .../threshold_watch_edit.tsx | 27 ++-- .../watch_edit/components/watch_edit.tsx | 53 +++---- .../watch_list/components/watch_list.tsx | 149 ++++++++---------- .../watch_status/components/watch_status.tsx | 146 ++++++++--------- .../public/application/shared_imports.ts | 1 + 13 files changed, 262 insertions(+), 309 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fcb5710f8208f9..fb936a58387816 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24295,8 +24295,6 @@ "xpack.watcher.sections.watchEdit.json.titlePanel.editWatchTitle": "{watchName}を編集", "xpack.watcher.sections.watchEdit.loadingWatchDescription": "ウォッチの読み込み中…", "xpack.watcher.sections.watchEdit.loadingWatchVisualizationDescription": "ウォッチビジュアライゼーションを読み込み中…", - "xpack.watcher.sections.watchEdit.monitoring.edit.calloutDescriptionText": "ウォッチ'{watchName}'はシステムウォッチであるため、編集できません。{watchStatusLink}", - "xpack.watcher.sections.watchEdit.monitoring.edit.calloutTitleText": "このウォッチは編集できません。", "xpack.watcher.sections.watchEdit.monitoring.header.watchLinkTitle": "ウォッチステータスを表示します。", "xpack.watcher.sections.watchEdit.simulate.form.actionModesFieldLabel": "アクションモード", "xpack.watcher.sections.watchEdit.simulate.form.actionOverridesDescription": "ウォッチでアクションを実行またはスキップすることができるようにします。{actionsLink}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8c865b62d9d1ab..998b2a4c672872 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24665,8 +24665,6 @@ "xpack.watcher.sections.watchEdit.json.titlePanel.editWatchTitle": "编辑 {watchName}", "xpack.watcher.sections.watchEdit.loadingWatchDescription": "正在加载监视……", "xpack.watcher.sections.watchEdit.loadingWatchVisualizationDescription": "正在加载监视可视化……", - "xpack.watcher.sections.watchEdit.monitoring.edit.calloutDescriptionText": "监视“{watchName}”为系统监视,无法编辑。{watchStatusLink}", - "xpack.watcher.sections.watchEdit.monitoring.edit.calloutTitleText": "此监视无法编辑。", "xpack.watcher.sections.watchEdit.monitoring.header.watchLinkTitle": "查看监视状态。", "xpack.watcher.sections.watchEdit.simulate.form.actionModesFieldLabel": "操作模式", "xpack.watcher.sections.watchEdit.simulate.form.actionOverridesDescription": "允许监视执行或跳过操作。{actionsLink}", diff --git a/x-pack/plugins/watcher/public/application/app.tsx b/x-pack/plugins/watcher/public/application/app.tsx index 6c233a44830b54..98d10bce3b6b24 100644 --- a/x-pack/plugins/watcher/public/application/app.tsx +++ b/x-pack/plugins/watcher/public/application/app.tsx @@ -17,7 +17,7 @@ import { import { Router, Switch, Route, Redirect, withRouter, RouteComponentProps } from 'react-router-dom'; -import { EuiCallOut, EuiLink } from '@elastic/eui'; +import { EuiPageContent, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -62,24 +62,30 @@ export const App = (deps: AppDeps) => { if (!valid) { return ( - - } - color="danger" - iconType="help" - > - {message}{' '} - - - - + + + + + } + body={

{message}

} + actions={[ + + + , + ]} + /> +
); } return ( diff --git a/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx b/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx index ca05d390518f26..321b5c0e5e11b7 100644 --- a/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx +++ b/x-pack/plugins/watcher/public/application/components/page_error/page_error.tsx @@ -25,7 +25,7 @@ export function getPageErrorCode(errorOrErrors: any) { } } -export function PageError({ errorCode, id }: { errorCode?: any; id?: any }) { +export function PageError({ errorCode, id }: { errorCode?: number; id?: string }) { switch (errorCode) { case 404: return ; diff --git a/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx b/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx index c2e93c7f066001..56dc5c7dc22b53 100644 --- a/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx +++ b/x-pack/plugins/watcher/public/application/components/page_error/page_error_forbidden.tsx @@ -13,8 +13,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; export function PageErrorForbidden() { return ( - + {id ? ( + + ) : ( + + )}

} /> diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx index 8b5827fbd0fe0d..80931c3f60c05a 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx @@ -7,15 +7,7 @@ import React, { useContext, useState } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPageContent, - EuiSpacer, - EuiTab, - EuiTabs, - EuiTitle, -} from '@elastic/eui'; +import { EuiPageHeader, EuiSpacer, EuiPageContentBody } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ExecuteDetails } from '../../../../models/execute_details'; import { getActionType } from '../../../../../../common/lib/get_action_type'; @@ -96,36 +88,31 @@ export const JsonWatchEdit = ({ pageTitle }: { pageTitle: string }) => { const hasExecuteWatchErrors = !!Object.keys(executeWatchErrors).find( (errorKey) => executeWatchErrors[errorKey].length >= 1 ); + return ( - - - - -

{pageTitle}

-
-
-
- - {WATCH_TABS.map((tab, index) => ( - { - setSelectedTab(tab.id); - setExecuteDetails( - new ExecuteDetails({ - ...executeDetails, - actionModes: getActionModes(watchActions), - }) - ); - }} - isSelected={tab.id === selectedTab} - key={index} - data-test-subj="tab" - > - {tab.name} - - ))} - + + {pageTitle}} + bottomBorder + tabs={WATCH_TABS.map((tab, index) => ({ + onClick: () => { + setSelectedTab(tab.id); + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + actionModes: getActionModes(watchActions), + }) + ); + }, + isSelected: tab.id === selectedTab, + key: index, + 'data-test-subj': 'tab', + label: tab.name, + }))} + /> + + {selectedTab === WATCH_SIMULATE_TAB && ( { watchActions={watchActions} /> )} + {selectedTab === WATCH_EDIT_TAB && } -
+ ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx index 930c11340ce5e3..b00e4dc310e27e 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx @@ -7,16 +7,7 @@ import React, { useContext } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiPageContent, - EuiSpacer, - EuiTitle, - EuiCallOut, - EuiText, - EuiLink, -} from '@elastic/eui'; +import { EuiPageContent, EuiEmptyPrompt, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { WatchContext } from '../../watch_context'; import { useAppContext } from '../../../../app_context'; @@ -27,46 +18,31 @@ export const MonitoringWatchEdit = ({ pageTitle }: { pageTitle: string }) => { const { watch } = useContext(WatchContext); const { history } = useAppContext(); - const systemWatchTitle = ( - - ); - const systemWatchMessage = ( - - - ), }} /> ); return ( - - - - -

{pageTitle}

-
-
-
- - - -

{systemWatchMessage}

-
-
+ + {pageTitle}} + body={

{systemWatchMessage}

} + actions={[ + + + , + ]} + />
); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx index 2f89a3bc2be641..6587974363a802 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx @@ -18,13 +18,14 @@ import { EuiFlexGroup, EuiFlexItem, EuiForm, - EuiPageContent, EuiPopover, EuiPopoverTitle, EuiSelect, EuiSpacer, EuiText, EuiTitle, + EuiPageHeader, + EuiPageContentBody, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -236,19 +237,15 @@ export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { }; return ( - - - - -

{pageTitle}

-
- - - {watch.titleDescription} - -
-
- + + {pageTitle}} + description={watch.titleDescription} + bottomBorder + /> + + + {serverError && ( @@ -957,6 +954,6 @@ export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { close={() => setIsRequestVisible(false)} /> ) : null} -
+ ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx index 525ae077df655f..fa3c7e374f7b56 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/watch_edit.tsx @@ -10,19 +10,20 @@ import { isEqual } from 'lodash'; import { EuiPageContent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import { FormattedMessage } from '@kbn/i18n/react'; -import { Watch } from '../../../models/watch'; + import { WATCH_TYPES } from '../../../../../common/constants'; import { BaseWatch } from '../../../../../common/types/watch_types'; -import { getPageErrorCode, PageError, SectionLoading, SectionError } from '../../../components'; +import { getPageErrorCode, PageError, SectionLoading } from '../../../components'; import { loadWatch } from '../../../lib/api'; import { listBreadcrumb, editBreadcrumb, createBreadcrumb } from '../../../lib/breadcrumbs'; +import { useAppContext } from '../../../app_context'; +import { Watch } from '../../../models/watch'; +import { PageError as GenericPageError } from '../../../shared_imports'; +import { WatchContext } from '../watch_context'; import { JsonWatchEdit } from './json_watch_edit'; import { ThresholdWatchEdit } from './threshold_watch_edit'; import { MonitoringWatchEdit } from './monitoring_watch_edit'; -import { WatchContext } from '../watch_context'; -import { useAppContext } from '../../../app_context'; const getTitle = (watch: BaseWatch) => { if (watch.isNew) { @@ -115,7 +116,7 @@ export const WatchEdit = ({ const loadedWatch = await loadWatch(id); dispatch({ command: 'setWatch', payload: loadedWatch }); } catch (error) { - dispatch({ command: 'setError', payload: error }); + dispatch({ command: 'setError', payload: error.body }); } } else if (type) { const WatchType = Watch.getWatchTypes()[type]; @@ -135,36 +136,34 @@ export const WatchEdit = ({ const errorCode = getPageErrorCode(loadError); if (errorCode) { return ( - + ); - } - - if (loadError) { + } else if (loadError) { return ( - - - } - error={loadError} - /> - + + } + error={loadError} + /> ); } if (!watch) { return ( - - - + + + + + ); } diff --git a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx index 0e89871063507e..31accef0b63691 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_list/components/watch_list.tsx @@ -11,25 +11,25 @@ import { CriteriaWithPagination, EuiButton, EuiButtonEmpty, - EuiFlexGroup, - EuiFlexItem, EuiInMemoryTable, EuiLink, EuiPageContent, EuiSpacer, EuiText, - EuiTitle, EuiToolTip, EuiEmptyPrompt, EuiButtonIcon, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem, + EuiPageHeader, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Moment } from 'moment'; +import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; + import { REFRESH_INTERVALS, PAGINATION, WATCH_TYPES } from '../../../../../common/constants'; import { listBreadcrumb } from '../../../lib/breadcrumbs'; import { @@ -37,15 +37,13 @@ import { PageError, DeleteWatchesModal, WatchStatus, - SectionError, SectionLoading, Error, } from '../../../components'; import { useLoadWatches } from '../../../lib/api'; import { goToCreateThresholdAlert, goToCreateAdvancedWatch } from '../../../lib/navigation'; import { useAppContext } from '../../../app_context'; - -import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; +import { PageError as GenericPageError } from '../../../shared_imports'; export const WatchList = () => { // hooks @@ -173,21 +171,36 @@ export const WatchList = () => { if (isWatchesLoading) { return ( - - - + + + + + ); } - if (getPageErrorCode(error)) { + const errorCode = getPageErrorCode(error); + if (errorCode) { return ( - - + + ); + } else if (error) { + return ( + + } + error={(error as unknown) as Error} + /> + ); } if (availableWatches && availableWatches.length === 0) { @@ -206,7 +219,7 @@ export const WatchList = () => { ); return ( - + { let content; - if (error) { - content = ( - - } - error={(error as unknown) as Error} - /> - ); - } else if (availableWatches) { + if (availableWatches) { const columns = [ { field: 'id', @@ -463,56 +464,46 @@ export const WatchList = () => { ); } - if (content) { - return ( - - { - if (deleted) { - setDeletedWatches([...deletedWatches, ...watchesToDelete]); - } - setWatchesToDelete([]); - }} - watchesToDelete={watchesToDelete} - /> - - - - -

- -

-
- - - - - -
-
- - - - -

{watcherDescriptionText}

-
+ return ( + <> + + + + } + bottomBorder + rightSideItems={[ + + + , + ]} + description={watcherDescriptionText} + /> + { + if (deleted) { + setDeletedWatches([...deletedWatches, ...watchesToDelete]); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> - + - {content} -
- ); - } - return null; + {content} + + ); }; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx index 1e3548620339aa..73400b9ccaaa72 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_status/components/watch_status.tsx @@ -9,14 +9,10 @@ import React, { useEffect, useState } from 'react'; import { EuiPageContent, EuiSpacer, - EuiTabs, - EuiTab, - EuiFlexGroup, - EuiFlexItem, - EuiTitle, EuiToolTip, EuiBadge, EuiButtonEmpty, + EuiPageHeader, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -88,18 +84,20 @@ export const WatchStatus = ({ if (isWatchDetailLoading) { return ( - - - + + + + + ); } if (errorCode) { return ( - + ); @@ -156,20 +154,11 @@ export const WatchStatus = ({ return ( - - { - if (deleted) { - goToWatchList(); - } - setWatchesToDelete([]); - }} - watchesToDelete={watchesToDelete} - /> - - - -

+ <> + + -

-
-
- {isSystemWatch ? ( - - - } - > - - - - - - ) : ( - - - + + {isSystemWatch && ( + <> + {' '} + + } + > + + + + + + )} + + } + bottomBorder + rightSideItems={ + isSystemWatch + ? [] + : [ toggleWatchActivation()} isLoading={isTogglingActivation} > {activationButtonText} - - - + , { @@ -223,30 +213,34 @@ export const WatchStatus = ({ id="xpack.watcher.sections.watchHistory.deleteWatchButtonLabel" defaultMessage="Delete" /> - - - - - )} -
- - - {WATCH_STATUS_TABS.map((tab, index) => ( - { - setSelectedTab(tab.id); - }} - isSelected={tab.id === selectedTab} - key={index} - data-test-subj="tab" - > - {tab.name} - - ))} - + , + ] + } + tabs={WATCH_STATUS_TABS.map((tab, index) => ({ + onClick: () => { + setSelectedTab(tab.id); + }, + isSelected: tab.id === selectedTab, + key: index, + 'data-test-subj': 'tab', + label: tab.name, + }))} + /> + + {selectedTab === WATCH_ACTIONS_TAB ? : } -
+ + { + if (deleted) { + goToWatchList(); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> +
); } diff --git a/x-pack/plugins/watcher/public/application/shared_imports.ts b/x-pack/plugins/watcher/public/application/shared_imports.ts index e3eb11eda77b30..44bef3b0c4f5f6 100644 --- a/x-pack/plugins/watcher/public/application/shared_imports.ts +++ b/x-pack/plugins/watcher/public/application/shared_imports.ts @@ -12,4 +12,5 @@ export { sendRequest, useRequest, XJson, + PageError, } from '../../../../../src/plugins/es_ui_shared/public'; From 8abb656d7f929c2a93b143b9e87f6ddc00d1f3a2 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Wed, 16 Jun 2021 18:15:47 +0300 Subject: [PATCH 28/32] [Kuery] Move json utils (#102058) * Move JSON utils to utils package * Imports from tests * delete * split package * docs * test * test * imports Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...bana-plugin-plugins-data-public.eskuery.md | 2 +- ...bana-plugin-plugins-data-server.eskuery.md | 2 +- package.json | 1 + packages/BUILD.bazel | 1 + packages/kbn-common-utils/BUILD.bazel | 82 +++++++++++++++++++ packages/kbn-common-utils/README.md | 3 + packages/kbn-common-utils/jest.config.js | 13 +++ packages/kbn-common-utils/package.json | 9 ++ packages/kbn-common-utils/src/index.ts | 9 ++ packages/kbn-common-utils/src/json/index.ts | 9 ++ .../kbn-common-utils/src/json}/typed_json.ts | 0 packages/kbn-common-utils/tsconfig.json | 18 ++++ .../data/common/es_query/kuery/ast/ast.ts | 2 +- .../es_query/kuery/node_types/named_arg.ts | 2 +- .../common/es_query/kuery/node_types/types.ts | 2 +- src/plugins/data/public/public.api.md | 3 +- src/plugins/data/server/server.api.md | 3 +- src/plugins/kibana_utils/common/index.ts | 1 - src/plugins/kibana_utils/public/index.ts | 3 - .../alerting/common/alert_navigation.ts | 3 +- .../public/alert_navigation_registry/types.ts | 2 +- .../authorization/alerting_authorization.ts | 2 +- .../alerting_authorization_kuery.ts | 2 +- .../server/routes/lib/rewrite_request_case.ts | 2 +- .../graph/public/types/workspace_state.ts | 2 +- x-pack/plugins/infra/common/typed_json.ts | 2 +- .../components/log_stream/log_stream.tsx | 2 +- .../logging/log_text_stream/field_value.tsx | 2 +- .../log_entry_field_column.tsx | 2 +- .../utils/log_column_render_configuration.tsx | 2 +- .../lib/adapters/framework/adapter_types.ts | 2 +- .../log_entries/kibana_log_entries_adapter.ts | 2 +- .../log_entries_domain/log_entries_domain.ts | 3 +- .../snapshot/lib/get_metrics_aggregations.ts | 2 +- .../services/log_entries/message/message.ts | 2 +- .../log_entries/message/rule_types.ts | 2 +- .../infra/server/utils/serialized_query.ts | 2 +- .../server/utils/typed_search_strategy.ts | 2 +- x-pack/plugins/ml/common/types/es_client.ts | 2 +- x-pack/plugins/osquery/common/typed_json.ts | 2 +- .../security_solution/common/typed_json.ts | 2 +- .../public/common/lib/keury/index.ts | 2 +- .../routes/resolver/queries/events.ts | 2 +- .../resolver/tree/queries/descendants.ts | 2 +- .../routes/resolver/tree/queries/lifecycle.ts | 2 +- .../routes/resolver/tree/queries/stats.ts | 2 +- .../routes/resolver/utils/pagination.ts | 2 +- .../server/endpoint/types.ts | 2 +- .../server/utils/serialized_query.ts | 2 +- .../server/monitoring/capacity_estimation.ts | 2 +- .../monitoring_stats_stream.test.ts | 2 +- .../monitoring/monitoring_stats_stream.ts | 2 +- .../runtime_statistics_aggregator.ts | 2 +- .../server/monitoring/task_run_calcultors.ts | 2 +- .../server/monitoring/task_run_statistics.ts | 2 +- .../server/monitoring/workload_statistics.ts | 2 +- .../task_manager/server/routes/health.ts | 2 +- .../uptime/server/lib/alerts/status_check.ts | 2 +- .../server/lib/requests/get_monitor_status.ts | 2 +- .../tests/services/annotations.ts | 2 +- .../basic/tests/annotations.ts | 2 +- .../trial/tests/annotations.ts | 2 +- .../apis/resolver/events.ts | 2 +- yarn.lock | 4 + 64 files changed, 203 insertions(+), 56 deletions(-) create mode 100644 packages/kbn-common-utils/BUILD.bazel create mode 100644 packages/kbn-common-utils/README.md create mode 100644 packages/kbn-common-utils/jest.config.js create mode 100644 packages/kbn-common-utils/package.json create mode 100644 packages/kbn-common-utils/src/index.ts create mode 100644 packages/kbn-common-utils/src/json/index.ts rename {src/plugins/kibana_utils/common => packages/kbn-common-utils/src/json}/typed_json.ts (100%) create mode 100644 packages/kbn-common-utils/tsconfig.json diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md index 5d92e348d62760..2cde2b74555851 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.eskuery.md @@ -10,6 +10,6 @@ esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("../../kibana_utils/common").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md index 19cb742785e7b2..4b96d8af756f37 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.eskuery.md @@ -10,6 +10,6 @@ esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("../../kibana_utils/common").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; } ``` diff --git a/package.json b/package.json index c9c6fa7f582c59..596bcff59797d8 100644 --- a/package.json +++ b/package.json @@ -156,6 +156,7 @@ "@kbn/ui-framework": "link:packages/kbn-ui-framework", "@kbn/ui-shared-deps": "link:packages/kbn-ui-shared-deps", "@kbn/utility-types": "link:bazel-bin/packages/kbn-utility-types", + "@kbn/common-utils": "link:bazel-bin/packages/kbn-common-utils", "@kbn/utils": "link:bazel-bin/packages/kbn-utils", "@loaders.gl/core": "^2.3.1", "@loaders.gl/json": "^2.3.1", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 3e17d471a3cac0..f2510a2386aa2c 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -12,6 +12,7 @@ filegroup( "//packages/kbn-apm-utils:build", "//packages/kbn-babel-code-parser:build", "//packages/kbn-babel-preset:build", + "//packages/kbn-common-utils:build", "//packages/kbn-config:build", "//packages/kbn-config-schema:build", "//packages/kbn-crypto:build", diff --git a/packages/kbn-common-utils/BUILD.bazel b/packages/kbn-common-utils/BUILD.bazel new file mode 100644 index 00000000000000..02446849733537 --- /dev/null +++ b/packages/kbn-common-utils/BUILD.bazel @@ -0,0 +1,82 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm") + +PKG_BASE_NAME = "kbn-common-utils" +PKG_REQUIRE_NAME = "@kbn/common-utils" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + ], + exclude = ["**/*.test.*"], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", + "README.md" +] + +SRC_DEPS = [ + "//packages/kbn-config-schema", + "@npm//load-json-file", + "@npm//tslib", +] + +TYPES_DEPS = [ + "@npm//@types/jest", + "@npm//@types/node", +] + +DEPS = SRC_DEPS + TYPES_DEPS + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + ], +) + +ts_project( + name = "tsc", + args = ['--pretty'], + srcs = SRCS, + deps = DEPS, + declaration = True, + declaration_map = True, + incremental = True, + out_dir = "target", + source_map = True, + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_BASE_NAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = DEPS + [":tsc"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [ + ":%s" % PKG_BASE_NAME, + ] +) + +filegroup( + name = "build", + srcs = [ + ":npm_module", + ], + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-common-utils/README.md b/packages/kbn-common-utils/README.md new file mode 100644 index 00000000000000..7b64c9f18fe89d --- /dev/null +++ b/packages/kbn-common-utils/README.md @@ -0,0 +1,3 @@ +# @kbn/common-utils + +Shared common (client and server sie) utilities shared across packages and plugins. \ No newline at end of file diff --git a/packages/kbn-common-utils/jest.config.js b/packages/kbn-common-utils/jest.config.js new file mode 100644 index 00000000000000..08f1995c474236 --- /dev/null +++ b/packages/kbn-common-utils/jest.config.js @@ -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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-common-utils'], +}; diff --git a/packages/kbn-common-utils/package.json b/packages/kbn-common-utils/package.json new file mode 100644 index 00000000000000..db99f4d6afb985 --- /dev/null +++ b/packages/kbn-common-utils/package.json @@ -0,0 +1,9 @@ +{ + "name": "@kbn/common-utils", + "main": "./target/index.js", + "browser": "./target/index.js", + "types": "./target/index.d.ts", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "private": true +} \ No newline at end of file diff --git a/packages/kbn-common-utils/src/index.ts b/packages/kbn-common-utils/src/index.ts new file mode 100644 index 00000000000000..1b8bffe4bf1580 --- /dev/null +++ b/packages/kbn-common-utils/src/index.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './json'; diff --git a/packages/kbn-common-utils/src/json/index.ts b/packages/kbn-common-utils/src/json/index.ts new file mode 100644 index 00000000000000..96c94df1bb48eb --- /dev/null +++ b/packages/kbn-common-utils/src/json/index.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { JsonArray, JsonValue, JsonObject } from './typed_json'; diff --git a/src/plugins/kibana_utils/common/typed_json.ts b/packages/kbn-common-utils/src/json/typed_json.ts similarity index 100% rename from src/plugins/kibana_utils/common/typed_json.ts rename to packages/kbn-common-utils/src/json/typed_json.ts diff --git a/packages/kbn-common-utils/tsconfig.json b/packages/kbn-common-utils/tsconfig.json new file mode 100644 index 00000000000000..98f1b30c0d7ff2 --- /dev/null +++ b/packages/kbn-common-utils/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "incremental": true, + "outDir": "target", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "sourceRoot": "../../../../packages/kbn-common-utils/src", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/src/plugins/data/common/es_query/kuery/ast/ast.ts b/src/plugins/data/common/es_query/kuery/ast/ast.ts index 5b22e3b3a3e0ea..be821289699689 100644 --- a/src/plugins/data/common/es_query/kuery/ast/ast.ts +++ b/src/plugins/data/common/es_query/kuery/ast/ast.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { JsonObject } from '@kbn/common-utils'; import { nodeTypes } from '../node_types/index'; import { KQLSyntaxError } from '../kuery_syntax_error'; import { KueryNode, DslQuery, KueryParseOptions } from '../types'; @@ -13,7 +14,6 @@ import { IIndexPattern } from '../../../index_patterns/types'; // @ts-ignore import { parse as parseKuery } from './_generated_/kuery'; -import { JsonObject } from '../../../../../kibana_utils/common'; const fromExpression = ( expression: string | DslQuery, diff --git a/src/plugins/data/common/es_query/kuery/node_types/named_arg.ts b/src/plugins/data/common/es_query/kuery/node_types/named_arg.ts index c65f195040b185..b1b202e4323af7 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/named_arg.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/named_arg.ts @@ -7,10 +7,10 @@ */ import _ from 'lodash'; +import { JsonObject } from '@kbn/common-utils'; import * as ast from '../ast'; import { nodeTypes } from '../node_types'; import { NamedArgTypeBuildNode } from './types'; -import { JsonObject } from '../../../../../kibana_utils/common'; export function buildNode(name: string, value: any): NamedArgTypeBuildNode { const argumentNode = diff --git a/src/plugins/data/common/es_query/kuery/node_types/types.ts b/src/plugins/data/common/es_query/kuery/node_types/types.ts index 196890ed0f7a3a..b3247a0ad8dc21 100644 --- a/src/plugins/data/common/es_query/kuery/node_types/types.ts +++ b/src/plugins/data/common/es_query/kuery/node_types/types.ts @@ -10,8 +10,8 @@ * WARNING: these typings are incomplete */ +import { JsonValue } from '@kbn/common-utils'; import { IIndexPattern } from '../../../index_patterns'; -import { JsonValue } from '../../../../../kibana_utils/common'; import { KueryNode } from '..'; export type FunctionName = diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 13352d183370bd..d56727b468da6f 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -53,6 +53,7 @@ import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IUiSettingsClient } from 'src/core/public'; +import { JsonValue } from '@kbn/common-utils'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; @@ -856,7 +857,7 @@ export const esFilters: { export const esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("../../kibana_utils/common").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 783bd8d2fcd0e1..c2b533bc42dc6f 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -38,6 +38,7 @@ import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource } from 'src/plugins/data/public'; import { IUiSettingsClient } from 'src/core/server'; import { IUiSettingsClient as IUiSettingsClient_3 } from 'kibana/server'; +import { JsonValue } from '@kbn/common-utils'; import { KibanaRequest } from 'src/core/server'; import { KibanaRequest as KibanaRequest_2 } from 'kibana/server'; import { Logger } from 'src/core/server'; @@ -460,7 +461,7 @@ export const esFilters: { export const esKuery: { nodeTypes: import("../common/es_query/kuery/node_types").NodeTypes; fromKueryExpression: (expression: any, parseOptions?: Partial) => import("../common").KueryNode; - toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("../../kibana_utils/common").JsonObject; + toElasticsearchQuery: (node: import("../common").KueryNode, indexPattern?: import("../common").IIndexPattern | undefined, config?: Record | undefined, context?: Record | undefined) => import("@kbn/common-utils").JsonObject; }; // Warning: (ae-missing-release-tag) "esQuery" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index 76a7cb2855c6e0..773c0b96d64136 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -11,7 +11,6 @@ export * from './field_wildcard'; export * from './of'; export * from './ui'; export * from './state_containers'; -export * from './typed_json'; export * from './errors'; export { AbortError, abortSignalToPromise } from './abort_utils'; export { createGetterSetter, Get, Set } from './create_getter_setter'; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 75c52e1301ea57..3d9b5db0629558 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -15,9 +15,6 @@ export { fieldWildcardFilter, fieldWildcardMatcher, Get, - JsonArray, - JsonObject, - JsonValue, of, Set, UiComponent, diff --git a/x-pack/plugins/alerting/common/alert_navigation.ts b/x-pack/plugins/alerting/common/alert_navigation.ts index d26afff9e8243f..7c9e428f9a09ee 100644 --- a/x-pack/plugins/alerting/common/alert_navigation.ts +++ b/x-pack/plugins/alerting/common/alert_navigation.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { JsonObject } from '../../../../src/plugins/kibana_utils/common'; - +import { JsonObject } from '@kbn/common-utils'; export interface AlertUrlNavigation { path: string; } diff --git a/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts b/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts index 53540facd9652e..12ac9061426475 100644 --- a/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts +++ b/x-pack/plugins/alerting/public/alert_navigation_registry/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { SanitizedAlert } from '../../common'; /** diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts index 7506accd8b88ea..52cef9a402e352 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization.ts @@ -8,6 +8,7 @@ import Boom from '@hapi/boom'; import { map, mapValues, fromPairs, has } from 'lodash'; import { KibanaRequest } from 'src/core/server'; +import { JsonObject } from '@kbn/common-utils'; import { AlertTypeRegistry } from '../types'; import { SecurityPluginSetup } from '../../../security/server'; import { RegistryAlertType } from '../alert_type_registry'; @@ -19,7 +20,6 @@ import { AlertingAuthorizationFilterOpts, } from './alerting_authorization_kuery'; import { KueryNode } from '../../../../../src/plugins/data/server'; -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; export enum AlertingAuthorizationEntity { Rule = 'rule', diff --git a/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.ts b/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.ts index eb6f1605f2ba5a..5205e6afdf29f3 100644 --- a/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.ts +++ b/x-pack/plugins/alerting/server/authorization/alerting_authorization_kuery.ts @@ -6,7 +6,7 @@ */ import { remove } from 'lodash'; -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { nodeBuilder, EsQueryConfig } from '../../../../../src/plugins/data/common'; import { toElasticsearchQuery } from '../../../../../src/plugins/data/common/es_query'; import { KueryNode } from '../../../../../src/plugins/data/server'; diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts index 361ba5ff5e55de..f5455d1a630934 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { JsonValue } from '../../../../../../src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; type RenameAlertToRule = K extends `alertTypeId` ? `ruleTypeId` diff --git a/x-pack/plugins/graph/public/types/workspace_state.ts b/x-pack/plugins/graph/public/types/workspace_state.ts index 73d76cfd9cc572..e511a2eb5c7798 100644 --- a/x-pack/plugins/graph/public/types/workspace_state.ts +++ b/x-pack/plugins/graph/public/types/workspace_state.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { JsonObject } from '@kbn/common-utils'; import { FontawesomeIcon } from '../helpers/style_choices'; import { WorkspaceField, AdvancedSettings } from './app_state'; -import { JsonObject } from '../../../../../src/plugins/kibana_utils/public'; export interface WorkspaceNode { x: number; diff --git a/x-pack/plugins/infra/common/typed_json.ts b/x-pack/plugins/infra/common/typed_json.ts index 5b7bbdcfbc07bd..44409ab433a601 100644 --- a/x-pack/plugins/infra/common/typed_json.ts +++ b/x-pack/plugins/infra/common/typed_json.ts @@ -6,7 +6,7 @@ */ import * as rt from 'io-ts'; -import { JsonArray, JsonObject, JsonValue } from '../../../../src/plugins/kibana_utils/common'; +import { JsonArray, JsonObject, JsonValue } from '@kbn/common-utils'; export { JsonArray, JsonObject, JsonValue }; diff --git a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx index 44d78591fbf2f3..0087d559a42e60 100644 --- a/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx +++ b/x-pack/plugins/infra/public/components/log_stream/log_stream.tsx @@ -7,6 +7,7 @@ import React, { useMemo, useCallback, useEffect } from 'react'; import { noop } from 'lodash'; +import { JsonValue } from '@kbn/common-utils'; import { DataPublicPluginStart, esQuery, Filter } from '../../../../../../src/plugins/data/public'; import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { LogEntryCursor } from '../../../common/log_entry'; @@ -17,7 +18,6 @@ import { BuiltEsQuery, useLogStream } from '../../containers/logs/log_stream'; import { ScrollableLogTextStreamView } from '../logging/log_text_stream'; import { LogColumnRenderConfiguration } from '../../utils/log_column_render_configuration'; -import { JsonValue } from '../../../../../../src/plugins/kibana_utils/common'; import { Query } from '../../../../../../src/plugins/data/common'; import { LogStreamErrorBoundary } from './log_stream_error_boundary'; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx index 29e511b2467e10..9cffef270219e4 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/field_value.tsx @@ -7,8 +7,8 @@ import stringify from 'json-stable-stringify'; import React from 'react'; +import { JsonArray, JsonValue } from '@kbn/common-utils'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; -import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { ActiveHighlightMarker, highlightFieldValue, HighlightMarker } from './highlighting'; export const FieldValue: React.FC<{ diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx index 4fffa8eb0ee021..33e81756552d8e 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_field_column.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { LogColumn } from '../../../../common/log_entry'; import { isFieldColumn, isHighlightFieldColumn } from '../../../utils/log_entry'; diff --git a/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx b/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx index 3758e02e77312b..a6adc716e02fbc 100644 --- a/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx +++ b/x-pack/plugins/infra/public/utils/log_column_render_configuration.tsx @@ -6,7 +6,7 @@ */ import { ReactNode } from 'react'; -import { JsonValue } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; /** * Interface for common configuration properties, regardless of the column type. diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 1231a19f80ca25..1657d41d0b7936 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -8,6 +8,7 @@ import { GenericParams, SearchResponse } from 'elasticsearch'; import { Lifecycle } from '@hapi/hapi'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { JsonArray, JsonValue } from '@kbn/common-utils'; import { RouteConfig, RouteMethod } from '../../../../../../../src/core/server'; import { PluginSetup as DataPluginSetup, @@ -19,7 +20,6 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../pl import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerting/server'; import { MlPluginSetup } from '../../../../../ml/server'; -import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; export interface InfraServerPluginSetupDeps { data: DataPluginSetup; diff --git a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts index 3aaa747b945a82..9f2e9e2713bbc7 100644 --- a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts @@ -11,7 +11,7 @@ import { constant, identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import * as runtimeTypes from 'io-ts'; import { compact } from 'lodash'; -import { JsonArray } from '../../../../../../../src/plugins/kibana_utils/common'; +import { JsonArray } from '@kbn/common-utils'; import type { InfraPluginRequestHandlerContext } from '../../../types'; import { LogEntriesAdapter, diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index ad8650bbb0fb65..f8268570710f2c 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -6,7 +6,8 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; + import type { InfraPluginRequestHandlerContext } from '../../../types'; import { diff --git a/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts b/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts index 0878131a69f0e6..33060f428b7ff3 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/lib/get_metrics_aggregations.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { InventoryItemType, MetricsUIAggregation, diff --git a/x-pack/plugins/infra/server/services/log_entries/message/message.ts b/x-pack/plugins/infra/server/services/log_entries/message/message.ts index 83d9aa6c8954ca..2deee584f5187a 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/message.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/message.ts @@ -5,8 +5,8 @@ * 2.0. */ +import { JsonArray, JsonValue } from '@kbn/common-utils'; import { LogMessagePart } from '../../../../common/log_entry'; -import { JsonArray, JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; import { LogMessageFormattingCondition, LogMessageFormattingFieldValueConditionValue, diff --git a/x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts b/x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts index 4760382fd9bdd0..56d1b38e7e3902 100644 --- a/x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts +++ b/x-pack/plugins/infra/server/services/log_entries/message/rule_types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { JsonValue } from '../../../../../../../src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; export interface LogMessageFormattingRule { when: LogMessageFormattingCondition; diff --git a/x-pack/plugins/infra/server/utils/serialized_query.ts b/x-pack/plugins/infra/server/utils/serialized_query.ts index 4f813d4fb137f3..4169e123d85322 100644 --- a/x-pack/plugins/infra/server/utils/serialized_query.ts +++ b/x-pack/plugins/infra/server/utils/serialized_query.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; export const parseFilterQuery = ( filterQuery: string | null | undefined diff --git a/x-pack/plugins/infra/server/utils/typed_search_strategy.ts b/x-pack/plugins/infra/server/utils/typed_search_strategy.ts index 546fd90a2da501..2482694474b0ed 100644 --- a/x-pack/plugins/infra/server/utils/typed_search_strategy.ts +++ b/x-pack/plugins/infra/server/utils/typed_search_strategy.ts @@ -7,7 +7,7 @@ import * as rt from 'io-ts'; import stringify from 'json-stable-stringify'; -import { JsonValue } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; import { jsonValueRT } from '../../common/typed_json'; import { SearchStrategyError } from '../../common/search_strategies/common/errors'; import { ShardFailure } from './elasticsearch_runtime_types'; diff --git a/x-pack/plugins/ml/common/types/es_client.ts b/x-pack/plugins/ml/common/types/es_client.ts index 67adda6b24c18a..29a7a81aa56939 100644 --- a/x-pack/plugins/ml/common/types/es_client.ts +++ b/x-pack/plugins/ml/common/types/es_client.ts @@ -7,9 +7,9 @@ import { estypes } from '@elastic/elasticsearch'; +import { JsonObject } from '@kbn/common-utils'; import { buildEsQuery } from '../../../../../src/plugins/data/common/es_query/es_query'; import type { DslQuery } from '../../../../../src/plugins/data/common/es_query/kuery'; -import type { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; import { isPopulatedObject } from '../util/object_utils'; diff --git a/x-pack/plugins/osquery/common/typed_json.ts b/x-pack/plugins/osquery/common/typed_json.ts index fb24b1dc0db5e3..8ce6907beb80bd 100644 --- a/x-pack/plugins/osquery/common/typed_json.ts +++ b/x-pack/plugins/osquery/common/typed_json.ts @@ -7,7 +7,7 @@ import { DslQuery, Filter } from 'src/plugins/data/common'; -import { JsonObject } from '../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; export type ESQuery = | ESRangeQuery diff --git a/x-pack/plugins/security_solution/common/typed_json.ts b/x-pack/plugins/security_solution/common/typed_json.ts index fb24b1dc0db5e3..8ce6907beb80bd 100644 --- a/x-pack/plugins/security_solution/common/typed_json.ts +++ b/x-pack/plugins/security_solution/common/typed_json.ts @@ -7,7 +7,7 @@ import { DslQuery, Filter } from 'src/plugins/data/common'; -import { JsonObject } from '../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; export type ESQuery = | ESRangeQuery diff --git a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts index bd026f486471f1..a71524f9e02a86 100644 --- a/x-pack/plugins/security_solution/public/common/lib/keury/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/keury/index.ts @@ -7,6 +7,7 @@ import { isEmpty, isString, flow } from 'lodash/fp'; +import { JsonObject } from '@kbn/common-utils'; import { EsQueryConfig, Query, @@ -15,7 +16,6 @@ import { esKuery, IIndexPattern, } from '../../../../../../../src/plugins/data/public'; -import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/public'; export const convertKueryToElasticSearchQuery = ( kueryExpression: string, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts index 28a220c6f048a6..70e74356188c76 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/queries/events.ts @@ -6,10 +6,10 @@ */ import type { IScopedClusterClient } from 'kibana/server'; +import { JsonObject } from '@kbn/common-utils'; import { parseFilterQuery } from '../../../../utils/serialized_query'; import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { PaginationBuilder } from '../utils/pagination'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; interface TimeRange { from: string; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts index bf9b3ce6aa8f3a..331f622951515e 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/descendants.ts @@ -7,8 +7,8 @@ import type { ApiResponse, estypes } from '@elastic/elasticsearch'; import { IScopedClusterClient } from 'src/core/server'; +import { JsonObject, JsonValue } from '@kbn/common-utils'; import { FieldsObject, ResolverSchema } from '../../../../../../common/endpoint/types'; -import { JsonObject, JsonValue } from '../../../../../../../../../src/plugins/kibana_utils/common'; import { NodeID, TimeRange, docValueFields, validIDs } from '../utils/index'; interface DescendantsParams { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts index f9780d1469756e..7de038ccc9ae45 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/lifecycle.ts @@ -6,8 +6,8 @@ */ import { IScopedClusterClient } from 'src/core/server'; +import { JsonObject, JsonValue } from '@kbn/common-utils'; import { FieldsObject, ResolverSchema } from '../../../../../../common/endpoint/types'; -import { JsonObject, JsonValue } from '../../../../../../../../../src/plugins/kibana_utils/common'; import { NodeID, TimeRange, docValueFields, validIDs } from '../utils/index'; interface LifecycleParams { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/stats.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/stats.ts index 24c97ad88b26ae..f21259980d464f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/stats.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/tree/queries/stats.ts @@ -6,7 +6,7 @@ */ import { IScopedClusterClient } from 'src/core/server'; -import { JsonObject } from '../../../../../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { EventStats, ResolverSchema } from '../../../../../../common/endpoint/types'; import { NodeID, TimeRange } from '../utils/index'; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts index befd69bdcf953c..24fc447173ba6c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.ts @@ -5,12 +5,12 @@ * 2.0. */ +import { JsonObject } from '@kbn/common-utils'; import { SafeResolverEvent } from '../../../../../common/endpoint/types'; import { eventIDSafeVersion, timestampSafeVersion, } from '../../../../../common/endpoint/models/event'; -import { JsonObject } from '../../../../../../../../src/plugins/kibana_utils/common'; type SearchAfterFields = [number, string]; diff --git a/x-pack/plugins/security_solution/server/endpoint/types.ts b/x-pack/plugins/security_solution/server/endpoint/types.ts index b3c7e58afe9915..6076aa9af635bf 100644 --- a/x-pack/plugins/security_solution/server/endpoint/types.ts +++ b/x-pack/plugins/security_solution/server/endpoint/types.ts @@ -8,9 +8,9 @@ import { LoggerFactory } from 'kibana/server'; import { SearchResponse } from '@elastic/elasticsearch/api/types'; +import { JsonObject } from '@kbn/common-utils'; import { ConfigType } from '../config'; import { EndpointAppContextService } from './endpoint_app_context_services'; -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; import { HostMetadata, MetadataQueryStrategyVersions } from '../../common/endpoint/types'; import { ExperimentalFeatures } from '../../common/experimental_features'; diff --git a/x-pack/plugins/security_solution/server/utils/serialized_query.ts b/x-pack/plugins/security_solution/server/utils/serialized_query.ts index fb5009eefa3180..7f8603ccab4b76 100644 --- a/x-pack/plugins/security_solution/server/utils/serialized_query.ts +++ b/x-pack/plugins/security_solution/server/utils/serialized_query.ts @@ -7,7 +7,7 @@ import { isEmpty, isPlainObject, isString } from 'lodash/fp'; -import { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; export const parseFilterQuery = (filterQuery: string): JsonObject => { try { diff --git a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts index 35eb0dfca7a6bc..073112f94e049b 100644 --- a/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts +++ b/x-pack/plugins/task_manager/server/monitoring/capacity_estimation.ts @@ -7,7 +7,7 @@ import { mapValues } from 'lodash'; import stats from 'stats-lite'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { RawMonitoringStats, RawMonitoredStat, HealthStatus } from './monitoring_stats_stream'; import { AveragedStat } from './task_run_calcultors'; import { TaskPersistenceTypes } from './task_run_statistics'; diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts index 7e13e25457ed68..fdf60fe6dda2c3 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.test.ts @@ -9,7 +9,7 @@ import { TaskManagerConfig } from '../config'; import { of, Subject } from 'rxjs'; import { take, bufferCount } from 'rxjs/operators'; import { createMonitoringStatsStream, AggregatedStat } from './monitoring_stats_stream'; -import { JsonValue } from 'src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; beforeEach(() => { jest.resetAllMocks(); diff --git a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts index 8338bf3197162e..78511f5a94ca07 100644 --- a/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts +++ b/x-pack/plugins/task_manager/server/monitoring/monitoring_stats_stream.ts @@ -9,7 +9,7 @@ import { merge, of, Observable } from 'rxjs'; import { map, scan } from 'rxjs/operators'; import { set } from '@elastic/safer-lodash-set'; import { Logger } from 'src/core/server'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { TaskStore } from '../task_store'; import { TaskPollingLifecycle } from '../polling_lifecycle'; import { diff --git a/x-pack/plugins/task_manager/server/monitoring/runtime_statistics_aggregator.ts b/x-pack/plugins/task_manager/server/monitoring/runtime_statistics_aggregator.ts index 0a6db350a88b9a..799ea054596c0a 100644 --- a/x-pack/plugins/task_manager/server/monitoring/runtime_statistics_aggregator.ts +++ b/x-pack/plugins/task_manager/server/monitoring/runtime_statistics_aggregator.ts @@ -6,7 +6,7 @@ */ import { Observable } from 'rxjs'; -import { JsonValue } from 'src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; export interface AggregatedStat { key: string; diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts index 4e2e689b71c88d..b0611437d87bec 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_calcultors.ts @@ -6,7 +6,7 @@ */ import stats from 'stats-lite'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { isUndefined, countBy, mapValues } from 'lodash'; export interface AveragedStat extends JsonObject { diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts index eb6cb0796c33cc..b792f4ca475f93 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts @@ -7,7 +7,7 @@ import { combineLatest, Observable } from 'rxjs'; import { filter, startWith, map } from 'rxjs/operators'; -import { JsonObject, JsonValue } from 'src/plugins/kibana_utils/common'; +import { JsonObject, JsonValue } from '@kbn/common-utils'; import { isNumber, mapValues } from 'lodash'; import { AggregatedStatProvider, AggregatedStat } from './runtime_statistics_aggregator'; import { TaskLifecycleEvent } from '../polling_lifecycle'; diff --git a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts index 669f6198325485..abd86be522f0cd 100644 --- a/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/workload_statistics.ts @@ -8,7 +8,7 @@ import { combineLatest, Observable, timer } from 'rxjs'; import { mergeMap, map, filter, switchMap, catchError } from 'rxjs/operators'; import { Logger } from 'src/core/server'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { keyBy, mapValues } from 'lodash'; import { estypes } from '@elastic/elasticsearch'; import { AggregatedStatProvider } from './runtime_statistics_aggregator'; diff --git a/x-pack/plugins/task_manager/server/routes/health.ts b/x-pack/plugins/task_manager/server/routes/health.ts index cc2f6c6630e56d..0f43575d844816 100644 --- a/x-pack/plugins/task_manager/server/routes/health.ts +++ b/x-pack/plugins/task_manager/server/routes/health.ts @@ -16,7 +16,7 @@ import { Observable, Subject } from 'rxjs'; import { tap, map } from 'rxjs/operators'; import { throttleTime } from 'rxjs/operators'; import { isString } from 'lodash'; -import { JsonValue } from 'src/plugins/kibana_utils/common'; +import { JsonValue } from '@kbn/common-utils'; import { Logger, ServiceStatus, ServiceStatusLevels } from '../../../../../src/core/server'; import { MonitoringStats, diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index 6a69921a36671e..c5a6ef877c47a4 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -8,10 +8,10 @@ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; import Mustache from 'mustache'; +import { JsonObject } from '@kbn/common-utils'; import { ActionGroupIdsOf } from '../../../../alerting/common'; import { UptimeAlertTypeFactory } from './types'; import { esKuery } from '../../../../../../src/plugins/data/server'; -import { JsonObject } from '../../../../../../src/plugins/kibana_utils/common'; import { StatusCheckFilters, Ping, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts index c126de27158cc1..07047bd0be7bc5 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_monitor_status.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { JsonObject } from 'src/plugins/kibana_utils/public'; +import { JsonObject } from '@kbn/common-utils'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; import { asMutableArray } from '../../../common/utils/as_mutable_array'; import { UMElasticsearchQueryFn } from '../adapters'; diff --git a/x-pack/test/apm_api_integration/tests/services/annotations.ts b/x-pack/test/apm_api_integration/tests/services/annotations.ts index 9a634c9bf82470..34eadbe3c609ce 100644 --- a/x-pack/test/apm_api_integration/tests/services/annotations.ts +++ b/x-pack/test/apm_api_integration/tests/services/annotations.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { merge, cloneDeep, isPlainObject } from 'lodash'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { registry } from '../../common/registry'; diff --git a/x-pack/test/observability_api_integration/basic/tests/annotations.ts b/x-pack/test/observability_api_integration/basic/tests/annotations.ts index 05bfba42dd59ca..4a2c7b68f612e9 100644 --- a/x-pack/test/observability_api_integration/basic/tests/annotations.ts +++ b/x-pack/test/observability_api_integration/basic/tests/annotations.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export diff --git a/x-pack/test/observability_api_integration/trial/tests/annotations.ts b/x-pack/test/observability_api_integration/trial/tests/annotations.ts index 1ea3460060bc9f..b1ef717ddfd88b 100644 --- a/x-pack/test/observability_api_integration/trial/tests/annotations.ts +++ b/x-pack/test/observability_api_integration/trial/tests/annotations.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { Annotation } from '../../../../plugins/observability/common/annotations'; import { FtrProviderContext } from '../../common/ftr_provider_context'; diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts index 073bc44e89e61f..b3aeb55eb38a12 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/events.ts @@ -6,7 +6,7 @@ */ import expect from '@kbn/expect'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; +import { JsonObject } from '@kbn/common-utils'; import { eventsIndexPattern } from '../../../../plugins/security_solution/common/endpoint/constants'; import { eventIDSafeVersion, diff --git a/yarn.lock b/yarn.lock index a9a81585000b5e..4316e5f638c379 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2620,6 +2620,10 @@ version "0.0.0" uid "" +"@kbn/common-utils@link:bazel-bin/packages/kbn-common-utils": + version "0.0.0" + uid "" + "@kbn/config-schema@link:bazel-bin/packages/kbn-config-schema": version "0.0.0" uid "" From c5e74d8241d645ccaf266bcb7d9c909a9c95b6df Mon Sep 17 00:00:00 2001 From: Madison Caldwell Date: Wed, 16 Jun 2021 11:35:07 -0400 Subject: [PATCH 29/32] [RAC][Security Solution] Pull Gap Remediation out of search_after_bulk_create (#102104) * Modify threshold rules to receive a single date range tuple * Modify threat match rules to receive a single date range tuple * Modify custom query rules to receive a single date range tuple * Fix up tests (partially) * Change log message to indicate single tuple instead of array * Bad test? * Prevent max_signals from being exceeded on threat match rule executions * Revert "Prevent max_signals from being exceeded on threat match rule executions" This reverts commit ba3b2f7a382ef7c369f02c7939e1495f72d92bfe. * Modify EQL rules to use date range tuple * Modify ML rules to use date range tuple * Fix ML/EQL tests * Use dateMath to parse moments in ML/Threshold tests * Add mocks for threshold test * Use dateMath for eql tests --- .../signals/executors/eql.test.ts | 10 +- .../detection_engine/signals/executors/eql.ts | 7 +- .../signals/executors/ml.test.ts | 12 +- .../detection_engine/signals/executors/ml.ts | 8 +- .../signals/executors/query.ts | 6 +- .../signals/executors/threat_match.ts | 6 +- .../signals/executors/threshold.test.ts | 25 +- .../signals/executors/threshold.ts | 160 ++++++------ .../signals/search_after_bulk_create.test.ts | 37 ++- .../signals/search_after_bulk_create.ts | 245 +++++++++--------- .../signals/signal_rule_alert_type.ts | 132 +++++----- .../threat_mapping/create_threat_signal.ts | 4 +- .../threat_mapping/create_threat_signals.ts | 4 +- .../signals/threat_mapping/types.ts | 4 +- .../lib/detection_engine/signals/types.ts | 4 +- 15 files changed, 352 insertions(+), 312 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts index 947e7d573173ea..e7af3d484dfbd2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import dateMath from '@elastic/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server/mocks'; import { eqlExecutor } from './eql'; @@ -23,6 +24,7 @@ describe('eql_executor', () => { let logger: ReturnType; let alertServices: AlertServicesMock; (getIndexVersion as jest.Mock).mockReturnValue(SIGNALS_TEMPLATE_VERSION); + const params = getEqlRuleParams(); const eqlSO = { id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', type: 'alert', @@ -40,10 +42,15 @@ describe('eql_executor', () => { interval: '5m', }, throttle: 'no_actions', - params: getEqlRuleParams(), + params, }, references: [], }; + const tuple = { + from: dateMath.parse(params.from)!, + to: dateMath.parse(params.to)!, + maxSignals: params.maxSignals, + }; const searchAfterSize = 7; beforeEach(() => { @@ -64,6 +71,7 @@ describe('eql_executor', () => { const exceptionItems = [getExceptionListItemSchemaMock({ entries: [getEntryListMock()] })]; const response = await eqlExecutor({ rule: eqlSO, + tuple, exceptionItems, services: alertServices, version, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts index 28d1f3e19baeed..a187b730696829 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts @@ -28,6 +28,7 @@ import { AlertAttributes, BulkCreate, EqlSignalSearchResponse, + RuleRangeTuple, SearchAfterAndBulkCreateReturnType, WrappedSignalHit, } from '../types'; @@ -35,6 +36,7 @@ import { createSearchAfterReturnType, makeFloatString, wrapSignal } from '../uti export const eqlExecutor = async ({ rule, + tuple, exceptionItems, services, version, @@ -43,6 +45,7 @@ export const eqlExecutor = async ({ bulkCreate, }: { rule: SavedObject>; + tuple: RuleRangeTuple; exceptionItems: ExceptionListItemSchema[]; services: AlertServices; version: string; @@ -81,8 +84,8 @@ export const eqlExecutor = async ({ const request = buildEqlSearchRequest( ruleParams.query, inputIndex, - ruleParams.from, - ruleParams.to, + tuple.from.toISOString(), + tuple.to.toISOString(), searchAfterSize, ruleParams.timestampOverride, exceptionItems, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts index 25a9d2c3f510fe..89c1392cb67ba7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import dateMath from '@elastic/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server/mocks'; import { mlExecutor } from './ml'; @@ -26,7 +27,13 @@ describe('ml_executor', () => { const exceptionItems = [getExceptionListItemSchemaMock()]; let logger: ReturnType; let alertServices: AlertServicesMock; - const mlSO = sampleRuleSO(getMlRuleParams()); + const params = getMlRuleParams(); + const mlSO = sampleRuleSO(params); + const tuple = { + from: dateMath.parse(params.from)!, + to: dateMath.parse(params.to)!, + maxSignals: params.maxSignals, + }; const buildRuleMessage = buildRuleMessageFactory({ id: mlSO.id, ruleId: mlSO.attributes.params.ruleId, @@ -60,6 +67,7 @@ describe('ml_executor', () => { await expect( mlExecutor({ rule: mlSO, + tuple, ml: undefined, exceptionItems, services: alertServices, @@ -76,6 +84,7 @@ describe('ml_executor', () => { jobsSummaryMock.mockResolvedValue([]); const response = await mlExecutor({ rule: mlSO, + tuple, ml: mlMock, exceptionItems, services: alertServices, @@ -101,6 +110,7 @@ describe('ml_executor', () => { const response = await mlExecutor({ rule: mlSO, + tuple, ml: mlMock, exceptionItems, services: alertServices, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts index f5c7d8822b51f0..20c4cb16dadc8d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts @@ -21,11 +21,12 @@ import { bulkCreateMlSignals } from '../bulk_create_ml_signals'; import { filterEventsAgainstList } from '../filters/filter_events_against_list'; import { findMlSignals } from '../find_ml_signals'; import { BuildRuleMessage } from '../rule_messages'; -import { AlertAttributes, BulkCreate, WrapHits } from '../types'; +import { AlertAttributes, BulkCreate, RuleRangeTuple, WrapHits } from '../types'; import { createErrorsFromShard, createSearchAfterReturnType, mergeReturns } from '../utils'; export const mlExecutor = async ({ rule, + tuple, ml, listClient, exceptionItems, @@ -36,6 +37,7 @@ export const mlExecutor = async ({ wrapHits, }: { rule: SavedObject>; + tuple: RuleRangeTuple; ml: SetupPlugins['ml']; listClient: ListClient; exceptionItems: ExceptionListItemSchema[]; @@ -88,8 +90,8 @@ export const mlExecutor = async ({ savedObjectsClient: services.savedObjectsClient, jobIds: ruleParams.machineLearningJobId, anomalyThreshold: ruleParams.anomalyThreshold, - from: ruleParams.from, - to: ruleParams.to, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), exceptionItems, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts index 9d76a06afa2755..385c01c2f1cda1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts @@ -24,7 +24,7 @@ import { QueryRuleParams, SavedQueryRuleParams } from '../../schemas/rule_schema export const queryExecutor = async ({ rule, - tuples, + tuple, listClient, exceptionItems, services, @@ -37,7 +37,7 @@ export const queryExecutor = async ({ wrapHits, }: { rule: SavedObject>; - tuples: RuleRangeTuple[]; + tuple: RuleRangeTuple; listClient: ListClient; exceptionItems: ExceptionListItemSchema[]; services: AlertServices; @@ -63,7 +63,7 @@ export const queryExecutor = async ({ }); return searchAfterAndBulkCreate({ - tuples, + tuple, listClient, exceptionsList: exceptionItems, ruleSO: rule, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts index 078eb8362069cf..d0e22f696b222e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts @@ -23,7 +23,7 @@ import { ThreatRuleParams } from '../../schemas/rule_schemas'; export const threatMatchExecutor = async ({ rule, - tuples, + tuple, listClient, exceptionItems, services, @@ -36,7 +36,7 @@ export const threatMatchExecutor = async ({ wrapHits, }: { rule: SavedObject>; - tuples: RuleRangeTuple[]; + tuple: RuleRangeTuple; listClient: ListClient; exceptionItems: ExceptionListItemSchema[]; services: AlertServices; @@ -51,7 +51,7 @@ export const threatMatchExecutor = async ({ const ruleParams = rule.attributes.params; const inputIndex = await getInputIndex(services, version, ruleParams.index); return createThreatSignals({ - tuples, + tuple, threatMapping: ruleParams.threatMapping, query: ruleParams.query, inputIndex, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index f03e8b8a147aea..3906c669222386 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -5,18 +5,23 @@ * 2.0. */ +import dateMath from '@elastic/datemath'; import { loggingSystemMock } from 'src/core/server/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server/mocks'; import { thresholdExecutor } from './threshold'; import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; import { getEntryListMock } from '../../../../../../lists/common/schemas/types/entry_list.mock'; import { getThresholdRuleParams } from '../../schemas/rule_schemas.mock'; import { buildRuleMessageFactory } from '../rule_messages'; +import { sampleEmptyDocSearchResults } from '../__mocks__/es_results'; describe('threshold_executor', () => { const version = '8.0.0'; let logger: ReturnType; let alertServices: AlertServicesMock; + const params = getThresholdRuleParams(); const thresholdSO = { id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', type: 'alert', @@ -34,10 +39,15 @@ describe('threshold_executor', () => { interval: '5m', }, throttle: 'no_actions', - params: getThresholdRuleParams(), + params, }, references: [], }; + const tuple = { + from: dateMath.parse(params.from)!, + to: dateMath.parse(params.to)!, + maxSignals: params.maxSignals, + }; const buildRuleMessage = buildRuleMessageFactory({ id: thresholdSO.id, ruleId: thresholdSO.attributes.params.ruleId, @@ -47,6 +57,9 @@ describe('threshold_executor', () => { beforeEach(() => { alertServices = alertsMock.createAlertServices(); + alertServices.scopedClusterClient.asCurrentUser.search.mockResolvedValue( + elasticsearchClientMock.createSuccessTransportRequestPromise(sampleEmptyDocSearchResults()) + ); logger = loggingSystemMock.createLogger(); }); @@ -55,14 +68,20 @@ describe('threshold_executor', () => { const exceptionItems = [getExceptionListItemSchemaMock({ entries: [getEntryListMock()] })]; const response = await thresholdExecutor({ rule: thresholdSO, - tuples: [], + tuple, exceptionItems, services: alertServices, version, logger, buildRuleMessage, startedAt: new Date(), - bulkCreate: jest.fn(), + bulkCreate: jest.fn().mockImplementation((hits) => ({ + errors: [], + success: true, + bulkCreateDuration: '0', + createdItemsCount: 0, + createdItems: [], + })), wrapHits: jest.fn(), }); expect(response.warningMessages.length).toEqual(1); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts index 5e23128c9c148a..378d68fc13d2a9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts @@ -39,7 +39,7 @@ import { BuildRuleMessage } from '../rule_messages'; export const thresholdExecutor = async ({ rule, - tuples, + tuple, exceptionItems, services, version, @@ -50,7 +50,7 @@ export const thresholdExecutor = async ({ wrapHits, }: { rule: SavedObject>; - tuples: RuleRangeTuple[]; + tuple: RuleRangeTuple; exceptionItems: ExceptionListItemSchema[]; services: AlertServices; version: string; @@ -70,90 +70,88 @@ export const thresholdExecutor = async ({ } const inputIndex = await getInputIndex(services, version, ruleParams.index); - for (const tuple of tuples) { - const { - thresholdSignalHistory, - searchErrors: previousSearchErrors, - } = await getThresholdSignalHistory({ - indexPattern: [ruleParams.outputIndex], - from: tuple.from.toISOString(), - to: tuple.to.toISOString(), - services, - logger, - ruleId: ruleParams.ruleId, - bucketByFields: ruleParams.threshold.field, - timestampOverride: ruleParams.timestampOverride, - buildRuleMessage, - }); + const { + thresholdSignalHistory, + searchErrors: previousSearchErrors, + } = await getThresholdSignalHistory({ + indexPattern: [ruleParams.outputIndex], + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + services, + logger, + ruleId: ruleParams.ruleId, + bucketByFields: ruleParams.threshold.field, + timestampOverride: ruleParams.timestampOverride, + buildRuleMessage, + }); - const bucketFilters = await getThresholdBucketFilters({ - thresholdSignalHistory, - timestampOverride: ruleParams.timestampOverride, - }); + const bucketFilters = await getThresholdBucketFilters({ + thresholdSignalHistory, + timestampOverride: ruleParams.timestampOverride, + }); + + const esFilter = await getFilter({ + type: ruleParams.type, + filters: ruleParams.filters ? ruleParams.filters.concat(bucketFilters) : bucketFilters, + language: ruleParams.language, + query: ruleParams.query, + savedId: ruleParams.savedId, + services, + index: inputIndex, + lists: exceptionItems, + }); + + const { + searchResult: thresholdResults, + searchErrors, + searchDuration: thresholdSearchDuration, + } = await findThresholdSignals({ + inputIndexPattern: inputIndex, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + services, + logger, + filter: esFilter, + threshold: ruleParams.threshold, + timestampOverride: ruleParams.timestampOverride, + buildRuleMessage, + }); - const esFilter = await getFilter({ - type: ruleParams.type, - filters: ruleParams.filters ? ruleParams.filters.concat(bucketFilters) : bucketFilters, - language: ruleParams.language, - query: ruleParams.query, - savedId: ruleParams.savedId, - services, - index: inputIndex, - lists: exceptionItems, - }); + const { + success, + bulkCreateDuration, + createdItemsCount, + createdItems, + errors, + } = await bulkCreateThresholdSignals({ + someResult: thresholdResults, + ruleSO: rule, + filter: esFilter, + services, + logger, + inputIndexPattern: inputIndex, + signalsIndex: ruleParams.outputIndex, + startedAt, + from: tuple.from.toDate(), + thresholdSignalHistory, + bulkCreate, + wrapHits, + }); - const { + result = mergeReturns([ + result, + createSearchAfterReturnTypeFromResponse({ searchResult: thresholdResults, - searchErrors, - searchDuration: thresholdSearchDuration, - } = await findThresholdSignals({ - inputIndexPattern: inputIndex, - from: tuple.from.toISOString(), - to: tuple.to.toISOString(), - services, - logger, - filter: esFilter, - threshold: ruleParams.threshold, timestampOverride: ruleParams.timestampOverride, - buildRuleMessage, - }); - - const { + }), + createSearchAfterReturnType({ success, - bulkCreateDuration, - createdItemsCount, - createdItems, - errors, - } = await bulkCreateThresholdSignals({ - someResult: thresholdResults, - ruleSO: rule, - filter: esFilter, - services, - logger, - inputIndexPattern: inputIndex, - signalsIndex: ruleParams.outputIndex, - startedAt, - from: tuple.from.toDate(), - thresholdSignalHistory, - bulkCreate, - wrapHits, - }); - - result = mergeReturns([ - result, - createSearchAfterReturnTypeFromResponse({ - searchResult: thresholdResults, - timestampOverride: ruleParams.timestampOverride, - }), - createSearchAfterReturnType({ - success, - errors: [...errors, ...previousSearchErrors, ...searchErrors], - createdSignalsCount: createdItemsCount, - createdSignals: createdItems, - bulkCreateTimes: bulkCreateDuration ? [bulkCreateDuration] : [], - searchAfterTimes: [thresholdSearchDuration], - }), - ]); - } + errors: [...errors, ...previousSearchErrors, ...searchErrors], + createdSignalsCount: createdItemsCount, + createdSignals: createdItems, + bulkCreateTimes: bulkCreateDuration ? [bulkCreateDuration] : [], + searchAfterTimes: [thresholdSearchDuration], + }), + ]); return result; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index e4eb7e854f670f..184b49c2d6c7b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -44,14 +44,14 @@ describe('searchAfterAndBulkCreate', () => { const sampleParams = getQueryRuleParams(); const ruleSO = sampleRuleSO(getQueryRuleParams()); sampleParams.maxSignals = 30; - let tuples: RuleRangeTuple[]; + let tuple: RuleRangeTuple; beforeEach(() => { jest.clearAllMocks(); listClient = listMock.getListClient(); listClient.searchListItemByValues = jest.fn().mockResolvedValue([]); inputIndexPattern = ['auditbeat-*']; mockService = alertsMock.createAlertServices(); - ({ tuples } = getRuleRangeTuples({ + tuple = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: new Date(), from: sampleParams.from, @@ -59,7 +59,7 @@ describe('searchAfterAndBulkCreate', () => { interval: '5m', maxSignals: sampleParams.maxSignals, buildRuleMessage, - })); + }).tuples[0]; bulkCreate = bulkCreateFactory( mockLogger, mockService.scopedClusterClient.asCurrentUser, @@ -174,7 +174,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - tuples, + tuple, ruleSO, listClient, exceptionsList: [exceptionItem], @@ -279,7 +279,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -357,7 +357,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -416,7 +416,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -495,7 +495,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [], services: mockService, @@ -550,7 +550,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -569,11 +569,6 @@ describe('searchAfterAndBulkCreate', () => { expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(1); expect(createdSignalsCount).toEqual(0); // should not create any signals because all events were in the allowlist expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); - // I don't like testing log statements since logs change but this is the best - // way I can think of to ensure this section is getting hit with this test case. - expect(((mockLogger.debug as unknown) as jest.Mock).mock.calls[7][0]).toContain( - 'ran out of sort ids to sort on name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' - ); }); test('should return success when no sortId present but search results are in the allowlist', async () => { @@ -627,7 +622,7 @@ describe('searchAfterAndBulkCreate', () => { ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -701,7 +696,7 @@ describe('searchAfterAndBulkCreate', () => { ); const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [], services: mockService, @@ -746,7 +741,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ listClient, exceptionsList: [exceptionItem], - tuples, + tuple, ruleSO, services: mockService, logger: mockLogger, @@ -793,7 +788,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ listClient, exceptionsList: [exceptionItem], - tuples, + tuple, ruleSO, services: mockService, logger: mockLogger, @@ -854,7 +849,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ listClient, exceptionsList: [exceptionItem], - tuples, + tuple, ruleSO, services: mockService, logger: mockLogger, @@ -979,7 +974,7 @@ describe('searchAfterAndBulkCreate', () => { errors, } = await searchAfterAndBulkCreate({ ruleSO, - tuples, + tuple, listClient, exceptionsList: [], services: mockService, @@ -1075,7 +1070,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ enrichment: mockEnrichment, ruleSO, - tuples, + tuple, listClient, exceptionsList: [], services: mockService, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index bb2e57b0606e59..eb4af0c38ce254 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -23,7 +23,7 @@ import { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } fr // search_after through documents and re-index using bulk endpoint. export const searchAfterAndBulkCreate = async ({ - tuples: totalToFromTuples, + tuple, ruleSO, exceptionsList, services, @@ -49,150 +49,143 @@ export const searchAfterAndBulkCreate = async ({ // to ensure we don't exceed maxSignals let signalsCreatedCount = 0; - const tuplesToBeLogged = [...totalToFromTuples]; - logger.debug(buildRuleMessage(`totalToFromTuples: ${totalToFromTuples.length}`)); - - while (totalToFromTuples.length > 0) { - const tuple = totalToFromTuples.pop(); - if (tuple == null || tuple.to == null || tuple.from == null) { - logger.error(buildRuleMessage(`[-] malformed date tuple`)); - return createSearchAfterReturnType({ - success: false, - errors: ['malformed date tuple'], - }); - } - signalsCreatedCount = 0; - while (signalsCreatedCount < tuple.maxSignals) { - try { - let mergedSearchResults = createSearchResultReturnType(); - logger.debug(buildRuleMessage(`sortIds: ${sortIds}`)); + if (tuple == null || tuple.to == null || tuple.from == null) { + logger.error(buildRuleMessage(`[-] malformed date tuple`)); + return createSearchAfterReturnType({ + success: false, + errors: ['malformed date tuple'], + }); + } + signalsCreatedCount = 0; + while (signalsCreatedCount < tuple.maxSignals) { + try { + let mergedSearchResults = createSearchResultReturnType(); + logger.debug(buildRuleMessage(`sortIds: ${sortIds}`)); - if (hasSortId) { - const { searchResult, searchDuration, searchErrors } = await singleSearchAfter({ - buildRuleMessage, - searchAfterSortIds: sortIds, - index: inputIndexPattern, - from: tuple.from.toISOString(), - to: tuple.to.toISOString(), - services, - logger, - // @ts-expect-error please, declare a type explicitly instead of unknown - filter, - pageSize: Math.ceil(Math.min(tuple.maxSignals, pageSize)), + if (hasSortId) { + const { searchResult, searchDuration, searchErrors } = await singleSearchAfter({ + buildRuleMessage, + searchAfterSortIds: sortIds, + index: inputIndexPattern, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + services, + logger, + // @ts-expect-error please, declare a type explicitly instead of unknown + filter, + pageSize: Math.ceil(Math.min(tuple.maxSignals, pageSize)), + timestampOverride: ruleParams.timestampOverride, + }); + mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResult]); + toReturn = mergeReturns([ + toReturn, + createSearchAfterReturnTypeFromResponse({ + searchResult: mergedSearchResults, timestampOverride: ruleParams.timestampOverride, - }); - mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResult]); - toReturn = mergeReturns([ - toReturn, - createSearchAfterReturnTypeFromResponse({ - searchResult: mergedSearchResults, - timestampOverride: ruleParams.timestampOverride, - }), - createSearchAfterReturnType({ - searchAfterTimes: [searchDuration], - errors: searchErrors, - }), - ]); + }), + createSearchAfterReturnType({ + searchAfterTimes: [searchDuration], + errors: searchErrors, + }), + ]); - const lastSortIds = getSafeSortIds( - searchResult.hits.hits[searchResult.hits.hits.length - 1]?.sort - ); - if (lastSortIds != null && lastSortIds.length !== 0) { - sortIds = lastSortIds; - hasSortId = true; - } else { - hasSortId = false; - } + const lastSortIds = getSafeSortIds( + searchResult.hits.hits[searchResult.hits.hits.length - 1]?.sort + ); + if (lastSortIds != null && lastSortIds.length !== 0) { + sortIds = lastSortIds; + hasSortId = true; + } else { + hasSortId = false; } + } + + // determine if there are any candidate signals to be processed + const totalHits = createTotalHitsFromSearchResult({ searchResult: mergedSearchResults }); + logger.debug(buildRuleMessage(`totalHits: ${totalHits}`)); + logger.debug( + buildRuleMessage(`searchResult.hit.hits.length: ${mergedSearchResults.hits.hits.length}`) + ); - // determine if there are any candidate signals to be processed - const totalHits = createTotalHitsFromSearchResult({ searchResult: mergedSearchResults }); - logger.debug(buildRuleMessage(`totalHits: ${totalHits}`)); + if (totalHits === 0 || mergedSearchResults.hits.hits.length === 0) { logger.debug( - buildRuleMessage(`searchResult.hit.hits.length: ${mergedSearchResults.hits.hits.length}`) + buildRuleMessage( + `${ + totalHits === 0 ? 'totalHits' : 'searchResult.hits.hits.length' + } was 0, exiting early` + ) ); + break; + } - if (totalHits === 0 || mergedSearchResults.hits.hits.length === 0) { - logger.debug( - buildRuleMessage( - `${ - totalHits === 0 ? 'totalHits' : 'searchResult.hits.hits.length' - } was 0, exiting and moving on to next tuple` - ) - ); - break; - } - - // filter out the search results that match with the values found in the list. - // the resulting set are signals to be indexed, given they are not duplicates - // of signals already present in the signals index. - const filteredEvents = await filterEventsAgainstList({ - listClient, - exceptionsList, - logger, - eventSearchResult: mergedSearchResults, - buildRuleMessage, - }); - - // only bulk create if there are filteredEvents leftover - // if there isn't anything after going through the value list filter - // skip the call to bulk create and proceed to the next search_after, - // if there is a sort id to continue the search_after with. - if (filteredEvents.hits.hits.length !== 0) { - // make sure we are not going to create more signals than maxSignals allows - if (signalsCreatedCount + filteredEvents.hits.hits.length > tuple.maxSignals) { - filteredEvents.hits.hits = filteredEvents.hits.hits.slice( - 0, - tuple.maxSignals - signalsCreatedCount - ); - } - const enrichedEvents = await enrichment(filteredEvents); - const wrappedDocs = wrapHits(enrichedEvents.hits.hits); + // filter out the search results that match with the values found in the list. + // the resulting set are signals to be indexed, given they are not duplicates + // of signals already present in the signals index. + const filteredEvents = await filterEventsAgainstList({ + listClient, + exceptionsList, + logger, + eventSearchResult: mergedSearchResults, + buildRuleMessage, + }); - const { - bulkCreateDuration: bulkDuration, - createdItemsCount: createdCount, - createdItems, - success: bulkSuccess, - errors: bulkErrors, - } = await bulkCreate(wrappedDocs); - toReturn = mergeReturns([ - toReturn, - createSearchAfterReturnType({ - success: bulkSuccess, - createdSignalsCount: createdCount, - createdSignals: createdItems, - bulkCreateTimes: bulkDuration ? [bulkDuration] : undefined, - errors: bulkErrors, - }), - ]); - signalsCreatedCount += createdCount; - logger.debug(buildRuleMessage(`created ${createdCount} signals`)); - logger.debug(buildRuleMessage(`signalsCreatedCount: ${signalsCreatedCount}`)); - logger.debug( - buildRuleMessage(`enrichedEvents.hits.hits: ${enrichedEvents.hits.hits.length}`) + // only bulk create if there are filteredEvents leftover + // if there isn't anything after going through the value list filter + // skip the call to bulk create and proceed to the next search_after, + // if there is a sort id to continue the search_after with. + if (filteredEvents.hits.hits.length !== 0) { + // make sure we are not going to create more signals than maxSignals allows + if (signalsCreatedCount + filteredEvents.hits.hits.length > tuple.maxSignals) { + filteredEvents.hits.hits = filteredEvents.hits.hits.slice( + 0, + tuple.maxSignals - signalsCreatedCount ); - - sendAlertTelemetryEvents(logger, eventsTelemetry, enrichedEvents, buildRuleMessage); } + const enrichedEvents = await enrichment(filteredEvents); + const wrappedDocs = wrapHits(enrichedEvents.hits.hits); - if (!hasSortId) { - logger.debug(buildRuleMessage('ran out of sort ids to sort on')); - break; - } - } catch (exc: unknown) { - logger.error(buildRuleMessage(`[-] search_after and bulk threw an error ${exc}`)); - return mergeReturns([ + const { + bulkCreateDuration: bulkDuration, + createdItemsCount: createdCount, + createdItems, + success: bulkSuccess, + errors: bulkErrors, + } = await bulkCreate(wrappedDocs); + toReturn = mergeReturns([ toReturn, createSearchAfterReturnType({ - success: false, - errors: [`${exc}`], + success: bulkSuccess, + createdSignalsCount: createdCount, + createdSignals: createdItems, + bulkCreateTimes: bulkDuration ? [bulkDuration] : undefined, + errors: bulkErrors, }), ]); + signalsCreatedCount += createdCount; + logger.debug(buildRuleMessage(`created ${createdCount} signals`)); + logger.debug(buildRuleMessage(`signalsCreatedCount: ${signalsCreatedCount}`)); + logger.debug( + buildRuleMessage(`enrichedEvents.hits.hits: ${enrichedEvents.hits.hits.length}`) + ); + + sendAlertTelemetryEvents(logger, eventsTelemetry, enrichedEvents, buildRuleMessage); + } + + if (!hasSortId) { + logger.debug(buildRuleMessage('ran out of sort ids to sort on')); + break; } + } catch (exc: unknown) { + logger.error(buildRuleMessage(`[-] search_after and bulk threw an error ${exc}`)); + return mergeReturns([ + toReturn, + createSearchAfterReturnType({ + success: false, + errors: [`${exc}`], + }), + ]); } } logger.debug(buildRuleMessage(`[+] completed bulk index of ${toReturn.createdSignalsCount}`)); - toReturn.totalToFromTuples = tuplesToBeLogged; return toReturn; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 0a2e22bc44b60e..bb1e50c14d4014 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -235,74 +235,86 @@ export const signalRulesAlertType = ({ if (isMlRule(type)) { const mlRuleSO = asTypeSpecificSO(savedObject, machineLearningRuleParams); - result = await mlExecutor({ - rule: mlRuleSO, - ml, - listClient, - exceptionItems, - services, - logger, - buildRuleMessage, - bulkCreate, - wrapHits, - }); + for (const tuple of tuples) { + result = await mlExecutor({ + rule: mlRuleSO, + tuple, + ml, + listClient, + exceptionItems, + services, + logger, + buildRuleMessage, + bulkCreate, + wrapHits, + }); + } } else if (isThresholdRule(type)) { const thresholdRuleSO = asTypeSpecificSO(savedObject, thresholdRuleParams); - result = await thresholdExecutor({ - rule: thresholdRuleSO, - tuples, - exceptionItems, - services, - version, - logger, - buildRuleMessage, - startedAt, - bulkCreate, - wrapHits, - }); + for (const tuple of tuples) { + result = await thresholdExecutor({ + rule: thresholdRuleSO, + tuple, + exceptionItems, + services, + version, + logger, + buildRuleMessage, + startedAt, + bulkCreate, + wrapHits, + }); + } } else if (isThreatMatchRule(type)) { const threatRuleSO = asTypeSpecificSO(savedObject, threatRuleParams); - result = await threatMatchExecutor({ - rule: threatRuleSO, - tuples, - listClient, - exceptionItems, - services, - version, - searchAfterSize, - logger, - eventsTelemetry, - buildRuleMessage, - bulkCreate, - wrapHits, - }); + for (const tuple of tuples) { + result = await threatMatchExecutor({ + rule: threatRuleSO, + tuple, + listClient, + exceptionItems, + services, + version, + searchAfterSize, + logger, + eventsTelemetry, + buildRuleMessage, + bulkCreate, + wrapHits, + }); + } } else if (isQueryRule(type)) { const queryRuleSO = validateQueryRuleTypes(savedObject); - result = await queryExecutor({ - rule: queryRuleSO, - tuples, - listClient, - exceptionItems, - services, - version, - searchAfterSize, - logger, - eventsTelemetry, - buildRuleMessage, - bulkCreate, - wrapHits, - }); + for (const tuple of tuples) { + result = await queryExecutor({ + rule: queryRuleSO, + tuple, + listClient, + exceptionItems, + services, + version, + searchAfterSize, + logger, + eventsTelemetry, + buildRuleMessage, + bulkCreate, + wrapHits, + }); + } } else if (isEqlRule(type)) { const eqlRuleSO = asTypeSpecificSO(savedObject, eqlRuleParams); - result = await eqlExecutor({ - rule: eqlRuleSO, - exceptionItems, - services, - version, - searchAfterSize, - bulkCreate, - logger, - }); + for (const tuple of tuples) { + result = await eqlExecutor({ + rule: eqlRuleSO, + tuple, + exceptionItems, + services, + version, + searchAfterSize, + bulkCreate, + logger, + }); + } } else { throw new Error(`unknown rule type ${type}`); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts index 3e30a08f1ae69c..806f5e47608e40 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts @@ -13,7 +13,7 @@ import { CreateThreatSignalOptions } from './types'; import { SearchAfterAndBulkCreateReturnType } from '../types'; export const createThreatSignal = async ({ - tuples, + tuple, threatMapping, threatEnrichment, query, @@ -70,7 +70,7 @@ export const createThreatSignal = async ({ ); const result = await searchAfterAndBulkCreate({ - tuples, + tuple, listClient, exceptionsList: exceptionItems, ruleSO, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index 5054ab1b2cca50..169a820392a6e6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -15,7 +15,7 @@ import { combineConcurrentResults } from './utils'; import { buildThreatEnrichment } from './build_threat_enrichment'; export const createThreatSignals = async ({ - tuples, + tuple, threatMapping, query, inputIndex, @@ -104,7 +104,7 @@ export const createThreatSignals = async ({ const concurrentSearchesPerformed = chunks.map>( (slicedChunk) => createThreatSignal({ - tuples, + tuple, threatEnrichment, threatMapping, query, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts index 34b064b0f88053..ded79fc647ac41 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts @@ -40,7 +40,7 @@ import { ThreatRuleParams } from '../../schemas/rule_schemas'; export type SortOrderOrUndefined = 'asc' | 'desc' | undefined; export interface CreateThreatSignalsOptions { - tuples: RuleRangeTuple[]; + tuple: RuleRangeTuple; threatMapping: ThreatMapping; query: string; inputIndex: string[]; @@ -70,7 +70,7 @@ export interface CreateThreatSignalsOptions { } export interface CreateThreatSignalOptions { - tuples: RuleRangeTuple[]; + tuple: RuleRangeTuple; threatMapping: ThreatMapping; threatEnrichment: SignalsEnrichment; query: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index c35eb04ba12707..8a6ce91b2575ab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -262,11 +262,11 @@ export type WrapHits = ( ) => Array>; export interface SearchAfterAndBulkCreateParams { - tuples: Array<{ + tuple: { to: moment.Moment; from: moment.Moment; maxSignals: number; - }>; + }; ruleSO: SavedObject; services: AlertServices; listClient: ListClient; From 973d0578461a8823eb529b38ee7a5edc427b93a0 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 16 Jun 2021 18:01:53 +0200 Subject: [PATCH 30/32] Upgrade normalize-url from v4.5.0 to v4.5.1 (#102291) --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4316e5f638c379..353527731cb04e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20304,9 +20304,9 @@ normalize-url@^3.0.0: integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== now-and-later@^2.0.0: version "2.0.0" From d2e81ee785df812135faf775e14f7aaaf58eb901 Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Wed, 16 Jun 2021 12:07:50 -0400 Subject: [PATCH 31/32] CIT for circle processor (renewed PR) (#102277) * Added CITs for Circle processor. * Fixed issue with form function using int instead of string. * Added changed per nits. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__jest__/processors/circle.test.tsx | 114 ++++++++++++++++++ .../__jest__/processors/processor.helpers.tsx | 2 + .../processor_form/processors/circle.tsx | 2 + 3 files changed, 118 insertions(+) create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx new file mode 100644 index 00000000000000..e29bb2ac6e92ec --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/circle.test.tsx @@ -0,0 +1,114 @@ +/* + * 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 { act } from 'react-dom/test-utils'; +import { setup, SetupResult, getProcessorValue } from './processor.helpers'; + +const CIRCLE_TYPE = 'circle'; + +describe('Processor: Circle', () => { + let onUpdate: jest.Mock; + let testBed: SetupResult; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(async () => { + onUpdate = jest.fn(); + + await act(async () => { + testBed = await setup({ + value: { + processors: [], + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + }); + testBed.component.update(); + const { + actions: { addProcessor, addProcessorType }, + } = testBed; + // Open the processor flyout + addProcessor(); + + // Add type (the other fields are not visible until a type is selected) + await addProcessorType(CIRCLE_TYPE); + }); + + test('prevents form submission if required fields are not provided', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Click submit button with only the type defined + await saveNewProcessor(); + + // Expect form error as "field" and "shape_type" are required parameters + expect(form.getErrorsMessages()).toEqual([ + 'A field value is required.', + 'A shape type value is required.', + ]); + }); + + test('saves with required parameter values', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value (required) + form.setInputValue('fieldNameField.input', 'field_1'); + // Save the field + form.setSelectValue('shapeSelectorField', 'shape'); + // Set the error distance + form.setInputValue('errorDistanceField.input', '10'); + + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, CIRCLE_TYPE); + + expect(processors[0].circle).toEqual({ + field: 'field_1', + error_distance: 10, + shape_type: 'shape', + }); + }); + + test('allows optional parameters to be set', async () => { + const { + actions: { saveNewProcessor }, + form, + } = testBed; + + // Add "field" value (required) + form.setInputValue('fieldNameField.input', 'field_1'); + // Select the shape + form.setSelectValue('shapeSelectorField', 'geo_shape'); + // Add "target_field" value + form.setInputValue('targetField.input', 'target_field'); + + form.setInputValue('errorDistanceField.input', '10'); + + // Save the field with new changes + await saveNewProcessor(); + + const processors = getProcessorValue(onUpdate, CIRCLE_TYPE); + expect(processors[0].circle).toEqual({ + field: 'field_1', + error_distance: 10, + shape_type: 'geo_shape', + target_field: 'target_field', + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx index c00f09b2d2b06c..15e8c323b1308e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/processors/processor.helpers.tsx @@ -151,6 +151,8 @@ type TestSubject = | 'keepOriginalField.input' | 'removeIfSuccessfulField.input' | 'targetFieldsField.input' + | 'shapeSelectorField' + | 'errorDistanceField.input' | 'separatorValueField.input' | 'quoteValueField.input' | 'emptyValueField.input' diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx index acb480df6d35f0..74a7f37d841aee 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/processor_form/processors/circle.tsx @@ -97,6 +97,7 @@ export const Circle: FunctionComponent = () => { /> { Date: Wed, 16 Jun 2021 12:42:15 -0400 Subject: [PATCH 32/32] Fix 7.13 aggregation reference issue (#102256) --- docs/user/dashboard/aggregation-reference.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/dashboard/aggregation-reference.asciidoc b/docs/user/dashboard/aggregation-reference.asciidoc index 001114578a1cd0..cb5c484def3b9d 100644 --- a/docs/user/dashboard/aggregation-reference.asciidoc +++ b/docs/user/dashboard/aggregation-reference.asciidoc @@ -190,8 +190,8 @@ For information about {es} metrics aggregations, refer to {ref}/search-aggregati | Metrics with filters | -^| X | +^| X | | Average