From f67f1ecc6288b743b52ce8fd69055d5de3461824 Mon Sep 17 00:00:00 2001 From: Daniil Date: Mon, 21 Dec 2020 13:42:25 +0300 Subject: [PATCH 001/100] Fix request with disabled aggregation (#85696) * Fix request with disabled aggregation * Update unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../search/expressions/esaggs/request_handler.test.ts | 9 ++++++++- .../common/search/expressions/esaggs/request_handler.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts index 78d169e8529c5a..9d0c63a8dc7aa4 100644 --- a/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts +++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.test.ts @@ -150,7 +150,8 @@ describe('esaggs expression function - public', () => { }); }); - test('calls agg.postFlightRequest if it exiests', async () => { + test('calls agg.postFlightRequest if it exiests and agg is enabled', async () => { + mockParams.aggs.aggs[0].enabled = true; await handleRequest(mockParams); expect(mockParams.aggs.aggs[0].type.postFlightRequest).toHaveBeenCalledTimes(1); @@ -160,6 +161,12 @@ describe('esaggs expression function - public', () => { expect(async () => await handleRequest(mockParams)).not.toThrowError(); }); + test('should skip agg.postFlightRequest call if the agg is disabled', async () => { + mockParams.aggs.aggs[0].enabled = false; + await handleRequest(mockParams); + expect(mockParams.aggs.aggs[0].type.postFlightRequest).toHaveBeenCalledTimes(0); + }); + test('tabifies response data', async () => { await handleRequest(mockParams); expect(tabifyAggResponse).toHaveBeenCalledWith( diff --git a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts index e4385526ee6e8f..b773aad67c3f89 100644 --- a/src/plugins/data/common/search/expressions/esaggs/request_handler.ts +++ b/src/plugins/data/common/search/expressions/esaggs/request_handler.ts @@ -170,7 +170,7 @@ export const handleRequest = async ({ // response data incorrectly in the inspector. let response = (searchSource as any).rawResponse; for (const agg of aggs.aggs) { - if (typeof agg.type.postFlightRequest === 'function') { + if (agg.enabled && typeof agg.type.postFlightRequest === 'function') { response = await agg.type.postFlightRequest( response, aggs, From 74d1e39ea490e8fea45fe8c67806c2dbc2adbcc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 21 Dec 2020 12:36:20 +0100 Subject: [PATCH 002/100] [APM] Bug: Service overview: Link to Transactions list from the Overview page is broken (#86447) * fixing link and refactoring some stuff * addressing pr comments --- .../Waterfall/FlyoutTopLevelProperties.tsx | 8 +++-- .../SpanFlyout/StickySpanProperties.tsx | 8 +++-- .../service_details/service_detail_tabs.tsx | 4 +-- .../service_inventory/ServiceList/index.tsx | 4 +-- .../index.tsx | 33 ++++++++++--------- .../service_overview_errors_table/index.tsx | 9 +++-- .../index.tsx | 15 ++++----- .../service_overview/table_link_flex_item.tsx | 14 -------- ....tsx => service_transactions_overview.tsx} | 7 ++-- .../Links/apm/transaction_overview_ink.tsx | 31 +++++++++++++++++ 10 files changed, 78 insertions(+), 55 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx rename x-pack/plugins/apm/public/components/shared/Links/apm/{TransactionOverviewLink.tsx => service_transactions_overview.tsx} (86%) create mode 100644 x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx index 0187ecd927e658..b0ef28fbb7b0d2 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx @@ -13,7 +13,7 @@ import { import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../../../shared/Links/apm/TransactionDetailLink'; import { StickyProperties } from '../../../../../shared/StickyProperties'; -import { TransactionOverviewLink } from '../../../../../shared/Links/apm/TransactionOverviewLink'; +import { ServiceOrTransactionsOverviewLink } from '../../../../../shared/Links/apm/service_transactions_overview'; interface Props { transaction?: Transaction; @@ -31,9 +31,11 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { }), fieldName: SERVICE_NAME, val: ( - + {transaction.service.name} - + ), width: '25%', }, diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx index c068fee3cd6c33..ca5b4938ff42ee 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx @@ -15,7 +15,7 @@ import { import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { StickyProperties } from '../../../../../../shared/StickyProperties'; -import { TransactionOverviewLink } from '../../../../../../shared/Links/apm/TransactionOverviewLink'; +import { ServiceOrTransactionsOverviewLink } from '../../../../../../shared/Links/apm/service_transactions_overview'; import { TransactionDetailLink } from '../../../../../../shared/Links/apm/TransactionDetailLink'; interface Props { @@ -33,9 +33,11 @@ export function StickySpanProperties({ span, transaction }: Props) { }), fieldName: SERVICE_NAME, val: ( - + {transaction.service.name} - + ), width: '25%', }, diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx index ae0dd85b6a8b55..961320baa6a4ec 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx @@ -15,7 +15,7 @@ import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; -import { useTransactionOverviewHref } from '../../shared/Links/apm/TransactionOverviewLink'; +import { useServiceOrTransactionsOverviewHref } from '../../shared/Links/apm/service_transactions_overview'; import { MainTabs } from '../../shared/main_tabs'; import { ErrorGroupOverview } from '../ErrorGroupOverview'; import { ServiceMap } from '../ServiceMap'; @@ -60,7 +60,7 @@ export function ServiceDetailTabs({ serviceName, tab }: Props) { const transactionsTab = { key: 'transactions', - href: useTransactionOverviewHref(serviceName), + href: useServiceOrTransactionsOverviewHref(serviceName), text: i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { defaultMessage: 'Transactions', }), diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx index a4c93f95dc53d7..157d3ecc738a1a 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx @@ -21,7 +21,7 @@ import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { fontSizes, px, truncate, unit } from '../../../../style/variables'; import { ManagedTable, ITableColumn } from '../../../shared/ManagedTable'; import { EnvironmentBadge } from '../../../shared/EnvironmentBadge'; -import { TransactionOverviewLink } from '../../../shared/Links/apm/TransactionOverviewLink'; +import { ServiceOrTransactionsOverviewLink } from '../../../shared/Links/apm/service_transactions_overview'; import { AgentIcon } from '../../../shared/AgentIcon'; import { HealthBadge } from './HealthBadge'; import { ServiceListMetric } from './ServiceListMetric'; @@ -39,7 +39,7 @@ function formatString(value?: string | null) { return value || NOT_AVAILABLE_LABEL; } -const AppLink = styled(TransactionOverviewLink)` +const AppLink = styled(ServiceOrTransactionsOverviewLink)` font-size: ${fontSizes.large}; ${truncate('100%')}; `; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index ed4f3277a4a045..ae297b840ebc88 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -4,13 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexItem } from '@elastic/eui'; -import { EuiInMemoryTable } from '@elastic/eui'; -import { EuiTitle } from '@elastic/eui'; -import { EuiBasicTableColumn } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; +import { + EuiBasicTableColumn, + EuiFlexGroup, + EuiFlexItem, + EuiInMemoryTable, + EuiTitle, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { asDuration, asPercent, @@ -18,20 +21,18 @@ import { } from '../../../../../common/utils/formatters'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ServiceDependencyItem } from '../../../../../server/lib/services/get_service_dependencies'; -import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; -import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { callApmApi } from '../../../../services/rest/createCallApmApi'; -import { ServiceMapLink } from '../../../shared/Links/apm/ServiceMapLink'; -import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; -import { TableLinkFlexItem } from '../table_link_flex_item'; +import { px, unit } from '../../../../style/variables'; import { AgentIcon } from '../../../shared/AgentIcon'; -import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { SparkPlot } from '../../../shared/charts/spark_plot'; -import { px, unit } from '../../../../style/variables'; import { ImpactBar } from '../../../shared/ImpactBar'; +import { ServiceMapLink } from '../../../shared/Links/apm/ServiceMapLink'; import { ServiceOverviewLink } from '../../../shared/Links/apm/service_overview_link'; import { SpanIcon } from '../../../shared/span_icon'; +import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; +import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { ServiceOverviewTableContainer } from '../service_overview_table_container'; interface Props { @@ -192,8 +193,8 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) { return ( - - + +

{i18n.translate( @@ -205,7 +206,7 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) {

- + {i18n.translate( 'xpack.apm.serviceOverview.dependenciesTableLinkText', @@ -214,7 +215,7 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) { } )} - +
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index da74a6fc0004d5..d14ef648c22d3d 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -25,7 +25,6 @@ import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { TimestampTooltip } from '../../../shared/TimestampTooltip'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { ServiceOverviewTableContainer } from '../service_overview_table_container'; -import { TableLinkFlexItem } from '../table_link_flex_item'; interface Props { serviceName: string; @@ -195,8 +194,8 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { return ( - - + +

{i18n.translate('xpack.apm.serviceOverview.errorsTableTitle', { @@ -205,13 +204,13 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) {

- + {i18n.translate('xpack.apm.serviceOverview.errorsTableLinkText', { defaultMessage: 'View errors', })} - +
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx index 6345d546c716fd..4b262f1f51319a 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx @@ -20,6 +20,7 @@ import { asPercent, asTransactionRate, } from '../../../../../common/utils/formatters'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { useLatencyAggregationType } from '../../../../hooks/use_latency_Aggregation_type'; @@ -28,13 +29,11 @@ import { callApmApi, } from '../../../../services/rest/createCallApmApi'; import { px, unit } from '../../../../style/variables'; -import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; -import { TransactionOverviewLink } from '../../../shared/Links/apm/TransactionOverviewLink'; -import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; -import { TableLinkFlexItem } from '../table_link_flex_item'; import { SparkPlot } from '../../../shared/charts/spark_plot'; import { ImpactBar } from '../../../shared/ImpactBar'; -import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; +import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; +import { TransactionOverviewLink } from '../../../shared/Links/apm/transaction_overview_ink'; +import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { ServiceOverviewTableContainer } from '../service_overview_table_container'; @@ -270,7 +269,7 @@ export function ServiceOverviewTransactionsTable(props: Props) { - +

{i18n.translate( @@ -282,7 +281,7 @@ export function ServiceOverviewTransactionsTable(props: Props) {

- + {i18n.translate( 'xpack.apm.serviceOverview.transactionsTableLinkText', @@ -291,7 +290,7 @@ export function ServiceOverviewTransactionsTable(props: Props) { } )} - +
diff --git a/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx b/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx deleted file mode 100644 index 35df003af380d4..00000000000000 --- a/x-pack/plugins/apm/public/components/app/service_overview/table_link_flex_item.tsx +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiFlexItem } from '@elastic/eui'; -import styled from 'styled-components'; - -export const TableLinkFlexItem = styled(EuiFlexItem)` - & > a { - text-align: right; - } -`; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx similarity index 86% rename from x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx index 1d99b82a67326a..24a78e5d64749a 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx @@ -18,7 +18,7 @@ const persistedFilters: Array = [ 'latencyAggregationType', ]; -export function useTransactionOverviewHref(serviceName: string) { +export function useServiceOrTransactionsOverviewHref(serviceName: string) { return useAPMHref(`/services/${serviceName}/transactions`, persistedFilters); } @@ -26,7 +26,10 @@ interface Props extends APMLinkExtendProps { serviceName: string; } -export function TransactionOverviewLink({ serviceName, ...rest }: Props) { +export function ServiceOrTransactionsOverviewLink({ + serviceName, + ...rest +}: Props) { const { urlParams } = useUrlParams(); return ( diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx new file mode 100644 index 00000000000000..d2978b3c02d532 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { APMLink, APMLinkExtendProps } from './APMLink'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { APMQueryParams } from '../url_helpers'; + +interface Props extends APMLinkExtendProps { + serviceName: string; +} + +const persistedFilters: Array = [ + 'latencyAggregationType', +]; + +export function TransactionOverviewLink({ serviceName, ...rest }: Props) { + const { urlParams } = useUrlParams(); + + return ( + + ); +} From c05533ebbda8ea57cacb3d732c439178f72d6e4b Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Mon, 21 Dec 2020 11:42:51 +0000 Subject: [PATCH 003/100] Fix ECS HTTP scheme and improve docs (#86612) --- docs/user/security/audit-logging.asciidoc | 189 +++++++++++++++++- .../server/audit/audit_events.test.ts | 4 +- .../security/server/audit/audit_events.ts | 18 +- 3 files changed, 191 insertions(+), 20 deletions(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 7facde28e956f9..acb0f94cf878cb 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -47,9 +47,11 @@ For information on how to configure `xpack.security.audit.appender`, refer to Refer to the table of events that can be logged for auditing purposes. -Each event is broken down into `category`, `type`, `action` and `outcome` fields +Each event is broken down into <>, <>, <> and <> fields to make it easy to filter, query and aggregate the resulting logs. +Refer to <> for a table of fields that get logged with audit event. + [NOTE] ============================================================================ To ensure that a record of every operation is persisted even in case of an @@ -230,3 +232,188 @@ Refer to the corresponding {es} logs for potential write errors. | `http_request` | `unknown` | User is making an HTTP request. |====== + + +[[xpack-security-ecs-audit-schema]] +==== ECS audit schema + +Audit logs are written in JSON using https://www.elastic.co/guide/en/ecs/1.6/index.html[Elastic Common Schema (ECS)] specification. + +[cols="2*<"] +|====== + +2+a| ===== Base Fields + +| *Field* +| *Description* + +| `@timestamp` +| Time when the event was generated. + +Example: `2016-05-23T08:05:34.853Z` + +| `message` +| Human readable description of the event. + +2+a| ===== Event Fields + +| *Field* +| *Description* + +| [[field-event-action]] `event.action` +| The action captured by the event. + +Refer to <> for a table of possible actions. + +| [[field-event-category]] `event.category` +| High level category associated with the event. + +This field is closely related to `event.type`, which is used as a subcategory. + +Possible values: +`database`, +`web`, +`authentication` + +| [[field-event-type]] `event.type` +| Subcategory associated with the event. + +This field can be used along with the `event.category` field to enable filtering events down to a level appropriate for single visualization. + +Possible values: +`creation`, +`access`, +`change`, +`deletion` + +| [[field-event-outcome]] `event.outcome` +| Denotes whether the event represents a success or failure. + +Possible values: +`success`, +`failure`, +`unknown` + +2+a| ===== User Fields + +| *Field* +| *Description* + +| `user.name` +| Login name of the user. + +Example: `jdoe` + +| `user.roles[]` +| Set of user roles at the time of the event. + +Example: `[kibana_admin, reporting_user]` + +2+a| ===== Kibana Fields + +| *Field* +| *Description* + +| `kibana.space_id` +| ID of the space associated with the event. + +Example: `default` + +| `kibana.session_id` +| ID of the user session associated with the event. + +Each login attempt results in a unique session id. + +| `kibana.saved_object.type` +| Type of saved object associated with the event. + +Example: `dashboard` + +| `kibana.saved_object.id` +| ID of the saved object associated with the event. + +| `kibana.authentication_provider` +| Name of the authentication provider associated with the event. + +Example: `my-saml-provider` + +| `kibana.authentication_type` +| Type of the authentication provider associated with the event. + +Example: `saml` + +| `kibana.authentication_realm` +| Name of the Elasticsearch realm that has authenticated the user. + +Example: `native` + +| `kibana.lookup_realm` +| Name of the Elasticsearch realm where the user details were retrieved from. + +Example: `native` + +| `kibana.add_to_spaces[]` +| Set of space IDs that a saved object is being shared to as part of the event. + +Example: `[default, marketing]` + +| `kibana.delete_from_spaces[]` +| Set of space IDs that a saved object is being removed from as part of the event. + +Example: `[marketing]` + +2+a| ===== Error Fields + +| *Field* +| *Description* + +| `error.code` +| Error code describing the error. + +| `error.message` +| Error message. + +2+a| ===== HTTP and URL Fields + +| *Field* +| *Description* + +| `http.request.method` +| HTTP request method. + +Example: `get`, `post`, `put`, `delete` + +| `url.domain` +| Domain of the url. + +Example: `www.elastic.co` + +| `url.path` +| Path of the request. + +Example: `/search` + +| `url.port` +| Port of the request. + +Example: `443` + +| `url.query` +| The query field describes the query string of the request. + +Example: `q=elasticsearch` + +| `url.scheme` +| Scheme of the request. + +Example: `https` + +2+a| ===== Tracing Fields + +| *Field* +| *Description* + +| `trace.id` +| Unique identifier allowing events of the same transaction from {kib} and {es} to be be correlated. + +|====== diff --git a/x-pack/plugins/security/server/audit/audit_events.test.ts b/x-pack/plugins/security/server/audit/audit_events.test.ts index 9bda628df66dcb..f7e41bce674ee5 100644 --- a/x-pack/plugins/security/server/audit/audit_events.test.ts +++ b/x-pack/plugins/security/server/audit/audit_events.test.ts @@ -284,7 +284,7 @@ describe('#httpRequestEvent', () => { "path": "/path", "port": undefined, "query": undefined, - "scheme": "http:", + "scheme": "http", }, } `); @@ -321,7 +321,7 @@ describe('#httpRequestEvent', () => { "path": "/original/path", "port": undefined, "query": "query=param", - "scheme": "http:", + "scheme": "http", }, } `); diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index 7f0dd39162adff..b6538af31bd608 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -28,14 +28,9 @@ export interface AuditEvent { category?: EventCategory; type?: EventType; outcome?: EventOutcome; - module?: string; - dataset?: string; }; user?: { name: string; - email?: string; - full_name?: string; - hash?: string; roles?: readonly string[]; }; kibana?: { @@ -87,17 +82,10 @@ export interface AuditEvent { http?: { request?: { method?: string; - body?: { - content: string; - }; - }; - response?: { - status_code?: number; }; }; url?: { domain?: string; - full?: string; path?: string; port?: number; query?: string; @@ -108,14 +96,10 @@ export interface AuditEvent { export enum EventCategory { DATABASE = 'database', WEB = 'web', - IAM = 'iam', AUTHENTICATION = 'authentication', - PROCESS = 'process', } export enum EventType { - USER = 'user', - GROUP = 'group', CREATION = 'creation', ACCESS = 'access', CHANGE = 'change', @@ -152,7 +136,7 @@ export function httpRequestEvent({ request }: HttpRequestParams): AuditEvent { path: url.pathname, port: url.port ? parseInt(url.port, 10) : undefined, query: url.search ? url.search.slice(1) : undefined, - scheme: url.protocol, + scheme: url.protocol ? url.protocol.substr(0, url.protocol.length - 1) : undefined, }, }; } From 6d72042ca4acf2e8bc4b92431ee6f93d7c14cf56 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 21 Dec 2020 12:17:53 +0000 Subject: [PATCH 004/100] [ML] Fixing endpoint schema for can_delete_job endpoint (#86436) * [ML] Fixing endpoint schema for can_delete_job endpoint * changing get to post in docs * renaming canUntag * fixing typo Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/ml/common/types/saved_objects.ts | 4 ++-- .../delete_job_check_modal.tsx | 20 +++++++++---------- x-pack/plugins/ml/server/routes/apidoc.json | 2 +- .../plugins/ml/server/routes/saved_objects.ts | 20 ++++++++++++++----- .../ml/server/routes/schemas/saved_objects.ts | 5 +++++ .../plugins/ml/server/saved_objects/checks.ts | 16 +++++++-------- .../apis/ml/saved_objects/can_delete_job.ts | 8 ++++---- 7 files changed, 45 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index 8783113502623f..259f9be1a1b26e 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -21,7 +21,7 @@ export interface SyncSavedObjectResponse { export interface CanDeleteJobResponse { [jobId: string]: { canDelete: boolean; - canUntag: boolean; + canRemoveFromSpace: boolean; }; } @@ -41,5 +41,5 @@ export interface DeleteJobCheckResponse { export interface DeleteJobPermission { canDelete: boolean; - canUntag: boolean; + canRemoveFromSpace: boolean; } diff --git a/x-pack/plugins/ml/public/application/components/delete_job_check_modal/delete_job_check_modal.tsx b/x-pack/plugins/ml/public/application/components/delete_job_check_modal/delete_job_check_modal.tsx index 151946ab31fd98..4393d2b2fcf4dc 100644 --- a/x-pack/plugins/ml/public/application/components/delete_job_check_modal/delete_job_check_modal.tsx +++ b/x-pack/plugins/ml/public/application/components/delete_job_check_modal/delete_job_check_modal.tsx @@ -36,31 +36,31 @@ interface ModalContentReturnType { interface JobCheckRespSummary { canDelete: boolean; - canUntag: boolean; + canRemoveFromSpace: boolean; canTakeAnyAction: boolean; } function getRespSummary(resp: CanDeleteJobResponse): JobCheckRespSummary { const jobsChecked = Object.keys(resp); // Default to first job's permissions - const { canDelete, canUntag } = resp[jobsChecked[0]]; + const { canDelete, canRemoveFromSpace } = resp[jobsChecked[0]]; let canTakeAnyAction = true; if (jobsChecked.length > 1) { // Check all jobs and make sure they have the same permissions - otherwise no action can be taken canTakeAnyAction = jobsChecked.every( - (id) => resp[id].canDelete === canDelete && resp[id].canUntag === canUntag + (id) => resp[id].canDelete === canDelete && resp[id].canRemoveFromSpace === canRemoveFromSpace ); } - return { canDelete, canUntag, canTakeAnyAction }; + return { canDelete, canRemoveFromSpace, canTakeAnyAction }; } function getModalContent( jobIds: string[], respSummary: JobCheckRespSummary ): ModalContentReturnType { - const { canDelete, canUntag, canTakeAnyAction } = respSummary; + const { canDelete, canRemoveFromSpace, canTakeAnyAction } = respSummary; if (canTakeAnyAction === false) { return { @@ -116,7 +116,7 @@ function getModalContent( ), }; - } else if (canUntag) { + } else if (canRemoveFromSpace) { return { buttonText: ( = ({ // Do the spaces check and set the content for the modal and buttons depending on results canDeleteJob(jobType, jobIds).then((resp) => { const respSummary = getRespSummary(resp); - const { canDelete, canUntag, canTakeAnyAction } = respSummary; - if (canTakeAnyAction && canDelete && !canUntag) { + const { canDelete, canRemoveFromSpace, canTakeAnyAction } = respSummary; + if (canTakeAnyAction && canDelete && !canRemoveFromSpace) { // Go straight to delete flow if that's the only action available canDeleteCallback(); return; @@ -260,7 +260,7 @@ export const DeleteJobCheckModal: FC = ({ {!hasUntagged && jobCheckRespSummary?.canTakeAnyAction && - jobCheckRespSummary?.canUntag && + jobCheckRespSummary?.canRemoveFromSpace && jobCheckRespSummary?.canDelete && ( = ({ size="s" onClick={ jobCheckRespSummary?.canTakeAnyAction && - jobCheckRespSummary?.canUntag && + jobCheckRespSummary?.canRemoveFromSpace && !jobCheckRespSummary?.canDelete ? onUntagClick : onClick diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index bf002907b1a434..015ec6e4ec9c0a 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -151,7 +151,7 @@ "RemoveJobsFromSpaces", "RemoveJobsFromCurrentSpace", "JobsSpaces", - "DeleteJobCheck", + "CanDeleteJob", "TrainedModels", "GetTrainedModel", diff --git a/x-pack/plugins/ml/server/routes/saved_objects.ts b/x-pack/plugins/ml/server/routes/saved_objects.ts index 29f9b218ea177a..a5144ab2a7af7e 100644 --- a/x-pack/plugins/ml/server/routes/saved_objects.ts +++ b/x-pack/plugins/ml/server/routes/saved_objects.ts @@ -12,8 +12,8 @@ import { jobsAndCurrentSpace, syncJobObjects, jobTypeSchema, + canDeleteJobSchema, } from './schemas/saved_objects'; -import { jobIdsSchema } from './schemas/job_service_schema'; import { spacesUtilsProvider } from '../lib/spaces_utils'; import { JobType } from '../../common/types/saved_objects'; @@ -284,13 +284,23 @@ export function savedObjectsRoutes( /** * @apiGroup JobSavedObjects * - * @api {get} /api/ml/saved_objects/delete_job_check Check whether user can delete a job - * @apiName DeleteJobCheck + * @api {post} /api/ml/saved_objects/can_delete_job Check whether user can delete a job + * @apiName CanDeleteJob * @apiDescription Check the user's ability to delete jobs. Returns whether they are able * to fully delete the job and whether they are able to remove it from * the current space. + * Note, this is only for enabling UI controls. A user calling endpoints + * directly will still be able to delete or remove the job from a space. * - * @apiSchema (body) jobIdsSchema (params) jobTypeSchema + * @apiSchema (params) jobTypeSchema + * @apiSchema (body) jobIdsSchema + * @apiSuccessExample {json} Error-Response: + * { + * "my_job": { + * "canDelete": false, + * "canRemoveFromSpace": true + * } + * } * */ router.post( @@ -298,7 +308,7 @@ export function savedObjectsRoutes( path: '/api/ml/saved_objects/can_delete_job/{jobType}', validate: { params: jobTypeSchema, - body: jobIdsSchema, + body: canDeleteJobSchema, }, options: { tags: ['access:ml:canGetJobs', 'access:ml:canGetDataFrameAnalytics'], diff --git a/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts b/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts index 147398694f191c..a64d38a9fda70c 100644 --- a/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts +++ b/x-pack/plugins/ml/server/routes/schemas/saved_objects.ts @@ -22,3 +22,8 @@ export const syncJobObjects = schema.object({ simulate: schema.maybe(schema.bool export const jobTypeSchema = schema.object({ jobType: schema.string(), }); + +export const canDeleteJobSchema = schema.object({ + /** List of job IDs. */ + jobIds: schema.arrayOf(schema.maybe(schema.string())), +}); diff --git a/x-pack/plugins/ml/server/saved_objects/checks.ts b/x-pack/plugins/ml/server/saved_objects/checks.ts index f682999cd59666..9258b143c9747b 100644 --- a/x-pack/plugins/ml/server/saved_objects/checks.ts +++ b/x-pack/plugins/ml/server/saved_objects/checks.ts @@ -180,7 +180,7 @@ export function checksFactory( return jobIds.reduce((results, jobId) => { results[jobId] = { canDelete: false, - canUntag: false, + canRemoveFromSpace: false, }; return results; }, {} as DeleteJobCheckResponse); @@ -191,7 +191,7 @@ export function checksFactory( return jobIds.reduce((results, jobId) => { results[jobId] = { canDelete: true, - canUntag: false, + canRemoveFromSpace: false, }; return results; }, {} as DeleteJobCheckResponse); @@ -208,7 +208,7 @@ export function checksFactory( // job saved object not found results[jobId] = { canDelete: false, - canUntag: false, + canRemoveFromSpace: false, }; return results; } @@ -220,7 +220,7 @@ export function checksFactory( if (canCreateGlobalJobs && isGlobalJob) { results[jobId] = { canDelete: true, - canUntag: false, + canRemoveFromSpace: false, }; return results; } @@ -229,20 +229,20 @@ export function checksFactory( if (isGlobalJob) { results[jobId] = { canDelete: false, - canUntag: false, + canRemoveFromSpace: false, }; return results; } // jobs with are in individual spaces can only be untagged // from current space if the job is in more than 1 space - const canUntag = namespaces.length > 1; + const canRemoveFromSpace = namespaces.length > 1; // job is in individual spaces, user cannot see all of them - untag only, no delete if (namespaces.includes('?')) { results[jobId] = { canDelete: false, - canUntag, + canRemoveFromSpace, }; return results; } @@ -250,7 +250,7 @@ export function checksFactory( // job is individual spaces, user can see all of them - delete and option to untag results[jobId] = { canDelete: true, - canUntag, + canRemoveFromSpace, }; return results; }, {} as DeleteJobCheckResponse); diff --git a/x-pack/test/api_integration/apis/ml/saved_objects/can_delete_job.ts b/x-pack/test/api_integration/apis/ml/saved_objects/can_delete_job.ts index d95c90d417203a..e80d5a333bbdf0 100644 --- a/x-pack/test/api_integration/apis/ml/saved_objects/can_delete_job.ts +++ b/x-pack/test/api_integration/apis/ml/saved_objects/can_delete_job.ts @@ -75,7 +75,7 @@ export default ({ getService }: FtrProviderContext) => { idSpace1 ); - expect(body).to.eql({ [adJobIdSpace12]: { canDelete: false, canUntag: true } }); + expect(body).to.eql({ [adJobIdSpace12]: { canDelete: false, canRemoveFromSpace: true } }); }); it('job in individual spaces, all spaces user can delete and untag', async () => { @@ -87,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => { idSpace1 ); - expect(body).to.eql({ [adJobIdSpace12]: { canDelete: true, canUntag: true } }); + expect(body).to.eql({ [adJobIdSpace12]: { canDelete: true, canRemoveFromSpace: true } }); }); it('job in * space, single space user can not untag or delete', async () => { @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext) => { idSpace1 ); - expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: false, canUntag: false } }); + expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: false, canRemoveFromSpace: false } }); }); it('job in * space, all spaces user can delete but not untag', async () => { @@ -111,7 +111,7 @@ export default ({ getService }: FtrProviderContext) => { idStarSpace ); - expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: true, canUntag: false } }); + expect(body).to.eql({ [adJobIdStarSpace]: { canDelete: true, canRemoveFromSpace: false } }); }); }); }; From 388b2508b312d61f3fc480e2dc88379bc66ed9ce Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 21 Dec 2020 14:26:14 +0100 Subject: [PATCH 005/100] [Lens] Make sure Lens does not reload unnecessarily (#86092) --- .../public/react_expression_renderer.test.tsx | 38 +++++++++ .../public/react_expression_renderer.tsx | 18 +++-- .../lens/public/app_plugin/mounter.tsx | 80 +++++++++++-------- .../editor_frame/suggestion_panel.tsx | 14 +++- 4 files changed, 108 insertions(+), 42 deletions(-) diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index e52d4d153882ff..4ebd626e70fc3e 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -146,6 +146,44 @@ describe('ExpressionRenderer', () => { instance.unmount(); }); + it('waits for debounce period on other loader option change if specified', () => { + jest.useFakeTimers(); + + const refreshSubject = new Subject(); + const loaderUpdate = jest.fn(); + + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$: new Subject(), + data$: new Subject(), + loading$: new Subject(), + update: loaderUpdate, + destroy: jest.fn(), + }; + }); + + const instance = mount( + + ); + + instance.setProps({ searchContext: { from: 'now-30m', to: 'now' } }); + + expect(loaderUpdate).toHaveBeenCalledTimes(1); + + act(() => { + jest.runAllTimers(); + }); + + expect(loaderUpdate).toHaveBeenCalledTimes(2); + + instance.unmount(); + }); + it('should display a custom error message if the user provides one and then remove it after successful render', () => { const dataSubject = new Subject(); const data$ = dataSubject.asObservable().pipe(share()); diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index 894325c8b65f7d..eac2371ec66d0c 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -90,21 +90,23 @@ export const ReactExpressionRenderer = ({ null ); const [debouncedExpression, setDebouncedExpression] = useState(expression); - useEffect(() => { + const [waitingForDebounceToComplete, setDebouncePending] = useState(false); + useShallowCompareEffect(() => { if (debounce === undefined) { return; } + setDebouncePending(true); const handler = setTimeout(() => { setDebouncedExpression(expression); + setDebouncePending(false); }, debounce); return () => { clearTimeout(handler); }; - }, [expression, debounce]); + }, [expression, expressionLoaderOptions, debounce]); const activeExpression = debounce !== undefined ? debouncedExpression : expression; - const waitingForDebounceToComplete = debounce !== undefined && expression !== debouncedExpression; /* eslint-disable react-hooks/exhaustive-deps */ // OK to ignore react-hooks/exhaustive-deps because options update is handled by calling .update() @@ -182,12 +184,16 @@ export const ReactExpressionRenderer = ({ // Re-fetch data automatically when the inputs change useShallowCompareEffect( () => { - if (expressionLoaderRef.current) { + // only update the loader if the debounce period is over + if (expressionLoaderRef.current && !waitingForDebounceToComplete) { expressionLoaderRef.current.update(activeExpression, expressionLoaderOptions); } }, - // when expression is changed by reference and when any other loaderOption is changed by reference - [{ activeExpression, ...expressionLoaderOptions }] + // when debounced, wait for debounce status to change to update loader. + // Otherwise, update when expression is changed by reference and when any other loaderOption is changed by reference + debounce === undefined + ? [{ activeExpression, ...expressionLoaderOptions }] + : [{ waitingForDebounceToComplete }] ); /* eslint-enable react-hooks/exhaustive-deps */ diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index b09ecfdcd5553a..fbfd9c57589480 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -3,11 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { AppMountParameters, CoreSetup } from 'kibana/public'; import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { History } from 'history'; import { render, unmountComponentAtNode } from 'react-dom'; import { i18n } from '@kbn/i18n'; @@ -86,25 +87,22 @@ export async function mountApp( }) ); - const getInitialInput = ( - routeProps: RouteComponentProps<{ id?: string }>, - editByValue?: boolean - ): LensEmbeddableInput | undefined => { + const getInitialInput = (id?: string, editByValue?: boolean): LensEmbeddableInput | undefined => { if (editByValue) { return embeddableEditorIncomingState?.valueInput as LensByValueInput; } - if (routeProps.match.params.id) { - return { savedObjectId: routeProps.match.params.id } as LensByReferenceInput; + if (id) { + return { savedObjectId: id } as LensByReferenceInput; } }; - const redirectTo = (routeProps: RouteComponentProps<{ id?: string }>, savedObjectId?: string) => { + const redirectTo = (history: History, savedObjectId?: string) => { if (!savedObjectId) { - routeProps.history.push({ pathname: '/', search: routeProps.history.location.search }); + history.push({ pathname: '/', search: history.location.search }); } else { - routeProps.history.push({ + history.push({ pathname: `/edit/${savedObjectId}`, - search: routeProps.history.location.search, + search: history.location.search, }); } }; @@ -144,27 +142,45 @@ export async function mountApp( } }; - const renderEditor = ( - routeProps: RouteComponentProps<{ id?: string }>, - editByValue?: boolean + // const featureFlagConfig = await getByValueFeatureFlag(); + const EditorRenderer = React.memo( + (props: { id?: string; history: History; editByValue?: boolean }) => { + const redirectCallback = useCallback( + (id?: string) => { + redirectTo(props.history, id); + }, + [props.history] + ); + trackUiEvent('loaded'); + return ( + + ); + } + ); + + const EditorRoute = ( + routeProps: RouteComponentProps<{ id?: string }> & { editByValue?: boolean } ) => { - trackUiEvent('loaded'); return ( - redirectTo(routeProps, savedObjectId)} - redirectToOrigin={redirectToOrigin} - redirectToDashboard={redirectToDashboard} - onAppLeave={params.onAppLeave} - setHeaderActionMenu={params.setHeaderActionMenu} + ); }; @@ -185,13 +201,13 @@ export async function mountApp( - + renderEditor(routeProps, true)} + render={(routeProps) => } /> - + diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index e42d4daffbb66d..338a998b6b4dc8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -7,7 +7,7 @@ import './suggestion_panel.scss'; import _, { camelCase } from 'lodash'; -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo, useRef } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiIcon, @@ -270,13 +270,19 @@ export function SuggestionPanel({ [frame.query, frame.dateRange.fromDate, frame.dateRange.toDate, frame.filters] ); + const contextRef = useRef(context); + contextRef.current = context; + const AutoRefreshExpressionRenderer = useMemo(() => { const autoRefreshFetch$ = plugins.data.query.timefilter.timefilter.getAutoRefreshFetch$(); return (props: ReactExpressionRendererProps) => ( - + ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [plugins.data.query.timefilter.timefilter, context]); + }, [plugins.data.query.timefilter.timefilter, ExpressionRendererComponent]); const [lastSelectedSuggestion, setLastSelectedSuggestion] = useState(-1); From 36a8343064849c09fffd7a2d010cb38a028d82d6 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 21 Dec 2020 15:02:51 +0100 Subject: [PATCH 006/100] fix time scaling bugs (#86444) --- .../indexpattern_datasource/indexpattern.test.ts | 3 +++ .../suffix_formatter.test.ts | 16 ++++++++++++++++ .../indexpattern_datasource/suffix_formatter.ts | 5 +++++ .../indexpattern_datasource/to_expression.ts | 1 + 4 files changed, 25 insertions(+) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 042ea0353ac635..2e55abf4a429ad 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -530,6 +530,9 @@ describe('IndexPattern Data Source', () => { "outputColumnId": Array [ "col1", ], + "outputColumnName": Array [ + "Count of records", + ], "targetUnit": Array [ "h", ], diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts index ef1739e4424fa3..ade6ba099d70e9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.test.ts @@ -25,4 +25,20 @@ describe('suffix formatter', () => { expect(convertMock).toHaveBeenCalledWith(12345); expect(formatFactory).toHaveBeenCalledWith({ id: 'nestedFormatter', params: nestedParams }); }); + + it('should not add suffix to empty strings', () => { + const convertMock = jest.fn((x) => ''); + const formatFactory = jest.fn(() => ({ convert: convertMock })); + const SuffixFormatter = getSuffixFormatter((formatFactory as unknown) as FormatFactory); + const nestedParams = { abc: 123 }; + const formatterInstance = new SuffixFormatter({ + unit: 'h', + id: 'nestedFormatter', + params: nestedParams, + }); + + const result = formatterInstance.convert(12345); + + expect(result).toEqual(''); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts index f5d764acab0869..3d9f3be01a11bb 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts @@ -49,6 +49,11 @@ export function getSuffixFormatter(formatFactory: FormatFactory) { val ); + // do not add suffixes to empty strings + if (formattedValue === '') { + return ''; + } + if (suffix) { return `${formattedValue}${suffix}`; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index a85b8920366b55..a5ce4dfbea371f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -121,6 +121,7 @@ function getExpressionForLayer( dateColumnId: [firstDateHistogramColumn![0]], inputColumnId: [id], outputColumnId: [id], + outputColumnName: [col.label], targetUnit: [col.timeScale!], }, }; From 34803ed98bbe6e04a11add288b1f9fae8bc3e201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Mon, 21 Dec 2020 15:13:07 +0100 Subject: [PATCH 007/100] [Logs UI] Toggle log entry context menu when user clicks the trigger button (#86307) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../logging/log_text_stream/log_entry_context_menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx index c627d6eda22a57..fe57b9db0e8b7c 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_entry_context_menu.tsx @@ -60,7 +60,7 @@ export const LogEntryContextMenu: React.FC = ({ size="s" fill aria-label={ariaLabel || DEFAULT_MENU_LABEL} - onClick={onOpen} + onClick={isOpen ? onClose : onOpen} minWidth="auto" > From d744eaec8ce40516fd0d6e81ba784e2dec5ca24f Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 21 Dec 2020 15:22:42 +0100 Subject: [PATCH 008/100] [ML] Data Frame Analytics: Scatterplot Matrix Fixes (#86357) - use ml API service from the Kibana context - adds jest tests for the Vega Lite Spec generator - fix chart layout overflow with too many fields selected --- .../scatterplot_matrix.scss | 2 + .../scatterplot_matrix/scatterplot_matrix.tsx | 6 +- .../scatterplot_matrix_vega_lite_spec.test.ts | 170 ++++++++++++++++++ .../scatterplot_matrix_vega_lite_spec.ts | 21 ++- 4 files changed, 188 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss index f59c27dccc7426..6a4fbd97b3bddd 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.scss @@ -1,4 +1,6 @@ .mlScatterplotMatrix { + overflow-x: auto; + .vega-bind span { font-size: $euiFontSizeXS; padding: 0 $euiSizeXS; diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index ad9f63b10ebdd2..b1ee9afb17788a 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -30,7 +30,7 @@ import { i18n } from '@kbn/i18n'; import type { SearchResponse7 } from '../../../../common/types/es_client'; -import { ml } from '../../services/ml_api_service'; +import { useMlApiContext } from '../../contexts/kibana'; import { getProcessedFields } from '../data_grid'; import { useCurrentEuiTheme } from '../color_range_legend'; @@ -72,6 +72,8 @@ export const ScatterplotMatrix: FC = ({ color, legendType, }) => { + const { esSearch } = useMlApiContext(); + // dynamicSize is optionally used for outlier charts where the scatterplot marks // are sized according to outlier_score const [dynamicSize, setDynamicSize] = useState(false); @@ -147,7 +149,7 @@ export const ScatterplotMatrix: FC = ({ } : { match_all: {} }; - const resp: SearchResponse7 = await ml.esSearch({ + const resp: SearchResponse7 = await esSearch({ index, body: { fields: queryFields, diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts new file mode 100644 index 00000000000000..dd467161ff489e --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.test.ts @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// @ts-ignore +import { compile } from 'vega-lite/build-es5/vega-lite'; + +import euiThemeLight from '@elastic/eui/dist/eui_theme_light.json'; + +import { + getColorSpec, + getScatterplotMatrixVegaLiteSpec, + COLOR_OUTLIER, + COLOR_RANGE_NOMINAL, + DEFAULT_COLOR, + LEGEND_TYPES, +} from './scatterplot_matrix_vega_lite_spec'; + +describe('getColorSpec()', () => { + it('should return the default color for non-outlier specs', () => { + const colorSpec = getColorSpec(euiThemeLight, false); + + expect(colorSpec).toEqual({ value: DEFAULT_COLOR }); + }); + + it('should return a conditional spec for outliers', () => { + const colorSpec = getColorSpec(euiThemeLight, true); + + expect(colorSpec).toEqual({ + condition: { + test: "(datum['outlier_score'] >= mlOutlierScoreThreshold.cutoff)", + value: COLOR_OUTLIER, + }, + value: euiThemeLight.euiColorMediumShade, + }); + }); + + it('should return a field based spec for non-outlier specs with legendType supplied', () => { + const colorName = 'the-color-field'; + + const colorSpec = getColorSpec(euiThemeLight, false, colorName, LEGEND_TYPES.NOMINAL); + + expect(colorSpec).toEqual({ + field: colorName, + scale: { + range: COLOR_RANGE_NOMINAL, + }, + type: 'nominal', + }); + }); +}); + +describe('getScatterplotMatrixVegaLiteSpec()', () => { + it('should return the default spec for non-outliers without a legend', () => { + const data = [{ x: 1, y: 1 }]; + + const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec(data, ['x', 'y'], euiThemeLight); + + // A valid Vega Lite spec shouldn't throw an error when compiled. + expect(() => compile(vegaLiteSpec)).not.toThrow(); + + expect(vegaLiteSpec.repeat).toEqual({ + column: ['x', 'y'], + row: ['y', 'x'], + }); + expect(vegaLiteSpec.spec.transform).toEqual([ + { as: 'x', calculate: "datum['x']" }, + { as: 'y', calculate: "datum['y']" }, + ]); + expect(vegaLiteSpec.spec.data.values).toEqual(data); + expect(vegaLiteSpec.spec.mark).toEqual({ + opacity: 0.75, + size: 8, + type: 'circle', + }); + expect(vegaLiteSpec.spec.encoding.color).toEqual({ value: DEFAULT_COLOR }); + expect(vegaLiteSpec.spec.encoding.tooltip).toEqual([ + { field: 'x', type: 'quantitative' }, + { field: 'y', type: 'quantitative' }, + ]); + }); + + it('should return the spec for outliers', () => { + const data = [{ x: 1, y: 1 }]; + + const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec(data, ['x', 'y'], euiThemeLight, 'ml'); + + // A valid Vega Lite spec shouldn't throw an error when compiled. + expect(() => compile(vegaLiteSpec)).not.toThrow(); + + expect(vegaLiteSpec.repeat).toEqual({ + column: ['x', 'y'], + row: ['y', 'x'], + }); + expect(vegaLiteSpec.spec.transform).toEqual([ + { as: 'x', calculate: "datum['x']" }, + { as: 'y', calculate: "datum['y']" }, + { + as: 'outlier_score', + calculate: "datum['ml.outlier_score']", + }, + ]); + expect(vegaLiteSpec.spec.data.values).toEqual(data); + expect(vegaLiteSpec.spec.mark).toEqual({ + opacity: 0.75, + size: 8, + type: 'circle', + }); + expect(vegaLiteSpec.spec.encoding.color).toEqual({ + condition: { + test: "(datum['outlier_score'] >= mlOutlierScoreThreshold.cutoff)", + value: COLOR_OUTLIER, + }, + value: euiThemeLight.euiColorMediumShade, + }); + expect(vegaLiteSpec.spec.encoding.tooltip).toEqual([ + { field: 'x', type: 'quantitative' }, + { field: 'y', type: 'quantitative' }, + { + field: 'outlier_score', + format: '.3f', + type: 'quantitative', + }, + ]); + }); + + it('should return the spec for classification', () => { + const data = [{ x: 1, y: 1 }]; + + const vegaLiteSpec = getScatterplotMatrixVegaLiteSpec( + data, + ['x', 'y'], + euiThemeLight, + undefined, + 'the-color-field', + LEGEND_TYPES.NOMINAL + ); + + // A valid Vega Lite spec shouldn't throw an error when compiled. + expect(() => compile(vegaLiteSpec)).not.toThrow(); + + expect(vegaLiteSpec.repeat).toEqual({ + column: ['x', 'y'], + row: ['y', 'x'], + }); + expect(vegaLiteSpec.spec.transform).toEqual([ + { as: 'x', calculate: "datum['x']" }, + { as: 'y', calculate: "datum['y']" }, + ]); + expect(vegaLiteSpec.spec.data.values).toEqual(data); + expect(vegaLiteSpec.spec.mark).toEqual({ + opacity: 0.75, + size: 8, + type: 'circle', + }); + expect(vegaLiteSpec.spec.encoding.color).toEqual({ + field: 'the-color-field', + scale: { + range: COLOR_RANGE_NOMINAL, + }, + type: 'nominal', + }); + expect(vegaLiteSpec.spec.encoding.tooltip).toEqual([ + { field: 'x', type: 'quantitative' }, + { field: 'y', type: 'quantitative' }, + ]); + }); +}); diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts index ee93074d9e034c..9e0834dd8b9221 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix_vega_lite_spec.ts @@ -24,12 +24,12 @@ export const OUTLIER_SCORE_FIELD = 'outlier_score'; const SCATTERPLOT_SIZE = 125; -const DEFAULT_COLOR = euiPaletteColorBlind()[0]; -const COLOR_OUTLIER = euiPaletteNegative(2)[1]; -const COLOR_RANGE_NOMINAL = euiPaletteColorBlind({ rotations: 2 }); -const COLOR_RANGE_QUANTITATIVE = euiPalettePositive(5); +export const DEFAULT_COLOR = euiPaletteColorBlind()[0]; +export const COLOR_OUTLIER = euiPaletteNegative(2)[1]; +export const COLOR_RANGE_NOMINAL = euiPaletteColorBlind({ rotations: 2 }); +export const COLOR_RANGE_QUANTITATIVE = euiPalettePositive(5); -const getColorSpec = ( +export const getColorSpec = ( euiTheme: typeof euiThemeLight, outliers = true, color?: string, @@ -72,10 +72,13 @@ export const getScatterplotMatrixVegaLiteSpec = ( calculate: `datum['${column}']`, as: column, })); - transform.push({ - calculate: `datum['${resultsField}.${OUTLIER_SCORE_FIELD}']`, - as: OUTLIER_SCORE_FIELD, - }); + + if (resultsField !== undefined) { + transform.push({ + calculate: `datum['${resultsField}.${OUTLIER_SCORE_FIELD}']`, + as: OUTLIER_SCORE_FIELD, + }); + } return { $schema: 'https://vega.github.io/schema/vega-lite/v4.17.0.json', From 8d5dd1de63d44b42db7acb8ba6fd0080cee0eeed Mon Sep 17 00:00:00 2001 From: ymao1 Date: Mon, 21 Dec 2020 10:11:19 -0500 Subject: [PATCH 009/100] [Alerting] Updating wording on "Notify" dropdown (#86453) * Updating wording * PR fixes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../sections/alert_form/alert_notify_when.tsx | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx index da872484dda4a2..eeb8b9f77a9b89 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_notify_when.tsx @@ -29,13 +29,18 @@ const DEFAULT_NOTIFY_WHEN_VALUE: AlertNotifyWhenType = 'onActionGroupChange'; const NOTIFY_WHEN_OPTIONS: Array> = [ { value: 'onActionGroupChange', - inputDisplay: 'Run only on status change', + inputDisplay: i18n.translate( + 'xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.onActionGroupChange.display', + { + defaultMessage: 'Only on status change.', + } + ), 'data-test-subj': 'onActionGroupChange', dropdownDisplay: ( @@ -52,13 +57,18 @@ const NOTIFY_WHEN_OPTIONS: Array> = [ }, { value: 'onActiveAlert', - inputDisplay: 'Run every time alert is active', + inputDisplay: i18n.translate( + 'xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.onActiveAlert.display', + { + defaultMessage: 'Every time alert is active', + } + ), 'data-test-subj': 'onActiveAlert', dropdownDisplay: ( @@ -75,13 +85,18 @@ const NOTIFY_WHEN_OPTIONS: Array> = [ }, { value: 'onThrottleInterval', - inputDisplay: 'Set a custom action interval', + inputDisplay: i18n.translate( + 'xpack.triggersActionsUI.sections.alertForm.alertNotifyWhen.onThrottleInterval.display', + { + defaultMessage: 'On a custom action interval', + } + ), 'data-test-subj': 'onThrottleInterval', dropdownDisplay: ( @@ -143,7 +158,7 @@ export const AlertNotifyWhen = ({ <> {' '} Date: Mon, 21 Dec 2020 08:18:32 -0700 Subject: [PATCH 010/100] [Security Solution] Fixing jest warnings and errors (#86532) --- .../components/case_action_bar/index.tsx | 20 ++++++------- .../event_details/event_details.test.tsx | 30 +++++++++++-------- .../events_viewer/events_viewer.test.tsx | 9 +++++- .../exceptions/builder/entry_item.test.tsx | 30 +++++++++++-------- .../components/last_updated/index.test.tsx | 14 ++++++--- .../ml/score/anomaly_score.test.tsx | 5 ++-- .../ml/score/anomaly_scores.test.tsx | 5 ++-- .../score/create_descriptions_list.test.tsx | 14 ++++----- .../properties/use_create_timeline.tsx | 4 +-- .../timeline/query_bar/index.test.tsx | 9 +++--- 10 files changed, 83 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx index 3bad7b7870a537..3a65ea724d7641 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_action_bar/index.tsx @@ -101,17 +101,17 @@ const CaseActionBarComponent: React.FC = ({ + + + + {i18n.SYNC_ALERTS} + + + + + + - - - - {i18n.SYNC_ALERTS} - - - - - - { @@ -36,18 +37,21 @@ describe('EventDetails', () => { isAlert: true, }; - const wrapper = mount( - - - - ); - - const alertsWrapper = mount( - - - - ); - + let wrapper: ReactWrapper; + let alertsWrapper: ReactWrapper; + beforeAll(async () => { + wrapper = mount( + + + + ) as ReactWrapper; + alertsWrapper = mount( + + + + ) as ReactWrapper; + await waitFor(() => wrapper.update()); + }); describe('rendering', () => { test('should match snapshot', () => { const shallowWrap = shallow(); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 5e5bdebffa1828..423b3566e4eb5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -30,6 +30,13 @@ jest.mock('../../../timelines/components/graph_overlay', () => ({ GraphOverlay: jest.fn(() =>
), })); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + return { + ...original, + useDataGridColumnSorting: jest.fn(), + }; +}); jest.mock('../../../timelines/containers', () => ({ useTimelineEvents: jest.fn(), })); @@ -84,7 +91,7 @@ const eventsViewerDefaultProps = { sort: [ { columnId: 'foo', - sortDirection: 'none' as SortDirection, + sortDirection: 'asc' as SortDirection, }, ], scopeId: SourcererScopeName.timeline, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.test.tsx index 60b2f079c869bf..ba894e0410b04d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/entry_item.test.tsx @@ -25,6 +25,7 @@ import { } from '../../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; import { getFoundListSchemaMock } from '../../../../../../lists/common/schemas/response/found_list_schema.mock'; import { getEmptyValue } from '../../empty_value'; +import { waitFor } from '@testing-library/dom'; // mock out lists hook const mockStart = jest.fn(); @@ -583,7 +584,7 @@ describe('BuilderEntryItem', () => { ); }); - test('it invokes "setErrorsExist" when user touches value input and leaves empty', () => { + test('it invokes "setErrorsExist" when user touches value input and leaves empty', async () => { const mockSetErrorExists = jest.fn(); wrapper = mount( { /> ); - ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { - onBlur: () => void; - }).onBlur(); + await waitFor(() => { + ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { + onBlur: () => void; + }).onBlur(); + }); expect(mockSetErrorExists).toHaveBeenCalledWith(true); }); - test('it invokes "setErrorsExist" when invalid value inputted for field value input', () => { + test('it invokes "setErrorsExist" when invalid value inputted for field value input', async () => { const mockSetErrorExists = jest.fn(); wrapper = mount( { setErrorsExist={mockSetErrorExists} /> ); - ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { - onBlur: () => void; - }).onBlur(); - // Invalid input because field type is number - ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { - onSearchChange: (arg: string) => void; - }).onSearchChange('hellooo'); + await waitFor(() => { + ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { + onBlur: () => void; + }).onBlur(); + + // Invalid input because field type is number + ((wrapper.find(EuiComboBox).at(2).props() as unknown) as { + onSearchChange: (arg: string) => void; + }).onSearchChange('hellooo'); + }); expect(mockSetErrorExists).toHaveBeenCalledWith(true); }); diff --git a/x-pack/plugins/security_solution/public/common/components/last_updated/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/last_updated/index.test.tsx index db42794448c533..31ba5530f60829 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_updated/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_updated/index.test.tsx @@ -8,12 +8,18 @@ import { mount } from 'enzyme'; import { I18nProvider } from '@kbn/i18n/react'; import { LastUpdatedAt } from './'; +jest.mock('@kbn/i18n/react', () => { + const originalModule = jest.requireActual('@kbn/i18n/react'); + const FormattedRelative = jest.fn(); + FormattedRelative.mockImplementation(() => '2 minutes ago'); + + return { + ...originalModule, + FormattedRelative, + }; +}); describe('LastUpdatedAt', () => { - beforeEach(() => { - Date.now = jest.fn().mockReturnValue(1603995369774); - }); - test('it renders correct relative time', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx index cb10c61302d3c2..745bd486524adb 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_score.test.tsx @@ -14,6 +14,7 @@ import { mockAnomalies } from '../mock'; import { TestProviders } from '../../../mock/test_providers'; import { useMountAppended } from '../../../utils/use_mount_appended'; import { Anomalies } from '../types'; +import { waitFor } from '@testing-library/dom'; const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; @@ -57,7 +58,7 @@ describe('anomaly_scores', () => { expect(wrapper.find('[data-test-subj="anomaly-description-list"]').exists()).toEqual(false); }); - test('show a popover on a mouse click', () => { + test('show a popover on a mouse click', async () => { const wrapper = mount( { ); wrapper.find('[data-test-subj="anomaly-score-popover"]').first().simulate('click'); - wrapper.update(); + await waitFor(() => wrapper.update()); expect(wrapper.find('[data-test-subj="anomaly-description-list"]').exists()).toEqual(true); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx index 52151f217e01ad..edbb3528a098af 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/anomaly_scores.test.tsx @@ -15,6 +15,7 @@ import { TestProviders } from '../../../mock/test_providers'; import { getEmptyValue } from '../../empty_value'; import { Anomalies } from '../types'; import { useMountAppended } from '../../../utils/use_mount_appended'; +import { waitFor } from '@testing-library/dom'; const startDate: string = '2020-07-07T08:20:18.966Z'; const endDate: string = '3000-01-01T00:00:00.000Z'; @@ -121,7 +122,7 @@ describe('anomaly_scores', () => { expect(wrapper.find('[data-test-subj="anomaly-description-list"]').exists()).toEqual(false); }); - test('showing a popover on a mouse click', () => { + test('showing a popover on a mouse click', async () => { const wrapper = mount( { ); wrapper.find('[data-test-subj="anomaly-score-popover"]').first().simulate('click'); - wrapper.update(); + await waitFor(() => wrapper.update()); expect(wrapper.find('[data-test-subj="anomaly-description-list"]').exists()).toEqual(true); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/create_descriptions_list.test.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/create_descriptions_list.test.tsx index e9dd5f922e26a5..695b4dd8caea45 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/create_descriptions_list.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/create_descriptions_list.test.tsx @@ -10,6 +10,7 @@ import { mockAnomalies } from '../mock'; import { createDescriptionList } from './create_description_list'; import { EuiDescriptionList } from '@elastic/eui'; import { Anomaly } from '../types'; +import { waitFor } from '@testing-library/dom'; jest.mock('../../../lib/kibana'); @@ -38,7 +39,7 @@ describe('create_description_list', () => { expect(wrapper).toMatchSnapshot(); }); - test('it calls the narrow date range function on click', () => { + test('it calls the narrow date range function on click', async () => { const wrapper = mount( { .find('[data-test-subj="anomaly-description-narrow-range-link"]') .first() .simulate('click'); - wrapper.update(); + await waitFor(() => wrapper.update()); expect(narrowDateRange.mock.calls.length).toBe(1); }); - test('it should the narrow date range with the score', () => { + test('it should the narrow date range with the score', async () => { const wrapper = mount( { .find('[data-test-subj="anomaly-description-narrow-range-link"]') .first() .simulate('click'); - wrapper.update(); + await waitFor(() => wrapper.update()); const expected: Anomaly = { detectorIndex: 0, @@ -119,7 +120,7 @@ describe('create_description_list', () => { expect(narrowDateRange.mock.calls[0][0]).toEqual(expected); }); - test('it should call the narrow date range with the interval', () => { + test('it should call the narrow date range with the interval', async () => { const wrapper = mount( { .find('[data-test-subj="anomaly-description-narrow-range-link"]') .first() .simulate('click'); - wrapper.update(); - + await waitFor(() => wrapper.update()); expect(narrowDateRange.mock.calls[0][1]).toEqual('hours'); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx index 12845477e0f393..96187329a21c65 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/use_create_timeline.tsx @@ -123,13 +123,13 @@ export const useCreateTimelineButton = ({ timelineId, timelineType, closeGearMen }; const dataTestSubjPrefix = timelineType === TimelineType.template ? `template-timeline-new` : `timeline-new`; - + const { fill: noThanks, ...propsWithoutFill } = buttonProps; return outline ? ( {title} ) : ( - + {title} ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx index 1226dabe485590..959504249e1270 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.test.tsx @@ -23,6 +23,7 @@ import { getDataProviderFilter, TIMELINE_FILTER_DROP_AREA, } from './index'; +import { waitFor } from '@testing-library/dom'; const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings; @@ -181,7 +182,7 @@ describe('Timeline QueryBar ', () => { }); describe('#onSavedQuery', () => { - test('is only reference that changed when dataProviders props get updated', () => { + test('is only reference that changed when dataProviders props get updated', async () => { const Proxy = (props: QueryBarTimelineComponentProps) => ( @@ -213,13 +214,13 @@ describe('Timeline QueryBar ', () => { const onSavedQueryRef = queryBarProps.onSavedQuery; wrapper.setProps({ dataProviders: mockDataProviders.slice(1, 0) }); - wrapper.update(); + await waitFor(() => wrapper.update()); expect(onSavedQueryRef).not.toEqual(wrapper.find(QueryBar).props().onSavedQuery); expect(onSubmitQueryRef).toEqual(wrapper.find(QueryBar).props().onSubmitQuery); }); - test('is only reference that changed when savedQueryId props get updated', () => { + test('is only reference that changed when savedQueryId props get updated', async () => { const Proxy = (props: QueryBarTimelineComponentProps) => ( @@ -253,7 +254,7 @@ describe('Timeline QueryBar ', () => { wrapper.setProps({ savedQueryId: 'new', }); - wrapper.update(); + await waitFor(() => wrapper.update()); expect(onSavedQueryRef).not.toEqual(wrapper.find(QueryBar).props().onSavedQuery); expect(onSubmitQueryRef).toEqual(wrapper.find(QueryBar).props().onSubmitQuery); From 664a7553d23adc22e17110887f5b4ccbb505c004 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 21 Dec 2020 11:19:25 -0500 Subject: [PATCH 011/100] [Lens] Refactor param editor to use layers (#86499) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/dimension_editor.tsx | 33 +- .../calculations/moving_average.tsx | 13 +- .../definitions/date_histogram.test.tsx | 458 +++++++++--------- .../operations/definitions/date_histogram.tsx | 12 +- .../definitions/filters/filters.test.tsx | 190 ++++---- .../definitions/filters/filters.tsx | 10 +- .../operations/definitions/index.ts | 13 +- .../definitions/last_value.test.tsx | 152 +++--- .../operations/definitions/last_value.tsx | 18 +- .../definitions/ranges/ranges.test.tsx | 434 +++++++---------- .../operations/definitions/ranges/ranges.tsx | 56 +-- .../operations/definitions/terms/index.tsx | 46 +- .../definitions/terms/terms.test.tsx | 274 +++++------ .../operations/layer_helpers.test.ts | 46 +- .../operations/layer_helpers.ts | 51 +- 15 files changed, 808 insertions(+), 998 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index df3b769acf8504..c655fc18ab5fae 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -107,7 +107,15 @@ export function DimensionEditor(props: DimensionEditorProps) { layerId, currentIndexPattern, hideGrouping, + dateRange, } = props; + const services = { + data: props.data, + uiSettings: props.uiSettings, + savedObjectsClient: props.savedObjectsClient, + http: props.http, + storage: props.storage, + }; const { fieldByOperation, operationWithoutField } = operationSupportMatrix; const setStateWrapper = (layer: IndexPatternLayer) => { @@ -346,17 +354,13 @@ export function DimensionEditor(props: DimensionEditorProps) { {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ParamEditor && ( <> )} @@ -407,12 +411,15 @@ export function DimensionEditor(props: DimensionEditorProps) { selectedColumn={selectedColumn} onChange={(newFormat) => { setState( - updateColumnParam({ + mergeLayer({ state, layerId, - currentColumn: selectedColumn, - paramName: 'format', - value: newFormat, + newLayer: updateColumnParam({ + layer: state.layers[layerId], + columnId, + paramName: 'format', + value: newFormat, + }), }) ); }} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index 522899662fbd10..59d5924b9a3707 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -111,10 +111,10 @@ export const movingAverageOperation: OperationDefinition< }; function MovingAverageParamEditor({ - state, - setState, + layer, + updateLayer, currentColumn, - layerId, + columnId, }: ParamEditorProps) { const [inputValue, setInputValue] = useState(String(currentColumn.params.window)); @@ -124,11 +124,10 @@ function MovingAverageParamEditor({ return; } const inputNumber = Number(inputValue); - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'window', value: inputNumber, }) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index e40b9ccf89c1ab..eadcf8384b1dd9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -5,19 +5,19 @@ */ import React from 'react'; -import { DateHistogramIndexPatternColumn } from './date_histogram'; +import type { DateHistogramIndexPatternColumn } from './date_histogram'; import { dateHistogramOperation } from './index'; import { shallow } from 'enzyme'; import { EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; -import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import type { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { UI_SETTINGS } from '../../../../../../../src/plugins/data/public'; import { dataPluginMock, getCalculateAutoTimeExpression, } from '../../../../../../../src/plugins/data/public/mocks'; import { createMockedIndexPattern } from '../../mocks'; -import { IndexPatternPrivateState } from '../../types'; +import type { IndexPatternLayer, IndexPattern } from '../../types'; import { getFieldByNameFactory } from '../../pure_helpers'; const dataStart = dataPluginMock.createStartContract(); @@ -29,6 +29,60 @@ dataStart.search.aggs.calculateAutoTimeExpression = getCalculateAutoTimeExpressi } ); +const indexPattern1: IndexPattern = { + id: '1', + title: 'Mock Indexpattern', + timeFieldName: 'timestamp', + hasRestrictions: false, + fields: [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + }, + ], + + getFieldByName: getFieldByNameFactory([ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + }, + ]), +}; + +const indexPattern2: IndexPattern = { + id: '2', + title: 'Mock Indexpattern 2', + hasRestrictions: false, + fields: [ + { + name: 'other_timestamp', + displayName: 'other_timestamp', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + }, + ], + getFieldByName: getFieldByNameFactory([ + { + name: 'other_timestamp', + displayName: 'other_timestamp', + type: 'date', + esTypes: ['date'], + aggregatable: true, + searchable: true, + }, + ]), +}; + const defaultOptions = { storage: {} as IStorageWrapper, uiSettings: {} as IUiSettingsClient, @@ -39,150 +93,47 @@ const defaultOptions = { }, data: dataStart, http: {} as HttpSetup, + indexPattern: indexPattern1, }; describe('date_histogram', () => { - let state: IndexPatternPrivateState; + let layer: IndexPatternLayer; const InlineOptions = dateHistogramOperation.paramEditor!; beforeEach(() => { - state = { - indexPatternRefs: [], - existingFields: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - indexPatterns: { - 1: { - id: '1', - title: 'Mock Indexpattern', - timeFieldName: 'timestamp', - hasRestrictions: false, - fields: [ - { - name: 'timestamp', - displayName: 'timestampLabel', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - }, - ], + layer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, - getFieldByName: getFieldByNameFactory([ - { - name: 'timestamp', - displayName: 'timestampLabel', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - }, - ]), - }, - 2: { - id: '2', - title: 'Mock Indexpattern 2', - hasRestrictions: false, - fields: [ - { - name: 'other_timestamp', - displayName: 'other_timestamp', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - }, - ], - getFieldByName: getFieldByNameFactory([ - { - name: 'other_timestamp', - displayName: 'other_timestamp', - type: 'date', - esTypes: ['date'], - aggregatable: true, - searchable: true, - }, - ]), - }, - }, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Value of timestamp', - dataType: 'date', - isBucketed: true, - - // Private - operationType: 'date_histogram', - params: { - interval: '42w', - }, - sourceField: 'timestamp', - }, - }, - }, - second: { - indexPatternId: '2', - columnOrder: ['col2'], - columns: { - col2: { - label: 'Value of timestamp', - dataType: 'date', - isBucketed: true, - - // Private - operationType: 'date_histogram', - params: { - interval: 'd', - }, - sourceField: 'other_timestamp', - }, - }, - }, - third: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - label: 'Value of timestamp', - dataType: 'date', - isBucketed: true, - - // Private - operationType: 'date_histogram', - params: { - interval: 'auto', - }, - sourceField: 'timestamp', - }, + // Private + operationType: 'date_histogram', + params: { + interval: '42w', }, + sourceField: 'timestamp', }, }, }; }); - function stateWithInterval(interval: string) { + function layerWithInterval(interval: string) { return ({ - ...state, - layers: { - ...state.layers, - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - interval, - }, - }, + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + interval, }, }, }, - } as unknown) as IndexPatternPrivateState; + } as unknown) as IndexPatternLayer; } describe('buildColumn', () => { @@ -246,9 +197,9 @@ describe('date_histogram', () => { describe('toEsAggsFn', () => { it('should reflect params correctly', () => { const esAggsFn = dateHistogramOperation.toEsAggsFn( - state.layers.first.columns.col1 as DateHistogramIndexPatternColumn, + layer.columns.col1 as DateHistogramIndexPatternColumn, 'col1', - state.indexPatterns['1'] + indexPattern1 ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -263,10 +214,10 @@ describe('date_histogram', () => { it('should not use normalized es interval for rollups', () => { const esAggsFn = dateHistogramOperation.toEsAggsFn( - state.layers.first.columns.col1 as DateHistogramIndexPatternColumn, + layer.columns.col1 as DateHistogramIndexPatternColumn, 'col1', { - ...state.indexPatterns['1'], + ...indexPattern1, fields: [ { name: 'timestamp', @@ -465,15 +416,14 @@ describe('date_histogram', () => { describe('param editor', () => { it('should render current value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -482,15 +432,34 @@ describe('date_histogram', () => { }); it('should render current value for other index pattern', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); + + const secondLayer: IndexPatternLayer = { + indexPatternId: '2', + columnOrder: ['col2'], + columns: { + col2: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, + + // Private + operationType: 'date_histogram', + params: { + interval: 'd', + }, + sourceField: 'other_timestamp', + }, + }, + }; const instance = shallow( ); @@ -499,14 +468,33 @@ describe('date_histogram', () => { }); it('should render disabled switch and no time interval control for auto interval', () => { + const thirdLayer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, + + // Private + operationType: 'date_histogram', + params: { + interval: 'auto', + }, + sourceField: 'timestamp', + }, + }, + }; + const instance = shallow( ); expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); @@ -515,35 +503,52 @@ describe('date_histogram', () => { }); it('should allow switching to manual interval', () => { - const setStateSpy = jest.fn(); + const thirdLayer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, + + // Private + operationType: 'date_histogram', + params: { + interval: 'auto', + }, + sourceField: 'timestamp', + }, + }, + }; + + const updateLayerSpy = jest.fn(); const instance = shallow( ); instance.find(EuiSwitch).prop('onChange')!({ target: { checked: true }, } as EuiSwitchEvent); - expect(setStateSpy).toHaveBeenCalled(); - const newState = setStateSpy.mock.calls[0][0]; - expect(newState).toHaveProperty('layers.third.columns.col1.params.interval', '30d'); + expect(updateLayerSpy).toHaveBeenCalled(); + const newLayer = updateLayerSpy.mock.calls[0][0]; + expect(newLayer).toHaveProperty('columns.col1.params.interval', '30d'); }); it('should force calendar values to 1', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({ @@ -551,67 +556,63 @@ describe('date_histogram', () => { value: '2', }, } as React.ChangeEvent); - expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('1w')); + expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('1w')); }); it('should display error if an invalid interval is specified', () => { - const setStateSpy = jest.fn(); - const testState = stateWithInterval('4quid'); + const updateLayerSpy = jest.fn(); + const testLayer = layerWithInterval('4quid'); const instance = shallow( ); expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); }); it('should not display error if interval value is blank', () => { - const setStateSpy = jest.fn(); - const testState = stateWithInterval('d'); + const updateLayerSpy = jest.fn(); + const testLayer = layerWithInterval('d'); const instance = shallow( ); expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeFalsy(); }); it('should display error if interval value is 0', () => { - const setStateSpy = jest.fn(); - const testState = stateWithInterval('0d'); + const updateLayerSpy = jest.fn(); + const testLayer = layerWithInterval('0d'); const instance = shallow( ); expect(instance.find('[data-test-subj="lensDateHistogramError"]').exists()).toBeTruthy(); }); it('should update the unit', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); instance.find('[data-test-subj="lensDateHistogramUnit"]').prop('onChange')!({ @@ -619,21 +620,20 @@ describe('date_histogram', () => { value: 'd', }, } as React.ChangeEvent); - expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('42d')); + expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('42d')); }); it('should update the value', () => { - const setStateSpy = jest.fn(); - const testState = stateWithInterval('42d'); + const updateLayerSpy = jest.fn(); + const testLayer = layerWithInterval('42d'); const instance = shallow( ); instance.find('[data-test-subj="lensDateHistogramValue"]').prop('onChange')!({ @@ -641,50 +641,48 @@ describe('date_histogram', () => { value: '9', }, } as React.ChangeEvent); - expect(setStateSpy).toHaveBeenCalledWith(stateWithInterval('9d')); + expect(updateLayerSpy).toHaveBeenCalledWith(layerWithInterval('9d')); }); it('should not render options if they are restricted', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); + + const indexPattern = { + ...indexPattern1, + fields: [ + { + ...indexPattern1.fields[0], + aggregationRestrictions: { + date_histogram: { + agg: 'date_histogram', + time_zone: 'UTC', + calendar_interval: '1h', + }, + }, + }, + ], + getFieldByName: getFieldByNameFactory([ + { + ...indexPattern1.fields[0], + aggregationRestrictions: { + date_histogram: { + agg: 'date_histogram', + time_zone: 'UTC', + calendar_interval: '1h', + }, + }, + }, + ]), + }; + const instance = shallow( ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index c48b9dad6c0d5b..cdd1ccad96a991 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -143,12 +143,8 @@ export const dateHistogramOperation: OperationDefinition< extended_bounds: JSON.stringify({}), }).toAst(); }, - paramEditor: ({ state, setState, currentColumn, layerId, dateRange, data }) => { - const field = - currentColumn && - state.indexPatterns[state.layers[layerId].indexPatternId].getFieldByName( - currentColumn.sourceField - ); + paramEditor: ({ layer, columnId, currentColumn, updateLayer, dateRange, data, indexPattern }) => { + const field = currentColumn && indexPattern.getFieldByName(currentColumn.sourceField); const intervalIsRestricted = field!.aggregationRestrictions && field!.aggregationRestrictions.date_histogram; @@ -167,14 +163,14 @@ export const dateHistogramOperation: OperationDefinition< const value = ev.target.checked ? data.search.aggs.calculateAutoTimeExpression({ from: fromDate, to: toDate }) || '1h' : autoInterval; - setState(updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value })); + updateLayer(updateColumnParam({ layer, columnId, paramName: 'interval', value })); } const setInterval = (newInterval: typeof interval) => { const isCalendarInterval = calendarOnlyIntervals.has(newInterval.unit); const value = `${isCalendarInterval ? '1' : newInterval.value}${newInterval.unit || 'd'}`; - setState(updateColumnParam({ state, layerId, currentColumn, paramName: 'interval', value })); + updateLayer(updateColumnParam({ layer, columnId, paramName: 'interval', value })); }; return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index bb8e52ba443a2f..cf57c35f6f68bf 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -7,12 +7,13 @@ import React from 'react'; import { mount } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import type { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; -import { FiltersIndexPatternColumn } from '.'; +import type { FiltersIndexPatternColumn } from '.'; import { filtersOperation } from '../index'; -import { IndexPatternPrivateState } from '../../../types'; +import type { IndexPatternLayer } from '../../../types'; +import { createMockedIndexPattern } from '../../../mocks'; import { FilterPopover } from './filter_popover'; const defaultProps = { @@ -22,6 +23,7 @@ const defaultProps = { dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), http: {} as HttpSetup, + indexPattern: createMockedIndexPattern(), }; // mocking random id generator function @@ -38,49 +40,40 @@ jest.mock('@elastic/eui', () => { }); describe('filters', () => { - let state: IndexPatternPrivateState; + let layer: IndexPatternLayer; const InlineOptions = filtersOperation.paramEditor!; beforeEach(() => { - state = { - indexPatternRefs: [], - indexPatterns: {}, - existingFields: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - label: 'filters', - dataType: 'document', - operationType: 'filters', - scale: 'ordinal', - isBucketed: true, - params: { - filters: [ - { - input: { query: 'bytes >= 1', language: 'kuery' }, - label: 'More than one', - }, - { - input: { query: 'src : 2', language: 'kuery' }, - label: '', - }, - ], + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'filters', + dataType: 'document', + operationType: 'filters', + scale: 'ordinal', + isBucketed: true, + params: { + filters: [ + { + input: { query: 'bytes >= 1', language: 'kuery' }, + label: 'More than one', }, - }, - col2: { - label: 'Count', - dataType: 'number', - isBucketed: false, - sourceField: 'Records', - operationType: 'count', - }, + { + input: { query: 'src : 2', language: 'kuery' }, + label: '', + }, + ], }, }, + col2: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, }, }; }); @@ -88,9 +81,9 @@ describe('filters', () => { describe('toEsAggsFn', () => { it('should reflect params correctly', () => { const esAggsFn = filtersOperation.toEsAggsFn( - state.layers.first.columns.col1 as FiltersIndexPatternColumn, + layer.columns.col1 as FiltersIndexPatternColumn, 'col1', - state.indexPatterns['1'] + createMockedIndexPattern() ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -133,15 +126,14 @@ describe('filters', () => { })); it('should update state when changing a filter', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -156,34 +148,29 @@ describe('filters', () => { }); }); instance.update(); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - filters: [ - { - input: { - query: 'dest : 5', - language: 'lucene', - }, - label: 'Dest5', - }, - { - input: { - language: 'kuery', - query: 'src : 2', - }, - label: '', - }, - ], + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + filters: [ + { + input: { + query: 'dest : 5', + language: 'lucene', + }, + label: 'Dest5', }, - }, + { + input: { + language: 'kuery', + query: 'src : 2', + }, + label: '', + }, + ], }, }, }, @@ -192,15 +179,14 @@ describe('filters', () => { describe('Modify filters', () => { it('should correctly show existing filters ', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); expect( @@ -218,15 +204,14 @@ describe('filters', () => { }); it('should remove filter', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -234,27 +219,22 @@ describe('filters', () => { .find('[data-test-subj="lns-customBucketContainer-remove"]') .at(2) .simulate('click'); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - filters: [ - { - input: { - language: 'kuery', - query: 'bytes >= 1', - }, - label: 'More than one', - }, - ], + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + filters: [ + { + input: { + language: 'kuery', + query: 'bytes >= 1', + }, + label: 'More than one', }, - }, + ], }, }, }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx index b6c0b565f9565f..8382aef7cc34ff 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx @@ -128,16 +128,14 @@ export const filtersOperation: OperationDefinition { - const indexPattern = state.indexPatterns[state.layers[layerId].indexPatternId]; + paramEditor: ({ layer, columnId, currentColumn, indexPattern, updateLayer, data }) => { const filters = currentColumn.params.filters; const setFilters = (newFilters: Filter[]) => - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'filters', value: newFilters, }) 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 1f19b4e7703137..6431dac7b381df 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 @@ -36,12 +36,7 @@ import { countOperation, CountIndexPatternColumn } from './count'; import { lastValueOperation, LastValueIndexPatternColumn } from './last_value'; import { OperationMetadata } from '../../../types'; import type { BaseIndexPatternColumn, ReferenceBasedIndexPatternColumn } from './column_types'; -import { - IndexPatternPrivateState, - IndexPattern, - IndexPatternField, - IndexPatternLayer, -} from '../../types'; +import { IndexPattern, IndexPatternField, IndexPatternLayer } from '../../types'; import { DateRange } from '../../../../common'; import { ExpressionAstFunction } from '../../../../../../../src/plugins/expressions/public'; import { DataPublicPluginStart } from '../../../../../../../src/plugins/data/public'; @@ -115,10 +110,10 @@ export { */ export interface ParamEditorProps { currentColumn: C; - state: IndexPatternPrivateState; - setState: (newState: IndexPatternPrivateState) => void; + layer: IndexPatternLayer; + updateLayer: (newLayer: IndexPatternLayer) => void; columnId: string; - layerId: string; + indexPattern: IndexPattern; uiSettings: IUiSettingsClient; storage: IStorageWrapper; savedObjectsClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index 6c896adfce9b16..817958aee54900 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -13,7 +13,7 @@ import { dataPluginMock } from '../../../../../../../src/plugins/data/public/moc import { createMockedIndexPattern } from '../../mocks'; import { LastValueIndexPatternColumn } from './last_value'; import { lastValueOperation } from './index'; -import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../types'; +import type { IndexPattern, IndexPatternLayer } from '../../types'; const defaultProps = { storage: {} as IStorageWrapper, @@ -22,52 +22,41 @@ const defaultProps = { dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), http: {} as HttpSetup, + indexPattern: { + ...createMockedIndexPattern(), + hasRestrictions: false, + } as IndexPattern, }; describe('last_value', () => { - let state: IndexPatternPrivateState; + let layer: IndexPatternLayer; const InlineOptions = lastValueOperation.paramEditor!; beforeEach(() => { - const indexPattern = createMockedIndexPattern(); - state = { - indexPatternRefs: [], - indexPatterns: { - '1': { - ...indexPattern, - hasRestrictions: false, - } as IndexPattern, - }, - existingFields: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, - operationType: 'terms', - params: { - orderBy: { type: 'alphabetical' }, - size: 3, - orderDirection: 'asc', - }, - sourceField: 'category', - }, - col2: { - label: 'Last value of a', - dataType: 'number', - isBucketed: false, - sourceField: 'a', - operationType: 'last_value', - params: { - sortField: 'datefield', - }, - }, + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col2: { + label: 'Last value of a', + dataType: 'number', + isBucketed: false, + sourceField: 'a', + operationType: 'last_value', + params: { + sortField: 'datefield', }, }, }, @@ -76,7 +65,7 @@ describe('last_value', () => { describe('toEsAggsFn', () => { it('should reflect params correctly', () => { - const lastValueColumn = state.layers.first.columns.col2 as LastValueIndexPatternColumn; + const lastValueColumn = layer.columns.col2 as LastValueIndexPatternColumn; const esAggsFn = lastValueOperation.toEsAggsFn( { ...lastValueColumn, params: { ...lastValueColumn.params } }, 'col1', @@ -345,15 +334,14 @@ describe('last_value', () => { describe('param editor', () => { it('should render current sortField', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -363,15 +351,14 @@ describe('last_value', () => { }); it('should update state when changing sortField', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -380,20 +367,15 @@ describe('last_value', () => { .find(EuiComboBox) .prop('onChange')!([{ label: 'datefield2', value: 'datefield2' }]); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col2: { - ...state.layers.first.columns.col2, - params: { - ...(state.layers.first.columns.col2 as LastValueIndexPatternColumn).params, - sortField: 'datefield2', - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + params: { + ...(layer.columns.col2 as LastValueIndexPatternColumn).params, + sortField: 'datefield2', }, }, }, @@ -403,10 +385,10 @@ describe('last_value', () => { describe('getErrorMessage', () => { let indexPattern: IndexPattern; - let layer: IndexPatternLayer; + let errorLayer: IndexPatternLayer; beforeEach(() => { indexPattern = createMockedIndexPattern(); - layer = { + errorLayer = { columns: { col1: { dataType: 'boolean', @@ -423,53 +405,55 @@ describe('last_value', () => { }; }); it('returns undefined if sourceField exists and sortField is of type date ', () => { - expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual(undefined); + expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual( + undefined + ); }); it('shows error message if the sourceField does not exist in index pattern', () => { - layer = { - ...layer, + errorLayer = { + ...errorLayer, columns: { col1: { - ...layer.columns.col1, + ...errorLayer.columns.col1, sourceField: 'notExisting', } as LastValueIndexPatternColumn, }, }; - expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([ 'Field notExisting was not found', ]); }); it('shows error message if the sortField does not exist in index pattern', () => { - layer = { - ...layer, + errorLayer = { + ...errorLayer, columns: { col1: { - ...layer.columns.col1, + ...errorLayer.columns.col1, params: { - ...layer.columns.col1.params, + ...errorLayer.columns.col1.params, sortField: 'notExisting', }, } as LastValueIndexPatternColumn, }, }; - expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([ 'Field notExisting was not found', ]); }); it('shows error message if the sortField is not date', () => { - layer = { - ...layer, + errorLayer = { + ...errorLayer, columns: { col1: { - ...layer.columns.col1, + ...errorLayer.columns.col1, params: { - ...layer.columns.col1.params, + ...errorLayer.columns.col1.params, sortField: 'bytes', }, } as LastValueIndexPatternColumn, }, }; - expect(lastValueOperation.getErrorMessage!(layer, 'col1', indexPattern)).toEqual([ + expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([ 'Field bytes is not a date field and cannot be used for sorting', ]); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 4cb2d876c83a13..7b5aee860654aa 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -186,12 +186,11 @@ export const lastValueOperation: OperationDefinition { - const currentIndexPattern = state.indexPatterns[state.layers[layerId].indexPatternId]; - const dateFields = getDateFields(currentIndexPattern); + paramEditor: ({ layer, updateLayer, columnId, currentColumn, indexPattern }) => { + const dateFields = getDateFields(indexPattern); const isSortFieldInvalid = !!getInvalidSortFieldMessage( currentColumn.params.sortField, - currentIndexPattern + indexPattern ); return ( <> @@ -228,11 +227,10 @@ export const lastValueOperation: OperationDefinition & React.MouseEvent; +const sourceField = 'MyField'; const defaultOptions = { storage: {} as IStorageWrapper, // need this for MAX_HISTOGRAM value @@ -64,75 +65,64 @@ const defaultOptions = { }, data: dataPluginMockValue, http: {} as HttpSetup, + indexPattern: { + id: '1', + title: 'my_index_pattern', + hasRestrictions: false, + fields: [{ name: sourceField, type: 'number', displayName: sourceField }], + getFieldByName: getFieldByNameFactory([ + { name: sourceField, type: 'number', displayName: sourceField }, + ]), + }, }; describe('ranges', () => { - let state: IndexPatternPrivateState; + let layer: IndexPatternLayer; const InlineOptions = rangeOperation.paramEditor!; - const sourceField = 'MyField'; const MAX_HISTOGRAM_VALUE = 100; const GRANULARITY_DEFAULT_VALUE = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / 2; const GRANULARITY_STEP = (MAX_HISTOGRAM_VALUE - MIN_HISTOGRAM_BARS) / SLICES; function setToHistogramMode() { - const column = state.layers.first.columns.col1 as RangeIndexPatternColumn; + const column = layer.columns.col1 as RangeIndexPatternColumn; column.dataType = 'number'; column.scale = 'interval'; column.params.type = MODES.Histogram; } function setToRangeMode() { - const column = state.layers.first.columns.col1 as RangeIndexPatternColumn; + const column = layer.columns.col1 as RangeIndexPatternColumn; column.dataType = 'string'; column.scale = 'ordinal'; column.params.type = MODES.Range; } - function getDefaultState(): IndexPatternPrivateState { + function getDefaultLayer(): IndexPatternLayer { return { - indexPatternRefs: [], - indexPatterns: { - '1': { - id: '1', - title: 'my_index_pattern', - hasRestrictions: false, - fields: [{ name: sourceField, type: 'number', displayName: sourceField }], - getFieldByName: getFieldByNameFactory([ - { name: sourceField, type: 'number', displayName: sourceField }, - ]), - }, - }, - existingFields: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - // Start with the histogram type - col1: { - label: sourceField, - dataType: 'number', - operationType: 'range', - scale: 'interval', - isBucketed: true, - sourceField, - params: { - type: MODES.Histogram, - ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }], - maxBars: 'auto', - }, - }, - col2: { - label: 'Count', - dataType: 'number', - isBucketed: false, - sourceField: 'Records', - operationType: 'count', - }, + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + // Start with the histogram type + col1: { + label: sourceField, + dataType: 'number', + operationType: 'range', + scale: 'interval', + isBucketed: true, + sourceField, + params: { + type: MODES.Histogram, + ranges: [{ from: 0, to: DEFAULT_INTERVAL, label: '' }], + maxBars: 'auto', }, }, + col2: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, }, }; } @@ -142,7 +132,7 @@ describe('ranges', () => { }); beforeEach(() => { - state = getDefaultState(); + layer = getDefaultLayer(); }); describe('toEsAggsFn', () => { @@ -150,7 +140,7 @@ describe('ranges', () => { it('should reflect params correctly', () => { const esAggsFn = rangeOperation.toEsAggsFn( - state.layers.first.columns.col1 as RangeIndexPatternColumn, + layer.columns.col1 as RangeIndexPatternColumn, 'col1', {} as IndexPattern ); @@ -189,10 +179,10 @@ describe('ranges', () => { }); it('should set maxBars param if provided', () => { - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.maxBars = 10; + (layer.columns.col1 as RangeIndexPatternColumn).params.maxBars = 10; const esAggsFn = rangeOperation.toEsAggsFn( - state.layers.first.columns.col1 as RangeIndexPatternColumn, + layer.columns.col1 as RangeIndexPatternColumn, 'col1', {} as IndexPattern ); @@ -211,7 +201,7 @@ describe('ranges', () => { setToRangeMode(); const esAggsFn = rangeOperation.toEsAggsFn( - state.layers.first.columns.col1 as RangeIndexPatternColumn, + layer.columns.col1 as RangeIndexPatternColumn, 'col1', {} as IndexPattern ); @@ -225,12 +215,12 @@ describe('ranges', () => { it('should include custom labels', () => { setToRangeMode(); - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges = [ + (layer.columns.col1 as RangeIndexPatternColumn).params.ranges = [ { from: 0, to: 100, label: 'customlabel' }, ]; const esAggsFn = rangeOperation.toEsAggsFn( - state.layers.first.columns.col1 as RangeIndexPatternColumn, + layer.columns.col1 as RangeIndexPatternColumn, 'col1', {} as IndexPattern ); @@ -276,36 +266,30 @@ describe('ranges', () => { describe('paramEditor', () => { describe('Modify intervals in basic mode', () => { beforeEach(() => { - state = getDefaultState(); + layer = getDefaultLayer(); }); it('should start update the state with the default maxBars value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); mount( ); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - maxBars: GRANULARITY_DEFAULT_VALUE, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE, }, }, }, @@ -313,16 +297,15 @@ describe('ranges', () => { }); it('should update state when changing Max bars number', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -343,20 +326,15 @@ describe('ranges', () => { jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); }); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - maxBars: MAX_HISTOGRAM_VALUE, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + maxBars: MAX_HISTOGRAM_VALUE, }, }, }, @@ -364,16 +342,15 @@ describe('ranges', () => { }); it('should update the state using the plus or minus buttons by the step amount', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -389,20 +366,15 @@ describe('ranges', () => { jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); }); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - maxBars: GRANULARITY_DEFAULT_VALUE - GRANULARITY_STEP, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE - GRANULARITY_STEP, }, }, }, @@ -417,25 +389,19 @@ describe('ranges', () => { jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); }); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - maxBars: GRANULARITY_DEFAULT_VALUE, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + maxBars: GRANULARITY_DEFAULT_VALUE, }, }, }, }); - // }); }); }); @@ -446,16 +412,15 @@ describe('ranges', () => { beforeEach(() => setToRangeMode()); it('should show one range interval to start with', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -463,16 +428,15 @@ describe('ranges', () => { }); it('should add a new range', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -495,23 +459,18 @@ describe('ranges', () => { } as React.ChangeEvent); jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - ranges: [ - { from: 0, to: DEFAULT_INTERVAL, label: '' }, - { from: 50, to: Infinity, label: '' }, - ], - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + ranges: [ + { from: 0, to: DEFAULT_INTERVAL, label: '' }, + { from: 50, to: Infinity, label: '' }, + ], }, }, }, @@ -520,16 +479,15 @@ describe('ranges', () => { }); it('should add a new range with custom label', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -552,23 +510,18 @@ describe('ranges', () => { } as React.ChangeEvent); jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - ranges: [ - { from: 0, to: DEFAULT_INTERVAL, label: '' }, - { from: DEFAULT_INTERVAL, to: Infinity, label: 'customlabel' }, - ], - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + ranges: [ + { from: 0, to: DEFAULT_INTERVAL, label: '' }, + { from: DEFAULT_INTERVAL, to: Infinity, label: 'customlabel' }, + ], }, }, }, @@ -577,16 +530,15 @@ describe('ranges', () => { }); it('should open a popover to edit an existing range', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -607,20 +559,15 @@ describe('ranges', () => { } as React.ChangeEvent); jest.advanceTimersByTime(TYPING_DEBOUNCE_TIME * 4); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...state.layers.first.columns.col1.params, - ranges: [{ from: 0, to: 50, label: '' }], - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...layer.columns.col1.params, + ranges: [{ from: 0, to: 50, label: '' }], }, }, }, @@ -629,16 +576,15 @@ describe('ranges', () => { }); it('should not accept invalid ranges', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -670,10 +616,10 @@ describe('ranges', () => { }); it('should be possible to remove a range if multiple', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); // Add an extra range - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ + (layer.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ from: DEFAULT_INTERVAL, to: 2 * DEFAULT_INTERVAL, label: '', @@ -682,11 +628,10 @@ describe('ranges', () => { const instance = mount( ); @@ -709,10 +654,10 @@ describe('ranges', () => { }); it('should handle correctly open ranges when saved', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); // Add an extra open range: - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ + (layer.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ from: null, to: null, label: '', @@ -721,11 +666,10 @@ describe('ranges', () => { const instance = mount( ); @@ -749,21 +693,21 @@ describe('ranges', () => { }); it('should correctly handle the default formatter for the field', () => { - const setStateSpy = jest.fn(); - - // set a default formatter for the sourceField used - state.indexPatterns['1'].fieldFormatMap = { - MyField: { id: 'custom', params: {} }, - }; + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -773,15 +717,10 @@ describe('ranges', () => { }); it('should correctly pick the dimension formatter for the field', () => { - const setStateSpy = jest.fn(); - - // set a default formatter for the sourceField used - state.indexPatterns['1'].fieldFormatMap = { - MyField: { id: 'custom', params: {} }, - }; + const updateLayerSpy = jest.fn(); // now set a format on the range operation - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.format = { + (layer.columns.col1 as RangeIndexPatternColumn).params.format = { id: 'bytes', params: { decimals: 0 }, }; @@ -789,11 +728,16 @@ describe('ranges', () => { const instance = mount( ); @@ -803,25 +747,24 @@ describe('ranges', () => { }); it('should not update the state on mount', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); mount( ); - expect(setStateSpy.mock.calls.length).toBe(0); + expect(updateLayerSpy.mock.calls.length).toBe(0); }); it('should not reset formatters when switching between custom ranges and auto histogram', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); // now set a format on the range operation - (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.format = { + (layer.columns.col1 as RangeIndexPatternColumn).params.format = { id: 'custom', params: { decimals: 3 }, }; @@ -829,11 +772,10 @@ describe('ranges', () => { const instance = mount( ); @@ -842,7 +784,7 @@ describe('ranges', () => { instance.find(EuiLink).first().prop('onClick')!({} as ReactMouseEvent); }); - expect(setStateSpy.mock.calls[1][0].layers.first.columns.col1.params.format).toEqual({ + expect(updateLayerSpy.mock.calls[1][0].columns.col1.params.format).toEqual({ id: 'custom', params: { decimals: 3 }, }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 062e2afb8a5bf0..2ba8f5febce5bd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -16,7 +16,6 @@ import { RangeEditor } from './range_editor'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { updateColumnParam } from '../../layer_helpers'; -import { mergeLayer } from '../../../state_helpers'; import { supportedFormats } from '../../../format_column'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; @@ -173,8 +172,15 @@ export const rangeOperation: OperationDefinition { - const indexPattern = state.indexPatterns[state.layers[layerId].indexPatternId]; + paramEditor: ({ + layer, + columnId, + currentColumn, + updateLayer, + indexPattern, + uiSettings, + data, + }) => { const currentField = indexPattern.getFieldByName(currentColumn.sourceField); const numberFormat = currentColumn.params.format; const numberFormatterPattern = @@ -198,11 +204,10 @@ export const rangeOperation: OperationDefinition { - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName, value, }) @@ -217,29 +222,24 @@ export const rangeOperation: OperationDefinition isSortableByColumn(column)) - .map(([columnId, column]) => { + const orderOptions = Object.entries(layer.columns) + .filter(([sortId, column]) => isSortableByColumn(column)) + .map(([sortId, column]) => { return { - value: toValue({ type: 'column', columnId }), + value: toValue({ type: 'column', columnId: sortId }), text: column.label, }; }); @@ -220,11 +219,10 @@ export const termsOperation: OperationDefinition { - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'size', value, }) @@ -263,11 +261,10 @@ export const termsOperation: OperationDefinition - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'otherBucket', value: e.target.checked, }) @@ -284,11 +281,10 @@ export const termsOperation: OperationDefinition - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'missingBucket', value: e.target.checked, }) @@ -312,11 +308,10 @@ export const termsOperation: OperationDefinition) => - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'orderBy', value: fromValue(e.target.value), }) @@ -353,11 +348,10 @@ export const termsOperation: OperationDefinition) => - setState( + updateLayer( updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName: 'orderDirection', value: e.target.value as 'asc' | 'desc', }) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index fc8f8534bcfb26..eb78bb3ffebff8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -8,14 +8,14 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { shallow, mount } from 'enzyme'; import { EuiRange, EuiSelect, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; -import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; -import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import type { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks'; import { createMockedIndexPattern } from '../../../mocks'; import { ValuesRangeInput } from './values_range_input'; -import { TermsIndexPatternColumn } from '.'; +import type { TermsIndexPatternColumn } from '.'; import { termsOperation } from '../index'; -import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from '../../../types'; +import { IndexPattern, IndexPatternLayer } from '../../../types'; const defaultProps = { storage: {} as IStorageWrapper, @@ -24,48 +24,36 @@ const defaultProps = { dateRange: { fromDate: 'now-1d', toDate: 'now' }, data: dataPluginMock.createStartContract(), http: {} as HttpSetup, + indexPattern: createMockedIndexPattern(), }; describe('terms', () => { - let state: IndexPatternPrivateState; + let layer: IndexPatternLayer; const InlineOptions = termsOperation.paramEditor!; beforeEach(() => { - state = { - indexPatternRefs: [], - indexPatterns: { - '1': { - hasRestrictions: false, - } as IndexPattern, - }, - existingFields: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, - operationType: 'terms', - params: { - orderBy: { type: 'alphabetical' }, - size: 3, - orderDirection: 'asc', - }, - sourceField: 'category', - }, - col2: { - label: 'Count', - dataType: 'number', - isBucketed: false, - sourceField: 'Records', - operationType: 'count', - }, + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', }, + sourceField: 'category', + }, + col2: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', }, }, }; @@ -73,7 +61,7 @@ describe('terms', () => { describe('toEsAggsFn', () => { it('should reflect params correctly', () => { - const termsColumn = state.layers.first.columns.col1 as TermsIndexPatternColumn; + const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; const esAggsFn = termsOperation.toEsAggsFn( { ...termsColumn, params: { ...termsColumn.params, otherBucket: true } }, 'col1', @@ -92,7 +80,7 @@ describe('terms', () => { }); it('should not enable missing bucket if other bucket is not set', () => { - const termsColumn = state.layers.first.columns.col1 as TermsIndexPatternColumn; + const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; const esAggsFn = termsOperation.toEsAggsFn( { ...termsColumn, @@ -370,7 +358,7 @@ describe('terms', () => { it('should use the default size when there is an existing bucket', () => { const termsColumn = termsOperation.buildColumn({ indexPattern: createMockedIndexPattern(), - layer: state.layers.first, + layer, field: { aggregatable: true, searchable: true, @@ -526,15 +514,14 @@ describe('terms', () => { describe('param editor', () => { it('should render current other bucket value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -546,15 +533,18 @@ describe('terms', () => { }); it('should hide other bucket setting for rollups', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -562,15 +552,14 @@ describe('terms', () => { }); it('should disable missing bucket setting as long as other bucket is not set', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -582,23 +571,22 @@ describe('terms', () => { }); it('should enable missing bucket setting as long as other bucket is set', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -610,15 +598,14 @@ describe('terms', () => { }); it('should update state when clicking other bucket toggle', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -631,20 +618,15 @@ describe('terms', () => { }, } as EuiSwitchEvent); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params, - otherBucket: true, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...(layer.columns.col1 as TermsIndexPatternColumn).params, + otherBucket: true, }, }, }, @@ -652,15 +634,14 @@ describe('terms', () => { }); it('should render current order by value and options', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -675,15 +656,14 @@ describe('terms', () => { }); it('should update state with the order by value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -696,22 +676,17 @@ describe('terms', () => { }, } as React.ChangeEvent); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params, - orderBy: { - type: 'column', - columnId: 'col2', - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...(layer.columns.col1 as TermsIndexPatternColumn).params, + orderBy: { + type: 'column', + columnId: 'col2', }, }, }, @@ -720,15 +695,14 @@ describe('terms', () => { }); it('should render current order direction value and options', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -741,15 +715,14 @@ describe('terms', () => { }); it('should update state with the order direction value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = shallow( ); @@ -762,20 +735,15 @@ describe('terms', () => { }, } as React.ChangeEvent); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params, - orderDirection: 'desc', - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...(layer.columns.col1 as TermsIndexPatternColumn).params, + orderDirection: 'desc', }, }, }, @@ -783,15 +751,14 @@ describe('terms', () => { }); it('should render current size value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -799,15 +766,14 @@ describe('terms', () => { }); it('should update state with the size value', () => { - const setStateSpy = jest.fn(); + const updateLayerSpy = jest.fn(); const instance = mount( ); @@ -815,20 +781,15 @@ describe('terms', () => { instance.find(ValuesRangeInput).prop('onChange')!(7); }); - expect(setStateSpy).toHaveBeenCalledWith({ - ...state, - layers: { - first: { - ...state.layers.first, - columns: { - ...state.layers.first.columns, - col1: { - ...state.layers.first.columns.col1, - params: { - ...(state.layers.first.columns.col1 as TermsIndexPatternColumn).params, - size: 7, - }, - }, + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col1: { + ...layer.columns.col1, + params: { + ...(layer.columns.col1 as TermsIndexPatternColumn).params, + size: 7, }, }, }, @@ -837,7 +798,6 @@ describe('terms', () => { }); describe('getErrorMessage', () => { let indexPattern: IndexPattern; - let layer: IndexPatternLayer; beforeEach(() => { indexPattern = createMockedIndexPattern(); layer = { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index 93447053a6029c..bb09474798fd4d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -18,7 +18,7 @@ import { operationDefinitionMap, OperationType } from '../operations'; import { TermsIndexPatternColumn } from './definitions/terms'; import { DateHistogramIndexPatternColumn } from './definitions/date_histogram'; import { AvgIndexPatternColumn } from './definitions/metrics'; -import type { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from '../types'; +import type { IndexPattern, IndexPatternLayer } from '../types'; import { documentField } from '../document_field'; import { getFieldByNameFactory } from '../pure_helpers'; import { generateId } from '../../id_generator'; @@ -1266,31 +1266,19 @@ describe('state_helpers', () => { sourceField: 'timestamp', }; - const state: IndexPatternPrivateState = { - indexPatternRefs: [], - existingFields: {}, - indexPatterns: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { + expect( + updateColumnParam({ + layer: { indexPatternId: '1', columnOrder: ['col1'], columns: { col1: currentColumn, }, }, - }, - }; - - expect( - updateColumnParam({ - state, - layerId: 'first', - currentColumn, + columnId: 'col1', paramName: 'interval', value: 'M', - }).layers.first.columns.col1 + }).columns.col1 ).toEqual({ ...currentColumn, params: { interval: 'M' }, @@ -1307,31 +1295,19 @@ describe('state_helpers', () => { sourceField: 'bytes', }; - const state: IndexPatternPrivateState = { - indexPatternRefs: [], - existingFields: {}, - indexPatterns: {}, - currentIndexPatternId: '1', - isFirstExistenceFetch: false, - layers: { - first: { + expect( + updateColumnParam({ + layer: { indexPatternId: '1', columnOrder: ['col1'], columns: { col1: currentColumn, }, }, - }, - }; - - expect( - updateColumnParam({ - state, - layerId: 'first', - currentColumn, + columnId: 'col1', paramName: 'format', value: { id: 'bytes' }, - }).layers.first.columns.col1 + }).columns.col1 ).toEqual({ ...currentColumn, params: { format: { id: 'bytes' } }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index b16418d44ba330..1619ad907fffca 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -13,14 +13,8 @@ import { IndexPatternColumn, RequiredReference, } from './definitions'; -import type { - IndexPattern, - IndexPatternField, - IndexPatternLayer, - IndexPatternPrivateState, -} from '../types'; +import type { IndexPattern, IndexPatternField, IndexPatternLayer } from '../types'; import { getSortScoreByPriority } from './operations'; -import { mergeLayer } from '../state_helpers'; import { generateId } from '../../id_generator'; import { ReferenceBasedIndexPatternColumn } from './definitions/column_types'; @@ -390,40 +384,29 @@ export function getMetricOperationTypes(field: IndexPatternField) { } export function updateColumnParam({ - state, - layerId, - currentColumn, + layer, + columnId, paramName, value, }: { - state: IndexPatternPrivateState; - layerId: string; - currentColumn: C; + layer: IndexPatternLayer; + columnId: string; paramName: string; value: unknown; -}): IndexPatternPrivateState { - const columnId = Object.entries(state.layers[layerId].columns).find( - ([_columnId, column]) => column === currentColumn - )![0]; - - const layer = state.layers[layerId]; - - return mergeLayer({ - state, - layerId, - newLayer: { - columns: { - ...layer.columns, - [columnId]: { - ...currentColumn, - params: { - ...currentColumn.params, - [paramName]: value, - }, +}): IndexPatternLayer { + return { + ...layer, + columns: { + ...layer.columns, + [columnId]: { + ...layer.columns[columnId], + params: { + ...layer.columns[columnId].params, + [paramName]: value, }, }, - }, - }); + } as Record, + }; } function adjustColumnReferencesForChangedColumn( From 5bcef6f82e9852a0ff73286381c11d520479ae49 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 21 Dec 2020 16:31:56 +0000 Subject: [PATCH 012/100] [ML] Fix displaying of setup errors in recognizer wizard (#86430) * [ML] Fix displaying of setup errors in recognizer wizard * add saved object error * sharing error message extractor Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/ml/common/index.ts | 1 + x-pack/plugins/ml/common/types/modules.ts | 14 +++----------- .../jobs/new_job/recognize/components/job_item.tsx | 5 +++-- .../recognize/components/kibana_objects.tsx | 3 ++- .../plugins/uptime/public/state/api/ml_anomaly.ts | 3 ++- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index d527a9a9780ad0..85770c856ca842 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -8,3 +8,4 @@ export { SearchResponse7 } from './types/es_client'; export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from './constants/anomalies'; export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; export { composeValidators, patternValidator } from './util/validators'; +export { extractErrorMessage } from './util/errors'; diff --git a/x-pack/plugins/ml/common/types/modules.ts b/x-pack/plugins/ml/common/types/modules.ts index 38f0c8cb9c1176..3c5d784d4a9e3c 100644 --- a/x-pack/plugins/ml/common/types/modules.ts +++ b/x-pack/plugins/ml/common/types/modules.ts @@ -5,6 +5,7 @@ */ import { SavedObjectAttributes } from 'kibana/public'; import { Datafeed, Job } from './anomaly_detection_jobs'; +import { ErrorType } from '../util/errors'; export interface ModuleJob { id: string; @@ -63,22 +64,13 @@ export interface KibanaObjectResponse extends ResultItem { error?: any; } -export interface SetupError { - body: string; - msg: string; - path: string; - query: {}; - response: string; - statusCode: number; -} - export interface DatafeedResponse extends ResultItem { started: boolean; - error?: SetupError; + error?: ErrorType; } export interface JobResponse extends ResultItem { - error?: SetupError; + error?: ErrorType; } export interface DataRecognizerConfigResponse { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx index 8dab25bf492f42..7e972b010cdd7a 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/job_item.tsx @@ -21,6 +21,7 @@ import { ModuleJobUI } from '../page'; import { SETUP_RESULTS_WIDTH } from './module_jobs'; import { tabColor } from '../../../../../../common/util/group_color_utils'; import { JobOverride } from '../../../../../../common/types/modules'; +import { extractErrorMessage } from '../../../../../../common/util/errors'; interface JobItemProps { job: ModuleJobUI; @@ -94,13 +95,13 @@ export const JobItem: FC = memo( {setupResult && setupResult.error && ( - {setupResult.error.msg} + {extractErrorMessage(setupResult.error)} )} {datafeedResult && datafeedResult.error && ( - {datafeedResult.error.msg} + {extractErrorMessage(datafeedResult.error)} )} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx index f8ca7926ad7d66..18647e20937577 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/components/kibana_objects.tsx @@ -18,6 +18,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { KibanaObjectUi } from '../page'; +import { extractErrorMessage } from '../../../../../../common/util/errors'; export interface KibanaObjectItemProps { objectType: string; @@ -57,7 +58,7 @@ export const KibanaObjects: FC = memo( {success === false && error !== undefined && ( - {error.message} + {extractErrorMessage(error)} )} diff --git a/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts index fa3d7ed834a9c1..5c0eb0bff07ebc 100644 --- a/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/api/ml_anomaly.ts @@ -13,6 +13,7 @@ import { JobExistResult, MlCapabilitiesResponse, } from '../../../../../plugins/ml/public'; +import { extractErrorMessage } from '../../../../../plugins/ml/common'; import { CreateMLJobSuccess, DeleteJobResults, @@ -62,7 +63,7 @@ export const createMLJob = async ({ }; } else { const { error } = jobResponse; - throw new Error(error?.msg); + throw new Error(extractErrorMessage(error)); } } else { return null; From 8ecd726b5c3dc1c4b2283f0d4f8db5695f8b3cc2 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 21 Dec 2020 17:32:19 +0100 Subject: [PATCH 013/100] [Lens] Fix esaggs missing default time field scenario in Lens (#85754) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/data/common/index_patterns/index_pattern.stub.ts | 1 + src/plugins/data/common/search/aggs/buckets/date_histogram.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/index_patterns/index_pattern.stub.ts b/src/plugins/data/common/index_patterns/index_pattern.stub.ts index e7384e09494aa1..c47a8b605ada34 100644 --- a/src/plugins/data/common/index_patterns/index_pattern.stub.ts +++ b/src/plugins/data/common/index_patterns/index_pattern.stub.ts @@ -25,6 +25,7 @@ export const stubIndexPattern: IIndexPattern = { fields: stubFields, title: 'logstash-*', timeFieldName: '@timestamp', + getTimeField: () => ({ name: '@timestamp', type: 'date' }), }; export const stubIndexPatternWithFields: IIndexPattern = { diff --git a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts index ba79a4264d6039..6b7f1522d19ad4 100644 --- a/src/plugins/data/common/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/common/search/aggs/buckets/date_histogram.ts @@ -149,7 +149,7 @@ export const getDateHistogramBucketAgg = ({ type: 'field', filterFieldTypes: KBN_FIELD_TYPES.DATE, default(agg: IBucketDateHistogramAggConfig) { - return agg.getIndexPattern().timeFieldName; + return agg.getIndexPattern().getTimeField?.()?.name; }, onChange(agg: IBucketDateHistogramAggConfig) { if (isAutoInterval(get(agg, 'params.interval')) && !agg.fieldIsTimeField()) { From 805fc4b95cd08b66eb247aa185709eef4fa88718 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 21 Dec 2020 11:50:28 -0500 Subject: [PATCH 014/100] Fix more sorts by adding unmapped_type (#85837) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/kibana_monitoring/collectors/lib/fetch_es_usage.ts | 1 + .../kibana_monitoring/collectors/lib/fetch_license_type.ts | 1 + .../monitoring/server/lib/alerts/fetch_legacy_alerts.test.ts | 2 +- .../plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts | 1 + .../server/lib/alerts/fetch_missing_monitoring_data.ts | 1 + x-pack/plugins/monitoring/server/lib/beats/get_beats.ts | 2 +- 6 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_es_usage.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_es_usage.ts index de0a1b8f99d968..9f054f4f95adfe 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_es_usage.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_es_usage.ts @@ -58,6 +58,7 @@ export async function fetchESUsage( { timestamp: { order: 'desc', + unmapped_type: 'long', }, }, ], diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_license_type.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_license_type.ts index f7b8b72637b1ff..4e132eac1fba47 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_license_type.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/lib/fetch_license_type.ts @@ -27,6 +27,7 @@ export async function fetchLicenseType( { timestamp: { order: 'desc', + unmapped_type: 'long', }, }, ], diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.test.ts index 50550170518164..a5331beeae067a 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.test.ts @@ -70,7 +70,7 @@ describe('fetchLegacyAlerts', () => { ], body: { size, - sort: [{ timestamp: { order: 'desc' } }], + sort: [{ timestamp: { order: 'desc', unmapped_type: 'long' } }], query: { bool: { minimum_should_match: 1, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts index 0ea37b4ac4daa5..c6416a992276ee 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_legacy_alerts.ts @@ -28,6 +28,7 @@ export async function fetchLegacyAlerts( { timestamp: { order: 'desc', + unmapped_type: 'long', }, }, ], diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts index 30706a0b3c922c..1bfe3a367fc951 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts @@ -97,6 +97,7 @@ export async function fetchMissingMonitoringData( { timestamp: { order: 'desc', + unmapped_type: 'long', }, }, ], diff --git a/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts b/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts index beda4334b49374..aa5ef81a8de339 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts @@ -151,7 +151,7 @@ export async function getBeats(req: LegacyRequest, beatsIndexPattern: string, cl inner_hits: { name: 'earliest', size: 1, - sort: [{ 'beats_stats.timestamp': 'asc' }], + sort: [{ 'beats_stats.timestamp': { order: 'asc', unmapped_type: 'long' } }], }, }, sort: [ From f349d0a398eb7944957b3fa45647be0b1fd62cb6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 21 Dec 2020 16:56:35 +0000 Subject: [PATCH 015/100] [ML] Fix cloning of partition field in per-partition categorization jobs (#86635) * [ML] Fix cloning of partition field in per-partition categorization jobs * fixing cloned bucket span --- .../job_creator/categorization_job_creator.ts | 59 ++++++++++++------- .../new_job/common/job_creator/job_creator.ts | 2 +- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts index a0f2832dc9f97a..aaf20ff1571eea 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts @@ -160,26 +160,6 @@ export class CategorizationJobCreator extends JobCreator { return this._categorizationAnalyzer; } - public cloneFromExistingJob(job: Job, datafeed: Datafeed) { - this._overrideConfigs(job, datafeed); - this.createdBy = CREATED_BY_LABEL.CATEGORIZATION; - const detectors = getRichDetectors(job, datafeed, this.additionalFields, false); - - const dtr = detectors[0]; - if (detectors.length && dtr.agg !== null && dtr.field !== null) { - this._detectorType = - dtr.agg.id === ML_JOB_AGGREGATION.COUNT - ? ML_JOB_AGGREGATION.COUNT - : ML_JOB_AGGREGATION.RARE; - - const bs = job.analysis_config.bucket_span; - this.setDetectorType(this._detectorType); - // set the bucketspan back to the original value - // as setDetectorType applies a default - this.bucketSpan = bs; - } - } - public get categorizationPerPartitionField() { return this._partitionFieldName; } @@ -204,4 +184,43 @@ export class CategorizationJobCreator extends JobCreator { } } } + + // override the setter and getter for the per-partition toggle + // so we can remove the partition field in the wizard when + // per-partition categorization is disabled. + public get perPartitionCategorization() { + return this._job_config.analysis_config.per_partition_categorization?.enabled === true; + } + + public set perPartitionCategorization(enabled: boolean) { + this._initPerPartitionCategorization(); + this._job_config.analysis_config.per_partition_categorization!.enabled = enabled; + if (enabled === false) { + this.categorizationPerPartitionField = null; + } + } + + public cloneFromExistingJob(job: Job, datafeed: Datafeed) { + this._overrideConfigs(job, datafeed); + this.createdBy = CREATED_BY_LABEL.CATEGORIZATION; + const detectors = getRichDetectors(job, datafeed, this.additionalFields, false); + + const dtr = detectors[0]; + if (dtr !== undefined && dtr.agg !== null && dtr.field !== null) { + const detectorType = + dtr.agg.id === ML_JOB_AGGREGATION.COUNT + ? ML_JOB_AGGREGATION.COUNT + : ML_JOB_AGGREGATION.RARE; + + const bs = job.analysis_config.bucket_span; + this.setDetectorType(detectorType); + if (dtr.partitionField !== null) { + this.categorizationPerPartitionField = dtr.partitionField.id; + } + + // set the bucketspan back to the original value + // as setDetectorType applies a default + this.bucketSpan = bs; + } + } } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index ba03e86724a150..c3b4471269bb86 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -631,7 +631,7 @@ export class JobCreator { return JSON.stringify(this._datafeed_config, null, 2); } - private _initPerPartitionCategorization() { + protected _initPerPartitionCategorization() { if (this._job_config.analysis_config.per_partition_categorization === undefined) { this._job_config.analysis_config.per_partition_categorization = {}; } From 1e9e0400a034fb003553234bb517a2385f66a10a Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Mon, 21 Dec 2020 11:59:03 -0500 Subject: [PATCH 016/100] Added docs for fleet UI extensions (#86654) --- .../fleet/dev_docs/fleet_ui_extensions.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md diff --git a/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md b/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md new file mode 100644 index 00000000000000..a3d82356af14f8 --- /dev/null +++ b/x-pack/plugins/fleet/dev_docs/fleet_ui_extensions.md @@ -0,0 +1,84 @@ +# Fleet UI Extensions + +Fleet's Kibana UI supports two types of UI extensions: + +- Custom UI components +- Navigation redirection + +More information about these can be found below. + + +## Custom UI Components + +Custom UI component extension can be registered with Fleet using the Plugin's `#start()` exposed interface, which contains a method named `registerExtension()`. In order to take advantage of these extension points, a Kibana plugin must set a dependency on Fleet so that the interface can be made available. + +Here is an example Security Solution's Endpoint takes advantage of this functionality to register several custom UI extensions: + +```typescript +export class Plugin { + //... + start(core: CoreStart, startDep: StartDependencies) { + const { registerExtension } = startDep.fleet; + + registerExtension({ + package: 'endpoint', + view: 'package-policy-edit', + component: getLazyEndpointPolicyEditExtension(core, plugins), + }); + + registerExtension({ + package: 'endpoint', + view: 'package-policy-create', + component: LazyEndpointPolicyCreateExtension, + }); + + registerExtension({ + package: 'endpoint', + view: 'package-detail-custom', + component: getLazyEndpointPackageCustomExtension(core, plugins), + }); + } + //... +} +``` + +> The code above lives in `x-pack/plugins/security_solution/public/plugin.tsx` + +For a list of supported Fleet UI extensions, see the `UIExtensionPoint` and associated Union types defined here: `x-pack/plugins/fleet/public/applications/fleet/types/ui_extensions.ts`. + + + + +## Navigation Redirection + +In order to support better user flows, some of Fleet's pages support changing the behaviour of the certain links and/or button clicks and to where they redirect if a user clicks on it. This type of UI extension does not need a Plugin level dependency on Fleet - it utilizes Route state via `react-router` along with Kibana's `core.application.navigateToApp()` method thus any kibana plugin can take advantage of it. + +Here is an example of how to create a link that redirects the user to Fleet's Agent Policy detail page with the Agent Enroll flyout opened, and once the user clicks "done", they are redirected back to the originating page: + +```typescript jsx +const LinkToAgentEnroll = () => { + const { services } = useKibana(); + const handleLinkClick = useCallback((ev) => { + ev.preventDefault(); + + const fleetPolicyPageRouteState: AgentPolicyDetailsDeployAgentAction = { + onDoneNavigateTo: [ + 'my-app-plugin-id', + { path: 'the/page/I/am/currently/on' }, + ], + }; + + services.application.navigateTo( + 'fleet', + { + path: '#/policies/123-some-uuid-321?openEnrollmentFlyout=true', + state: fleetPolicyPageRouteState + } + ); + }, [services.application.navigateTo]); + + return Enroll an agent +} +``` + +For a list of supported Fleet pages, see the type `AnyIntraAppRouteState` and its associated Union types in `x-pack/plugins/fleet/public/applications/fleet/types/intra_app_route_state.ts` From 256c8cdffb17f6030b657f18189b20e857a1dcf4 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 21 Dec 2020 11:26:35 -0600 Subject: [PATCH 017/100] skip histogram brushing, #86602 --- test/functional/apps/discover/_discover.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_discover.ts b/test/functional/apps/discover/_discover.ts index e52c33078029a3..1b333c377f7774 100644 --- a/test/functional/apps/discover/_discover.ts +++ b/test/functional/apps/discover/_discover.ts @@ -110,7 +110,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('should modify the time range when the histogram is brushed', async function () { + it.skip('should modify the time range when the histogram is brushed', async function () { await PageObjects.timePicker.setDefaultAbsoluteRange(); await PageObjects.discover.brushHistogram(); await PageObjects.discover.waitUntilSearchingHasFinished(); From 7de243e7fd328da7361eeb50b8cc97955babbb42 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 21 Dec 2020 19:54:45 +0100 Subject: [PATCH 018/100] [ML] Fix time range adjustment for the swim lane causing the infinite loop update (#86461) * [ML] fix swim lane time selection adjustment * [ML] fix adjustment * [ML] fix tooManyBuckets condition * [ML] fix typo --- .../contexts/kibana/__mocks__/index.ts | 1 + .../kibana/__mocks__/use_timefilter.ts | 40 ++++++ .../explorer_charts_container_service.js | 5 +- .../explorer/hooks/use_selected_cells.test.ts | 133 ++++++++++++++++++ .../explorer/hooks/use_selected_cells.ts | 80 +++++------ .../application/routing/routes/explorer.tsx | 2 +- 6 files changed, 219 insertions(+), 42 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/use_timefilter.ts create mode 100644 x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/index.ts b/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/index.ts index 7051abe6dc34ec..dd405ddcc04fa1 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/index.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/index.ts @@ -5,3 +5,4 @@ */ export { useMlKibana } from './kibana_context'; +export { useTimefilter } from './use_timefilter'; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/use_timefilter.ts b/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/use_timefilter.ts new file mode 100644 index 00000000000000..949de51cf2c2de --- /dev/null +++ b/x-pack/plugins/ml/public/application/contexts/kibana/__mocks__/use_timefilter.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TimefilterContract } from '../../../../../../../../src/plugins/data/public'; +import { Observable } from 'rxjs'; + +/** + * Copy from {@link '../../../../../../../../src/plugins/data/public/query/timefilter/timefilter_service.mock'} + */ +const timefilterMock: jest.Mocked = { + isAutoRefreshSelectorEnabled: jest.fn(), + isTimeRangeSelectorEnabled: jest.fn(), + isTimeTouched: jest.fn(), + getEnabledUpdated$: jest.fn(), + getTimeUpdate$: jest.fn(), + getRefreshIntervalUpdate$: jest.fn(), + getAutoRefreshFetch$: jest.fn(() => new Observable()), + getFetch$: jest.fn(), + getTime: jest.fn(), + setTime: jest.fn(), + setRefreshInterval: jest.fn(), + getRefreshInterval: jest.fn(), + getActiveBounds: jest.fn(), + disableAutoRefreshSelector: jest.fn(), + disableTimeRangeSelector: jest.fn(), + enableAutoRefreshSelector: jest.fn(), + enableTimeRangeSelector: jest.fn(), + getBounds: jest.fn(), + calculateBounds: jest.fn(), + createFilter: jest.fn(), + getRefreshIntervalDefaults: jest.fn(), + getTimeDefaults: jest.fn(), +}; + +export const useTimefilter = jest.fn(() => { + return timefilterMock; +}); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js index 0f1b9c77afced5..47087e776d6dd3 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js @@ -694,7 +694,10 @@ function calculateChartRange( chartRange.min = chartRange.min + maxBucketSpanMs; } - if (chartRange.min > selectedEarliestMs || chartRange.max < selectedLatestMs) { + if ( + (chartRange.min > selectedEarliestMs || chartRange.max < selectedLatestMs) && + chartRange.max - chartRange.min < selectedLatestMs - selectedEarliestMs + ) { tooManyBuckets = true; } diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts new file mode 100644 index 00000000000000..08c8d11987f196 --- /dev/null +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; +import { renderHook } from '@testing-library/react-hooks'; +import { useSelectedCells } from './use_selected_cells'; +import { ExplorerAppState } from '../../../../common/types/ml_url_generator'; +import { TimefilterContract } from '../../../../../../../src/plugins/data/public'; + +import { useTimefilter } from '../../contexts/kibana'; + +jest.mock('../../contexts/kibana'); + +describe('useSelectedCells', () => { + test('should not set state when the cell selection is correct', () => { + (useTimefilter() as jest.Mocked).getBounds.mockReturnValue({ + min: moment(1498824778 * 1000), + max: moment(1502366798 * 1000), + }); + + const urlState = { + mlExplorerSwimlane: { + selectedType: 'overall', + selectedLanes: ['Overall'], + selectedTimes: [1498780800, 1498867200], + showTopFieldValues: true, + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + mlExplorerFilter: {}, + } as ExplorerAppState; + + const setUrlState = jest.fn(); + + const bucketInterval = 86400; + + renderHook(() => useSelectedCells(urlState, setUrlState, bucketInterval)); + + expect(setUrlState).not.toHaveBeenCalled(); + }); + + test('should reset cell selection when it is completely out of range', () => { + (useTimefilter() as jest.Mocked).getBounds.mockReturnValue({ + min: moment(1501071178 * 1000), + max: moment(1502366798 * 1000), + }); + + const urlState = { + mlExplorerSwimlane: { + selectedType: 'overall', + selectedLanes: ['Overall'], + selectedTimes: [1498780800, 1498867200], + showTopFieldValues: true, + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + mlExplorerFilter: {}, + } as ExplorerAppState; + + const setUrlState = jest.fn(); + + const bucketInterval = 86400; + + const { result } = renderHook(() => useSelectedCells(urlState, setUrlState, bucketInterval)); + + expect(result.current[0]).toEqual({ + lanes: ['Overall'], + showTopFieldValues: true, + times: [1498780800, 1498867200], + type: 'overall', + viewByFieldName: 'apache2.access.remote_ip', + }); + + expect(setUrlState).toHaveBeenCalledWith({ + mlExplorerSwimlane: { + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + }); + }); + + test('should adjust cell selection time boundaries based on the main time range', () => { + (useTimefilter() as jest.Mocked).getBounds.mockReturnValue({ + min: moment(1501071178 * 1000), + max: moment(1502366798 * 1000), + }); + + const urlState = { + mlExplorerSwimlane: { + selectedType: 'overall', + selectedLanes: ['Overall'], + selectedTimes: [1498780800, 1502366798], + showTopFieldValues: true, + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + mlExplorerFilter: {}, + } as ExplorerAppState; + + const setUrlState = jest.fn(); + + const bucketInterval = 86400; + + const { result } = renderHook(() => useSelectedCells(urlState, setUrlState, bucketInterval)); + + expect(result.current[0]).toEqual({ + lanes: ['Overall'], + showTopFieldValues: true, + times: [1498780800, 1502366798], + type: 'overall', + viewByFieldName: 'apache2.access.remote_ip', + }); + + expect(setUrlState).toHaveBeenCalledWith({ + mlExplorerSwimlane: { + selectedLanes: ['Overall'], + selectedTimes: [1500984778, 1502366798], + selectedType: 'overall', + showTopFieldValues: true, + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + }); + }); +}); diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts index 7a279afc22ae77..4ce828b0b76337 100644 --- a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts @@ -5,7 +5,6 @@ */ import { useCallback, useEffect, useMemo } from 'react'; -import { Duration } from 'moment'; import { SWIMLANE_TYPE } from '../explorer_constants'; import { AppStateSelectedCells } from '../explorer_utils'; import { ExplorerAppState } from '../../../../common/types/ml_url_generator'; @@ -14,10 +13,9 @@ import { useTimefilter } from '../../contexts/kibana'; export const useSelectedCells = ( appState: ExplorerAppState, setAppState: (update: Partial) => void, - bucketInterval: Duration | undefined + bucketIntervalInSeconds: number | undefined ): [AppStateSelectedCells | undefined, (swimlaneSelectedCells: AppStateSelectedCells) => void] => { const timeFilter = useTimefilter(); - const timeBounds = timeFilter.getBounds(); // keep swimlane selection, restore selectedCells from AppState @@ -76,43 +74,45 @@ export const useSelectedCells = ( * Adjust cell selection with respect to the time boundaries. * Reset it entirely when it out of range. */ - useEffect(() => { - if (selectedCells?.times === undefined || bucketInterval === undefined) return; - - let [selectedFrom, selectedTo] = selectedCells.times; - - const rangeFrom = timeBounds.min!.unix(); - /** - * Because each cell on the swim lane represent the fixed bucket interval, - * the selection range could be outside of the time boundaries with - * correction within the bucket interval. - */ - const rangeTo = timeBounds.max!.unix() + bucketInterval.asSeconds(); - - selectedFrom = Math.max(selectedFrom, rangeFrom); - - selectedTo = Math.min(selectedTo, rangeTo); - - const isSelectionOutOfRange = rangeFrom > selectedTo || rangeTo < selectedFrom; - - if (isSelectionOutOfRange) { - // reset selection - setSelectedCells(); - return; - } - - if (selectedFrom !== rangeFrom || selectedTo !== rangeTo) { - setSelectedCells({ - ...selectedCells, - times: [selectedFrom, selectedTo], - }); - } - }, [ - timeBounds.min?.valueOf(), - timeBounds.max?.valueOf(), - selectedCells, - bucketInterval?.asMilliseconds(), - ]); + useEffect( + function adjustSwimLaneTimeSelection() { + if (selectedCells?.times === undefined || bucketIntervalInSeconds === undefined) return; + + const [selectedFrom, selectedTo] = selectedCells.times; + + /** + * Because each cell on the swim lane represent the fixed bucket interval, + * the selection range could be outside of the time boundaries with + * correction within the bucket interval. + */ + const rangeFrom = timeBounds.min!.unix() - bucketIntervalInSeconds; + const rangeTo = timeBounds.max!.unix() + bucketIntervalInSeconds; + + const resultFrom = Math.max(selectedFrom, rangeFrom); + const resultTo = Math.min(selectedTo, rangeTo); + + const isSelectionOutOfRange = rangeFrom > resultTo || rangeTo < resultFrom; + + if (isSelectionOutOfRange) { + // reset selection + setSelectedCells(); + return; + } + + if (selectedFrom === resultFrom && selectedTo === resultTo) { + // selection is correct, no need to adjust the range + return; + } + + if (resultFrom !== rangeFrom || resultTo !== rangeTo) { + setSelectedCells({ + ...selectedCells, + times: [resultFrom, resultTo], + }); + } + }, + [timeBounds.min?.unix(), timeBounds.max?.unix(), selectedCells, bucketIntervalInSeconds] + ); return [selectedCells, setSelectedCells]; }; diff --git a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx index 15ce3847f4d9cc..0075846571cee5 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx @@ -201,7 +201,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim const [selectedCells, setSelectedCells] = useSelectedCells( explorerUrlState, setExplorerUrlState, - explorerState?.swimlaneBucketInterval + explorerState?.swimlaneBucketInterval?.asSeconds() ); useEffect(() => { From 61a0b008029ef63bbaf5f50a4534cf2f5f1a34fd Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 21 Dec 2020 12:09:23 -0700 Subject: [PATCH 019/100] Migrates kql_telemetry usage collector es client (#86585) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../usage_collector/fetch.test.ts | 70 ++++++++++--------- .../kql_telemetry/usage_collector/fetch.ts | 28 ++++---- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts index 1794df7391cb00..038f340babb1fb 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.test.ts @@ -18,7 +18,7 @@ */ import { fetchProvider } from './fetch'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; import { CollectorFetchContext } from 'src/plugins/usage_collection/server'; import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; @@ -30,7 +30,7 @@ jest.mock('../../../common', () => ({ })); let fetch: ReturnType; -let callCluster: LegacyAPICaller; +let esClient: ElasticsearchClient; let collectorFetchContext: CollectorFetchContext; const collectorFetchContextMock = createCollectorFetchContextMock(); @@ -38,34 +38,33 @@ function setupMockCallCluster( optCount: { optInCount?: number; optOutCount?: number } | null, language: string | undefined | null ) { - callCluster = (jest.fn((method, params) => { - if (params && 'id' in params && params.id === 'kql-telemetry:kql-telemetry') { - if (optCount === null) { - return Promise.resolve({ + function mockedEsGetMethod() { + if (optCount === null) { + return Promise.resolve({ + body: { _index: '.kibana_1', _id: 'kql-telemetry:kql-telemetry', found: false, - }); - } else { - return Promise.resolve({ + }, + }); + } else { + return Promise.resolve({ + body: { _source: { - 'kql-telemetry': { - ...optCount, - }, + 'kql-telemetry': { ...optCount }, type: 'kql-telemetry', updated_at: '2018-10-05T20:20:56.258Z', }, - }); - } - } else if (params && 'body' in params && params.body.query.term.type === 'config') { - if (language === 'missingConfigDoc') { - return Promise.resolve({ - hits: { - hits: [], - }, - }); - } else { - return Promise.resolve({ + }, + }); + } + } + function mockedEsSearchMethod() { + if (language === 'missingConfigDoc') { + return Promise.resolve({ body: { hits: { hits: [] } } }); + } else { + return Promise.resolve({ + body: { hits: { hits: [ { @@ -77,12 +76,15 @@ function setupMockCallCluster( }, ], }, - }); - } + }, + }); } - - throw new Error('invalid call'); - }) as unknown) as LegacyAPICaller; + } + const esClientMock = ({ + get: jest.fn().mockImplementation(mockedEsGetMethod), + search: jest.fn().mockImplementation(mockedEsSearchMethod), + } as unknown) as ElasticsearchClient; + esClient = esClientMock; } describe('makeKQLUsageCollector', () => { @@ -95,7 +97,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.optInCount).toBe(1); @@ -106,7 +108,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('kuery'); @@ -117,7 +119,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, null); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('lucene'); @@ -127,7 +129,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster({ optInCount: 1 }, undefined); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('default-lucene'); @@ -137,7 +139,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster(null, 'kuery'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.optInCount).toBe(0); @@ -148,7 +150,7 @@ describe('makeKQLUsageCollector', () => { setupMockCallCluster(null, 'missingConfigDoc'); collectorFetchContext = { ...collectorFetchContextMock, - callCluster, + esClient, }; const fetchResponse = await fetch(collectorFetchContext); expect(fetchResponse.defaultQueryLanguage).toBe('default-lucene'); diff --git a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts index 21a1843d1ec816..5178aa65705d8a 100644 --- a/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts +++ b/src/plugins/data/server/kql_telemetry/usage_collector/fetch.ts @@ -30,18 +30,22 @@ export interface Usage { } export function fetchProvider(index: string) { - return async ({ callCluster }: CollectorFetchContext): Promise => { - const [response, config] = await Promise.all([ - callCluster('get', { - index, - id: 'kql-telemetry:kql-telemetry', - ignore: [404], - }), - callCluster('search', { - index, - body: { query: { term: { type: 'config' } } }, - ignore: [404], - }), + return async ({ esClient }: CollectorFetchContext): Promise => { + const [{ body: response }, { body: config }] = await Promise.all([ + esClient.get( + { + index, + id: 'kql-telemetry:kql-telemetry', + }, + { ignore: [404] } + ), + esClient.search( + { + index, + body: { query: { term: { type: 'config' } } }, + }, + { ignore: [404] } + ), ]); const queryLanguageConfigValue: string | null | undefined = get( From 2db76660ce809de02fa906a19b9741951809e73a Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Mon, 21 Dec 2020 19:17:04 +0000 Subject: [PATCH 020/100] [Alerting] Encourage type safe usage of Alerting (#86623) This PR encourages type safe usage of the Alerting framework by replacing the current default Params/State/InstanceState/InstanceContext types (which are `AlertTypeParams`/`AlertTypeState`/etc.) with `never`. This means that code can continue to omit the specific types for these fields, as long as they aren't referenced. Once an alert developer wishes to actually reference the parameters (or state/context), then they have to specify the type. This PR also changed the typing of the `AlertTypeParams` and `AlertTypeState` from `Record` to `Record`, to ensure that where these catch-all types are used they will at least enforce `unknown` rather than `any`. This change broke some usage in both @elastic/kibana-alerting-services plugins, but also other plugins in the Stack/Solutions. I tried to fix these where I could, but some of these require new types and refactoring in other teams' code, which I decided is best done by the team who own and maintain that code - I've added explicit `TODO` comments in all of these places, describing the required fix. This PR also introduced a Generics based typing for the `Alert` type so that the `params` field can be typed as something other than `AlertTypeParams`. --- .../alerting_example/common/constants.ts | 4 +- .../public/components/view_astros_alert.tsx | 4 +- .../server/alert_types/astros.ts | 6 +- x-pack/plugins/alerts/common/alert.ts | 12 +- .../create_alert_instance_factory.ts | 10 +- .../alerts/server/alert_type_registry.ts | 43 +++-- .../server/alerts_client/alerts_client.ts | 80 ++++++--- .../server/alerts_client/tests/create.test.ts | 12 +- ...rt_instance_summary_from_event_log.test.ts | 4 +- .../alert_instance_summary_from_event_log.ts | 2 +- .../lib/validate_alert_type_params.test.ts | 72 ++------ .../server/lib/validate_alert_type_params.ts | 13 +- x-pack/plugins/alerts/server/mocks.ts | 22 ++- .../alerts/server/routes/create.test.ts | 2 +- x-pack/plugins/alerts/server/routes/create.ts | 12 +- .../plugins/alerts/server/routes/get.test.ts | 4 +- .../task_runner/alert_task_instance.test.ts | 4 +- .../server/task_runner/alert_task_instance.ts | 5 +- .../create_execution_handler.test.ts | 65 ++++--- .../task_runner/create_execution_handler.ts | 23 ++- .../server/task_runner/task_runner.test.ts | 168 ++++++++++++++---- .../alerts/server/task_runner/task_runner.ts | 125 ++++++++----- .../task_runner/task_runner_factory.test.ts | 4 +- .../server/task_runner/task_runner_factory.ts | 4 +- x-pack/plugins/alerts/server/types.ts | 40 +++-- .../infra/public/alerting/inventory/index.ts | 14 +- .../public/alerting/metric_threshold/index.ts | 14 +- .../inventory_metric_threshold_executor.ts | 16 +- ...r_inventory_metric_threshold_alert_type.ts | 14 +- .../log_threshold/log_threshold_executor.ts | 39 ++-- .../metric_threshold/lib/evaluate_alert.ts | 18 +- .../metric_threshold_executor.test.ts | 35 +++- .../metric_threshold_executor.ts | 19 +- .../register_metric_threshold_alert_type.ts | 28 ++- .../plugins/monitoring/common/types/alerts.ts | 6 +- .../ccr_read_exceptions_alert/index.tsx | 5 +- .../alerts/components/duration/validation.tsx | 2 + .../cpu_usage_alert/cpu_usage_alert.tsx | 4 +- .../public/alerts/disk_usage_alert/index.tsx | 4 +- .../alerts/memory_usage_alert/index.tsx | 4 +- .../monitoring/server/alerts/base_alert.ts | 6 +- .../notifications/create_notifications.ts | 6 +- .../notifications/find_notifications.ts | 4 +- .../notifications/read_notifications.ts | 4 +- .../detection_engine/notifications/types.ts | 32 ++-- .../notifications/update_notifications.ts | 6 +- .../routes/rules/create_rules_bulk_route.ts | 9 +- .../routes/rules/create_rules_route.ts | 9 +- .../detection_engine/routes/rules/utils.ts | 9 +- .../routes/rules/validate.test.ts | 5 +- .../detection_engine/routes/rules/validate.ts | 9 +- .../detection_engine/rules/create_rules.ts | 10 +- .../lib/detection_engine/rules/find_rules.ts | 3 +- .../rules/install_prepacked_rules.ts | 6 +- .../rules/patch_rules.mock.ts | 30 +++- .../lib/detection_engine/rules/patch_rules.ts | 10 +- .../lib/detection_engine/rules/read_rules.ts | 3 +- .../lib/detection_engine/rules/types.ts | 14 +- .../detection_engine/rules/update_rules.ts | 10 +- .../signals/signal_params_schema.ts | 2 +- .../signals/signal_rule_alert_type.test.ts | 5 +- .../signals/signal_rule_alert_type.ts | 12 +- .../lib/detection_engine/signals/types.ts | 24 ++- .../server/lib/detection_engine/types.ts | 3 +- .../alert_types/geo_containment/types.ts | 3 +- .../public/alert_types/geo_threshold/types.ts | 3 +- .../alert_types/threshold/expression.tsx | 12 +- .../public/alert_types/threshold/types.ts | 4 +- .../alert_types/geo_containment/alert_type.ts | 38 +++- .../geo_containment/geo_containment.ts | 43 ++--- .../alert_types/geo_containment/index.ts | 15 +- .../tests/geo_containment.test.ts | 17 +- .../alert_types/geo_threshold/alert_type.ts | 45 ++++- .../geo_threshold/geo_threshold.ts | 34 +--- .../public/application/lib/alert_api.test.ts | 13 +- .../triggers_actions_ui/public/plugin.ts | 4 +- .../triggers_actions_ui/public/types.ts | 26 ++- .../uptime/public/state/actions/types.ts | 5 +- .../uptime/public/state/alerts/alerts.ts | 27 +-- .../plugins/uptime/public/state/api/alerts.ts | 11 +- .../lib/alerts/__tests__/status_check.test.ts | 10 +- .../plugins/uptime/server/lib/alerts/types.ts | 11 +- .../server/lib/alerts/uptime_alert_wrapper.ts | 28 ++- .../plugins/alerts/server/alert_types.ts | 14 +- .../fixtures/plugins/alerts/server/plugin.ts | 11 +- 85 files changed, 1044 insertions(+), 523 deletions(-) diff --git a/x-pack/examples/alerting_example/common/constants.ts b/x-pack/examples/alerting_example/common/constants.ts index 40cc298db795a6..8e4ea4faf014c1 100644 --- a/x-pack/examples/alerting_example/common/constants.ts +++ b/x-pack/examples/alerting_example/common/constants.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../plugins/alerts/common'; + export const ALERTING_EXAMPLE_APP_ID = 'AlertingExample'; // always firing export const DEFAULT_INSTANCES_TO_GENERATE = 5; -export interface AlwaysFiringParams { +export interface AlwaysFiringParams extends AlertTypeParams { instances?: number; thresholds?: { small?: number; diff --git a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx index e4687c75fa0b7b..eb682a86f5ff66 100644 --- a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx @@ -23,7 +23,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom'; import { CoreStart } from 'kibana/public'; import { isEmpty } from 'lodash'; import { Alert, AlertTaskState, BASE_ALERT_API_PATH } from '../../../../plugins/alerts/common'; -import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; +import { ALERTING_EXAMPLE_APP_ID, AlwaysFiringParams } from '../../common/constants'; type Props = RouteComponentProps & { http: CoreStart['http']; @@ -34,7 +34,7 @@ function hasCraft(state: any): state is { craft: string } { return state && state.craft; } export const ViewPeopleInSpaceAlertPage = withRouter(({ http, id }: Props) => { - const [alert, setAlert] = useState(null); + const [alert, setAlert] = useState | null>(null); const [alertState, setAlertState] = useState(null); useEffect(() => { diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/alert_types/astros.ts index 27a8bfc7a53a3d..22c2f25c410cd4 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/alert_types/astros.ts @@ -38,7 +38,11 @@ function getCraftFilter(craft: string) { craft === Craft.OuterSpace ? true : craft === person.craft; } -export const alertType: AlertType = { +export const alertType: AlertType< + { outerSpaceCapacity: number; craft: string; op: string }, + { peopleInSpace: number }, + { craft: string } +> = { id: 'example.people-in-space', name: 'People In Space Right Now', actionGroups: [{ id: 'default', name: 'default' }], diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index d74f66898eff66..ed3fbcf2ddc9bc 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -7,10 +7,8 @@ import { SavedObjectAttribute, SavedObjectAttributes } from 'kibana/server'; import { AlertNotifyWhenType } from './alert_notify_when_type'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeState = Record; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type AlertTypeParams = Record; +export type AlertTypeState = Record; +export type AlertTypeParams = Record; export interface IntervalSchedule extends SavedObjectAttributes { interval: string; @@ -52,7 +50,7 @@ export interface AlertAggregations { alertExecutionStatus: { [status: string]: number }; } -export interface Alert { +export interface Alert { id: string; enabled: boolean; name: string; @@ -61,7 +59,7 @@ export interface Alert { consumer: string; schedule: IntervalSchedule; actions: AlertAction[]; - params: AlertTypeParams; + params: Params; scheduledTaskId?: string; createdBy: string | null; updatedBy: string | null; @@ -76,7 +74,7 @@ export interface Alert { executionStatus: AlertExecutionStatus; } -export type SanitizedAlert = Omit; +export type SanitizedAlert = Omit, 'apiKey'>; export enum HealthStatus { OK = 'ok', diff --git a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts index 0b29262ddcc07d..47f013a5d0e552 100644 --- a/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts +++ b/x-pack/plugins/alerts/server/alert_instance/create_alert_instance_factory.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertInstanceContext, AlertInstanceState } from '../types'; import { AlertInstance } from './alert_instance'; -export function createAlertInstanceFactory(alertInstances: Record) { - return (id: string): AlertInstance => { +export function createAlertInstanceFactory< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(alertInstances: Record>) { + return (id: string): AlertInstance => { if (!alertInstances[id]) { - alertInstances[id] = new AlertInstance(); + alertInstances[id] = new AlertInstance(); } return alertInstances[id]; diff --git a/x-pack/plugins/alerts/server/alert_type_registry.ts b/x-pack/plugins/alerts/server/alert_type_registry.ts index d436d1987c0271..5e4188c1f3bc1c 100644 --- a/x-pack/plugins/alerts/server/alert_type_registry.ts +++ b/x-pack/plugins/alerts/server/alert_type_registry.ts @@ -32,7 +32,7 @@ export interface ConstructorOptions { export interface RegistryAlertType extends Pick< - NormalizedAlertType, + UntypedNormalizedAlertType, | 'name' | 'actionGroups' | 'recoveryActionGroup' @@ -66,16 +66,23 @@ const alertIdSchema = schema.string({ }); export type NormalizedAlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext > = Omit, 'recoveryActionGroup'> & Pick>, 'recoveryActionGroup'>; +export type UntypedNormalizedAlertType = NormalizedAlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export class AlertTypeRegistry { private readonly taskManager: TaskManagerSetupContract; - private readonly alertTypes: Map = new Map(); + private readonly alertTypes: Map = new Map(); private readonly taskRunnerFactory: TaskRunnerFactory; private readonly licenseState: ILicenseState; private readonly licensing: LicensingPluginSetup; @@ -96,10 +103,10 @@ export class AlertTypeRegistry { } public register< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext >(alertType: AlertType) { if (this.has(alertType.id)) { throw new Error( @@ -113,14 +120,22 @@ export class AlertTypeRegistry { } alertType.actionVariables = normalizedActionVariables(alertType.actionVariables); - const normalizedAlertType = augmentActionGroupsWithReserved(alertType as AlertType); + const normalizedAlertType = augmentActionGroupsWithReserved< + Params, + State, + InstanceState, + InstanceContext + >(alertType); - this.alertTypes.set(alertIdSchema.validate(alertType.id), normalizedAlertType); + this.alertTypes.set( + alertIdSchema.validate(alertType.id), + normalizedAlertType as UntypedNormalizedAlertType + ); this.taskManager.registerTaskDefinitions({ [`alerting:${alertType.id}`]: { title: alertType.name, createTaskRunner: (context: RunContext) => - this.taskRunnerFactory.create(normalizedAlertType, context), + this.taskRunnerFactory.create(normalizedAlertType as UntypedNormalizedAlertType, context), }, }); // No need to notify usage on basic alert types @@ -170,7 +185,7 @@ export class AlertTypeRegistry { producer, minimumLicenseRequired, }, - ]: [string, NormalizedAlertType]) => ({ + ]: [string, UntypedNormalizedAlertType]) => ({ id, name, actionGroups, diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index f21cd2b02943a9..e21fee4ce3d61f 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -23,13 +23,13 @@ import { RawAlert, AlertTypeRegistry, AlertAction, - AlertType, IntervalSchedule, SanitizedAlert, AlertTaskState, AlertInstanceSummary, AlertExecutionStatusValues, AlertNotifyWhenType, + AlertTypeParams, } from '../types'; import { validateAlertTypeParams, @@ -44,7 +44,7 @@ import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/se import { TaskManagerStartContract } from '../../../task_manager/server'; import { taskInstanceToAlertTaskInstance } from '../task_runner/alert_task_instance'; import { deleteTaskIfItExists } from '../lib/delete_task_if_it_exists'; -import { RegistryAlertType } from '../alert_type_registry'; +import { RegistryAlertType, UntypedNormalizedAlertType } from '../alert_type_registry'; import { AlertsAuthorization, WriteOperations, ReadOperations } from '../authorization'; import { IEventLogClient } from '../../../../plugins/event_log/server'; import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; @@ -127,16 +127,16 @@ interface AggregateResult { alertExecutionStatus: { [status: string]: number }; } -export interface FindResult { +export interface FindResult { page: number; perPage: number; total: number; - data: SanitizedAlert[]; + data: Array>; } -export interface CreateOptions { +export interface CreateOptions { data: Omit< - Alert, + Alert, | 'id' | 'createdBy' | 'updatedBy' @@ -154,14 +154,14 @@ export interface CreateOptions { }; } -interface UpdateOptions { +interface UpdateOptions { id: string; data: { name: string; tags: string[]; schedule: IntervalSchedule; actions: NormalizedAlertAction[]; - params: Record; + params: Params; throttle: string | null; notifyWhen: AlertNotifyWhenType | null; }; @@ -223,7 +223,10 @@ export class AlertsClient { this.auditLogger = auditLogger; } - public async create({ data, options }: CreateOptions): Promise { + public async create({ + data, + options, + }: CreateOptions): Promise> { const id = SavedObjectsUtils.generateId(); try { @@ -248,7 +251,10 @@ export class AlertsClient { // Throws an error if alert type isn't registered const alertType = this.alertTypeRegistry.get(data.alertTypeId); - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); const username = await this.getUserName(); const createdAPIKey = data.enabled @@ -334,10 +340,14 @@ export class AlertsClient { }); createdAlert.attributes.scheduledTaskId = scheduledTask.id; } - return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); + return this.getAlertFromRaw(createdAlert.id, createdAlert.attributes, references); } - public async get({ id }: { id: string }): Promise { + public async get({ + id, + }: { + id: string; + }): Promise> { const result = await this.unsecuredSavedObjectsClient.get('alert', id); try { await this.authorization.ensureAuthorized( @@ -361,7 +371,7 @@ export class AlertsClient { savedObject: { type: 'alert', id }, }) ); - return this.getAlertFromRaw(result.id, result.attributes, result.references); + return this.getAlertFromRaw(result.id, result.attributes, result.references); } public async getAlertState({ id }: { id: string }): Promise { @@ -426,9 +436,9 @@ export class AlertsClient { }); } - public async find({ + public async find({ options: { fields, ...options } = {}, - }: { options?: FindOptions } = {}): Promise { + }: { options?: FindOptions } = {}): Promise> { let authorizationTuple; try { authorizationTuple = await this.authorization.getFindAuthorizationFilter(); @@ -475,7 +485,7 @@ export class AlertsClient { ); throw error; } - return this.getAlertFromRaw( + return this.getAlertFromRaw( id, fields ? (pick(attributes, fields) as RawAlert) : attributes, references @@ -605,15 +615,21 @@ export class AlertsClient { return removeResult; } - public async update({ id, data }: UpdateOptions): Promise { + public async update({ + id, + data, + }: UpdateOptions): Promise> { return await retryIfConflicts( this.logger, `alertsClient.update('${id}')`, - async () => await this.updateWithOCC({ id, data }) + async () => await this.updateWithOCC({ id, data }) ); } - private async updateWithOCC({ id, data }: UpdateOptions): Promise { + private async updateWithOCC({ + id, + data, + }: UpdateOptions): Promise> { let alertSavedObject: SavedObject; try { @@ -658,7 +674,7 @@ export class AlertsClient { this.alertTypeRegistry.ensureAlertTypeEnabled(alertSavedObject.attributes.alertTypeId); - const updateResult = await this.updateAlert({ id, data }, alertSavedObject); + const updateResult = await this.updateAlert({ id, data }, alertSavedObject); await Promise.all([ alertSavedObject.attributes.apiKey @@ -692,14 +708,17 @@ export class AlertsClient { return updateResult; } - private async updateAlert( - { id, data }: UpdateOptions, + private async updateAlert( + { id, data }: UpdateOptions, { attributes, version }: SavedObject - ): Promise { + ): Promise> { const alertType = this.alertTypeRegistry.get(attributes.alertTypeId); // Validate - const validatedAlertTypeParams = validateAlertTypeParams(alertType, data.params); + const validatedAlertTypeParams = validateAlertTypeParams( + data.params, + alertType.validate?.params + ); this.validateActions(alertType, data.actions); const { actions, references } = await this.denormalizeActions(data.actions); @@ -1343,7 +1362,7 @@ export class AlertsClient { }) as Alert['actions']; } - private getAlertFromRaw( + private getAlertFromRaw( id: string, rawAlert: RawAlert, references: SavedObjectReference[] | undefined @@ -1351,14 +1370,14 @@ export class AlertsClient { // In order to support the partial update API of Saved Objects we have to support // partial updates of an Alert, but when we receive an actual RawAlert, it is safe // to cast the result to an Alert - return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; + return this.getPartialAlertFromRaw(id, rawAlert, references) as Alert; } - private getPartialAlertFromRaw( + private getPartialAlertFromRaw( id: string, { createdAt, updatedAt, meta, notifyWhen, scheduledTaskId, ...rawAlert }: Partial, references: SavedObjectReference[] | undefined - ): PartialAlert { + ): PartialAlert { // Not the prettiest code here, but if we want to use most of the // alert fields from the rawAlert using `...rawAlert` kind of access, we // need to specifically delete the executionStatus as it's a different type @@ -1386,7 +1405,10 @@ export class AlertsClient { }; } - private validateActions(alertType: AlertType, actions: NormalizedAlertAction[]): void { + private validateActions( + alertType: UntypedNormalizedAlertType, + actions: NormalizedAlertAction[] + ): void { const { actionGroups: alertTypeActionGroups } = alertType; const usedAlertActionGroups = actions.map((action) => action.group); const availableAlertTypeActionGroups = new Set(map(alertTypeActionGroups, 'id')); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts index 5f830a6c5bc51a..0424a1295c9b99 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -59,7 +59,11 @@ beforeEach(() => { setGlobalDate(); -function getMockData(overwrites: Record = {}): CreateOptions['data'] { +function getMockData( + overwrites: Record = {} +): CreateOptions<{ + bar: boolean; +}>['data'] { return { enabled: true, name: 'abc', @@ -93,7 +97,11 @@ describe('create()', () => { }); describe('authorization', () => { - function tryToExecuteOperation(options: CreateOptions): Promise { + function tryToExecuteOperation( + options: CreateOptions<{ + bar: boolean; + }> + ): Promise { unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ { diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts index d6357494546b03..0f91e5d0c24a91 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts @@ -635,11 +635,11 @@ export class EventsFactory { } } -function createAlert(overrides: Partial): SanitizedAlert { +function createAlert(overrides: Partial): SanitizedAlert<{ bar: boolean }> { return { ...BaseAlert, ...overrides }; } -const BaseAlert: SanitizedAlert = { +const BaseAlert: SanitizedAlert<{ bar: boolean }> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts index f540f9a9b884c8..a020eecd448a40 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.ts @@ -9,7 +9,7 @@ import { IEvent } from '../../../event_log/server'; import { EVENT_LOG_ACTIONS, EVENT_LOG_PROVIDER, LEGACY_EVENT_LOG_ACTIONS } from '../plugin'; export interface AlertInstanceSummaryFromEventLogParams { - alert: SanitizedAlert; + alert: SanitizedAlert<{ bar: boolean }>; events: IEvent[]; dateStart: string; dateEnd: string; diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts index 2814eaef3e02a5..634b6885aa59be 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.test.ts @@ -8,51 +8,19 @@ import { schema } from '@kbn/config-schema'; import { validateAlertTypeParams } from './validate_alert_type_params'; test('should return passed in params when validation not defined', () => { - const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - async executor() {}, - producer: 'alerts', - }, - { - foo: true, - } - ); + const result = validateAlertTypeParams({ + foo: true, + }); expect(result).toEqual({ foo: true }); }); test('should validate and apply defaults when params is valid', () => { const result = validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - param2: schema.string({ defaultValue: 'default-value' }), - }), - }, - async executor() {}, - producer: 'alerts', - }, - { param1: 'value' } + { param1: 'value' }, + schema.object({ + param1: schema.string(), + param2: schema.string({ defaultValue: 'default-value' }), + }) ); expect(result).toEqual({ param1: 'value', @@ -63,26 +31,10 @@ test('should validate and apply defaults when params is valid', () => { test('should validate and throw error when params is invalid', () => { expect(() => validateAlertTypeParams( - { - id: 'my-alert-type', - name: 'My description', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - async executor() {}, - producer: 'alerts', - }, - {} + {}, + schema.object({ + param1: schema.string(), + }) ) ).toThrowErrorMatchingInlineSnapshot( `"params invalid: [param1]: expected value of type [string] but got [undefined]"` diff --git a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts index a443143d8cbde8..2f510f90a2367a 100644 --- a/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts +++ b/x-pack/plugins/alerts/server/lib/validate_alert_type_params.ts @@ -5,15 +5,14 @@ */ import Boom from '@hapi/boom'; -import { AlertType, AlertExecutorOptions } from '../types'; +import { AlertTypeParams, AlertTypeParamsValidator } from '../types'; -export function validateAlertTypeParams( - alertType: AlertType, - params: Record -): AlertExecutorOptions['params'] { - const validator = alertType.validate && alertType.validate.params; +export function validateAlertTypeParams( + params: Record, + validator?: AlertTypeParamsValidator +): Params { if (!validator) { - return params as AlertExecutorOptions['params']; + return params as Params; } try { diff --git a/x-pack/plugins/alerts/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts index cfae4c650bd42c..0f042b7a81d6c1 100644 --- a/x-pack/plugins/alerts/server/mocks.ts +++ b/x-pack/plugins/alerts/server/mocks.ts @@ -11,6 +11,7 @@ import { elasticsearchServiceMock, savedObjectsClientMock, } from '../../../../src/core/server/mocks'; +import { AlertInstanceContext, AlertInstanceState } from './types'; export { alertsClientMock }; @@ -30,8 +31,14 @@ const createStartMock = () => { return mock; }; -export type AlertInstanceMock = jest.Mocked; -const createAlertInstanceFactoryMock = () => { +export type AlertInstanceMock< + State extends AlertInstanceState = AlertInstanceState, + Context extends AlertInstanceContext = AlertInstanceContext +> = jest.Mocked>; +const createAlertInstanceFactoryMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { const mock = { hasScheduledActions: jest.fn(), isThrottled: jest.fn(), @@ -50,14 +57,17 @@ const createAlertInstanceFactoryMock = () => { mock.unscheduleActions.mockReturnValue(mock); mock.scheduleActions.mockReturnValue(mock); - return (mock as unknown) as AlertInstanceMock; + return (mock as unknown) as AlertInstanceMock; }; -const createAlertServicesMock = () => { - const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); +const createAlertServicesMock = < + InstanceState extends AlertInstanceState = AlertInstanceState, + InstanceContext extends AlertInstanceContext = AlertInstanceContext +>() => { + const alertInstanceFactoryMock = createAlertInstanceFactoryMock(); return { alertInstanceFactory: jest - .fn, [string]>() + .fn>, [string]>() .mockReturnValue(alertInstanceFactoryMock), callCluster: elasticsearchServiceMock.createLegacyScopedClusterClient().callAsCurrentUser, getLegacyScopedClusterClient: jest.fn(), diff --git a/x-pack/plugins/alerts/server/routes/create.test.ts b/x-pack/plugins/alerts/server/routes/create.test.ts index 5597b315158cd7..fc531821f25b68 100644 --- a/x-pack/plugins/alerts/server/routes/create.test.ts +++ b/x-pack/plugins/alerts/server/routes/create.test.ts @@ -49,7 +49,7 @@ describe('createAlertRoute', () => { ], }; - const createResult: Alert = { + const createResult: Alert<{ bar: boolean }> = { ...mockedAlert, enabled: true, muteAll: false, diff --git a/x-pack/plugins/alerts/server/routes/create.ts b/x-pack/plugins/alerts/server/routes/create.ts index a34a3118985fa5..a79a9d40b236f2 100644 --- a/x-pack/plugins/alerts/server/routes/create.ts +++ b/x-pack/plugins/alerts/server/routes/create.ts @@ -16,7 +16,13 @@ import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { validateDurationSchema } from '../lib'; import { handleDisabledApiKeysError } from './lib/error_handler'; -import { Alert, AlertNotifyWhenType, BASE_ALERT_API_PATH, validateNotifyWhenType } from '../types'; +import { + Alert, + AlertNotifyWhenType, + AlertTypeParams, + BASE_ALERT_API_PATH, + validateNotifyWhenType, +} from '../types'; import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; export const bodySchema = schema.object({ @@ -65,7 +71,9 @@ export const createAlertRoute = (router: IRouter, licenseState: ILicenseState) = const alert = req.body; const notifyWhen = alert?.notifyWhen ? (alert.notifyWhen as AlertNotifyWhenType) : null; try { - const alertRes: Alert = await alertsClient.create({ data: { ...alert, notifyWhen } }); + const alertRes: Alert = await alertsClient.create({ + data: { ...alert, notifyWhen }, + }); return res.ok({ body: alertRes, }); diff --git a/x-pack/plugins/alerts/server/routes/get.test.ts b/x-pack/plugins/alerts/server/routes/get.test.ts index 21e52ece82d2d6..747f9b11e2b47a 100644 --- a/x-pack/plugins/alerts/server/routes/get.test.ts +++ b/x-pack/plugins/alerts/server/routes/get.test.ts @@ -22,7 +22,9 @@ beforeEach(() => { }); describe('getAlertRoute', () => { - const mockedAlert: Alert = { + const mockedAlert: Alert<{ + bar: true; + }> = { id: '1', alertTypeId: '1', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts index 09236ec5e0ad1a..1bd8b75e2133de 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts @@ -9,7 +9,9 @@ import { AlertTaskInstance, taskInstanceToAlertTaskInstance } from './alert_task import uuid from 'uuid'; import { SanitizedAlert } from '../types'; -const alert: SanitizedAlert = { +const alert: SanitizedAlert<{ + bar: boolean; +}> = { id: 'alert-123', alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts index a290f3fa33c70d..ab074cfdffa1cf 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.ts @@ -13,6 +13,7 @@ import { alertParamsSchema, alertStateSchema, AlertTaskParams, + AlertTypeParams, } from '../../common'; export interface AlertTaskInstance extends ConcreteTaskInstance { @@ -23,9 +24,9 @@ export interface AlertTaskInstance extends ConcreteTaskInstance { const enumerateErrorFields = (e: t.Errors) => `${e.map(({ context }) => context.map(({ key }) => key).join('.'))}`; -export function taskInstanceToAlertTaskInstance( +export function taskInstanceToAlertTaskInstance( taskInstance: ConcreteTaskInstance, - alert?: SanitizedAlert + alert?: SanitizedAlert ): AlertTaskInstance { return { ...taskInstance, diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts index b414e726f01010..5603b13a3b1f5c 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.test.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertType } from '../types'; -import { createExecutionHandler } from './create_execution_handler'; +import { createExecutionHandler, CreateExecutionHandlerOptions } from './create_execution_handler'; import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsMock, @@ -16,12 +15,19 @@ import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { KibanaRequest } from 'kibana/server'; import { asSavedObjectExecutionSource } from '../../../actions/server'; import { InjectActionParamsOpts } from './inject_action_params'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; +import { + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), })); -const alertType: AlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'Test', actionGroups: [ @@ -39,18 +45,26 @@ const alertType: AlertType = { }; const actionsClient = actionsClientMock.create(); -const createExecutionHandlerParams = { - actionsPlugin: actionsMock.createStart(), + +const mockActionsPlugin = actionsMock.createStart(); +const mockEventLogger = eventLoggerMock.create(); +const createExecutionHandlerParams: jest.Mocked< + CreateExecutionHandlerOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + > +> = { + actionsPlugin: mockActionsPlugin, spaceId: 'default', alertId: '1', alertName: 'name-of-alert', tags: ['tag-A', 'tag-B'], apiKey: 'MTIzOmFiYw==', - spaceIdToNamespace: jest.fn().mockReturnValue(undefined), - getBasePath: jest.fn().mockReturnValue(undefined), alertType, logger: loggingSystemMock.create().get(), - eventLogger: eventLoggerMock.create(), + eventLogger: mockEventLogger, actions: [ { id: '1', @@ -79,12 +93,10 @@ beforeEach(() => { .injectActionParams.mockImplementation( ({ actionParams }: InjectActionParamsOpts) => actionParams ); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest.mockResolvedValue( - actionsClient - ); - createExecutionHandlerParams.actionsPlugin.renderActionParameterTemplates.mockImplementation( + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(true); + mockActionsPlugin.isActionExecutable.mockReturnValue(true); + mockActionsPlugin.getActionsClientWithRequest.mockResolvedValue(actionsClient); + mockActionsPlugin.renderActionParameterTemplates.mockImplementation( renderActionParameterTemplatesDefault ); }); @@ -97,9 +109,9 @@ test('enqueues execution per selected action', async () => { context: {}, alertInstanceId: '2', }); - expect( - createExecutionHandlerParams.actionsPlugin.getActionsClientWithRequest - ).toHaveBeenCalledWith(createExecutionHandlerParams.request); + expect(mockActionsPlugin.getActionsClientWithRequest).toHaveBeenCalledWith( + createExecutionHandlerParams.request + ); expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(1); expect(actionsClient.enqueueExecution.mock.calls[0]).toMatchInlineSnapshot(` Array [ @@ -124,9 +136,8 @@ test('enqueues execution per selected action', async () => { ] `); - const eventLogger = createExecutionHandlerParams.eventLogger; - expect(eventLogger.logEvent).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` + expect(mockEventLogger.logEvent).toHaveBeenCalledTimes(1); + expect(mockEventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -171,9 +182,9 @@ test('enqueues execution per selected action', async () => { test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => { // Mock two calls, one for check against actions[0] and the second for actions[1] - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); + mockActionsPlugin.isActionExecutable.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValueOnce(true); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -214,9 +225,9 @@ test(`doesn't call actionsPlugin.execute for disabled actionTypes`, async () => }); test('trow error error message when action type is disabled', async () => { - createExecutionHandlerParams.actionsPlugin.preconfiguredActions = []; - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockReturnValue(false); - createExecutionHandlerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(false); + mockActionsPlugin.preconfiguredActions = []; + mockActionsPlugin.isActionExecutable.mockReturnValue(false); + mockActionsPlugin.isActionTypeEnabled.mockReturnValue(false); const executionHandler = createExecutionHandler({ ...createExecutionHandlerParams, actions: [ @@ -243,7 +254,7 @@ test('trow error error message when action type is disabled', async () => { expect(actionsClient.enqueueExecution).toHaveBeenCalledTimes(0); - createExecutionHandlerParams.actionsPlugin.isActionExecutable.mockImplementation(() => true); + mockActionsPlugin.isActionExecutable.mockImplementation(() => true); const executionHandlerForPreconfiguredAction = createExecutionHandler({ ...createExecutionHandlerParams, actions: [...createExecutionHandlerParams.actions], diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index 8c7ad79483194f..8b4412aeb23e5d 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -15,14 +15,20 @@ import { EVENT_LOG_ACTIONS } from '../plugin'; import { injectActionParams } from './inject_action_params'; import { AlertAction, + AlertTypeParams, + AlertTypeState, AlertInstanceState, AlertInstanceContext, - AlertType, - AlertTypeParams, RawAlert, } from '../types'; +import { NormalizedAlertType } from '../alert_type_registry'; -interface CreateExecutionHandlerOptions { +export interface CreateExecutionHandlerOptions< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { alertId: string; alertName: string; tags?: string[]; @@ -30,7 +36,7 @@ interface CreateExecutionHandlerOptions { actions: AlertAction[]; spaceId: string; apiKey: RawAlert['apiKey']; - alertType: AlertType; + alertType: NormalizedAlertType; logger: Logger; eventLogger: IEventLogger; request: KibanaRequest; @@ -45,7 +51,12 @@ interface ExecutionHandlerOptions { state: AlertInstanceState; } -export function createExecutionHandler({ +export function createExecutionHandler< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>({ logger, alertId, alertName, @@ -58,7 +69,7 @@ export function createExecutionHandler({ eventLogger, request, alertParams, -}: CreateExecutionHandlerOptions) { +}: CreateExecutionHandlerOptions) { const alertTypeActionGroups = new Map( alertType.actionGroups.map((actionGroup) => [actionGroup.id, actionGroup.name]) ); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index a4b565194e4314..967c5263b9730e 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -6,7 +6,13 @@ import sinon from 'sinon'; import { schema } from '@kbn/config-schema'; -import { AlertExecutorOptions } from '../types'; +import { + AlertExecutorOptions, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../types'; import { ConcreteTaskInstance, isUnrecoverableError, @@ -28,9 +34,9 @@ import { IEventLogger } from '../../../event_log/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { Alert, RecoveredActionGroup } from '../../common'; import { omit } from 'lodash'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType = { +const alertType: jest.Mocked = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], @@ -91,7 +97,7 @@ describe('Task Runner', () => { alertTypeRegistry, }; - const mockedAlertTypeSavedObject: Alert = { + const mockedAlertTypeSavedObject: Alert = { id: '1', consumer: 'bar', createdAt: new Date('2019-02-12T21:01:22.479Z'), @@ -150,7 +156,7 @@ describe('Task Runner', () => { test('successfully executes the task', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -254,14 +260,21 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subDefault'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -407,12 +420,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -516,13 +536,20 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); executorServices.alertInstanceFactory('2').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -562,12 +589,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -656,12 +690,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -696,14 +737,21 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices .alertInstanceFactory('1') .scheduleActionsWithSubGroup('default', 'subgroup1'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -744,12 +792,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionTypeEnabled.mockReturnValue(true); taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -912,12 +967,19 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams.actionsPlugin.isActionExecutable.mockReturnValue(true); alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -1012,12 +1074,19 @@ describe('Task Runner', () => { }; alertTypeWithCustomRecovery.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const taskRunner = new TaskRunner( - alertTypeWithCustomRecovery as NormalizedAlertType, + alertTypeWithCustomRecovery, { ...mockedTaskInstance, state: { @@ -1103,13 +1172,20 @@ describe('Task Runner', () => { test('persists alertInstances passed in from state, only if they are scheduled for execution', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { executorServices.alertInstanceFactory('1').scheduleActions('default'); } ); const date = new Date().toISOString(); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: { @@ -1239,7 +1315,7 @@ describe('Task Runner', () => { param1: schema.string(), }), }, - } as NormalizedAlertType, + }, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1267,7 +1343,7 @@ describe('Task Runner', () => { test('uses API key when provided', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1300,7 +1376,7 @@ describe('Task Runner', () => { test(`doesn't use API key when not provided`, async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1330,7 +1406,7 @@ describe('Task Runner', () => { test('rescheduled the Alert if the schedule has update during a task run', async () => { const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1365,13 +1441,20 @@ describe('Task Runner', () => { test('recovers gracefully when the AlertType executor throws an exception', async () => { alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1438,7 +1521,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1497,7 +1580,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1564,7 +1647,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1631,7 +1714,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1701,7 +1784,7 @@ describe('Task Runner', () => { const legacyTaskInstance = omit(mockedTaskInstance, 'schedule'); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, legacyTaskInstance, taskRunnerFactoryInitializerParams ); @@ -1733,13 +1816,20 @@ describe('Task Runner', () => { }; alertType.executor.mockImplementation( - ({ services: executorServices }: AlertExecutorOptions) => { + async ({ + services: executorServices, + }: AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext + >) => { throw new Error('OMG'); } ); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, { ...mockedTaskInstance, state: originalAlertSate, @@ -1770,7 +1860,7 @@ describe('Task Runner', () => { }); const taskRunner = new TaskRunner( - alertType as NormalizedAlertType, + alertType, mockedTaskInstance, taskRunnerFactoryInitializerParams ); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts index 44cf7dd91be7d5..c4187145e5a167 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -26,7 +26,6 @@ import { RawAlertInstance, AlertTaskState, Alert, - AlertExecutorOptions, SanitizedAlert, AlertExecutionStatus, AlertExecutionStatusErrorReasons, @@ -39,7 +38,13 @@ import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_l import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; import { AlertsClient } from '../alerts_client'; import { partiallyUpdateAlert } from '../saved_objects'; -import { ActionGroup } from '../../common'; +import { + ActionGroup, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../common'; import { NormalizedAlertType } from '../alert_type_registry'; const FALLBACK_RETRY_INTERVAL = '5m'; @@ -55,15 +60,20 @@ interface AlertTaskInstance extends ConcreteTaskInstance { state: AlertTaskState; } -export class TaskRunner { +export class TaskRunner< + Params extends AlertTypeParams, + State extends AlertTypeState, + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { private context: TaskRunnerContext; private logger: Logger; private taskInstance: AlertTaskInstance; - private alertType: NormalizedAlertType; + private alertType: NormalizedAlertType; private readonly alertTypeRegistry: AlertTypeRegistry; constructor( - alertType: NormalizedAlertType, + alertType: NormalizedAlertType, taskInstance: ConcreteTaskInstance, context: TaskRunnerContext ) { @@ -131,8 +141,8 @@ export class TaskRunner { tags: string[] | undefined, spaceId: string, apiKey: RawAlert['apiKey'], - actions: Alert['actions'], - alertParams: RawAlert['params'] + actions: Alert['actions'], + alertParams: Params ) { return createExecutionHandler({ alertId, @@ -152,7 +162,7 @@ export class TaskRunner { async executeAlertInstance( alertInstanceId: string, - alertInstance: AlertInstance, + alertInstance: AlertInstance, executionHandler: ReturnType ) { const { @@ -168,8 +178,8 @@ export class TaskRunner { async executeAlertInstances( services: Services, - alert: SanitizedAlert, - params: AlertExecutorOptions['params'], + alert: SanitizedAlert, + params: Params, executionHandler: ReturnType, spaceId: string, event: Event @@ -190,9 +200,12 @@ export class TaskRunner { } = this.taskInstance; const namespace = this.context.spaceIdToNamespace(spaceId); - const alertInstances = mapValues, AlertInstance>( + const alertInstances = mapValues< + Record, + AlertInstance + >( alertRawInstances, - (rawAlertInstance) => new AlertInstance(rawAlertInstance) + (rawAlertInstance) => new AlertInstance(rawAlertInstance) ); const originalAlertInstances = cloneDeep(alertInstances); @@ -205,10 +218,12 @@ export class TaskRunner { alertId, services: { ...services, - alertInstanceFactory: createAlertInstanceFactory(alertInstances), + alertInstanceFactory: createAlertInstanceFactory( + alertInstances + ), }, params, - state: alertTypeState, + state: alertTypeState as State, startedAt: this.taskInstance.startedAt!, previousStartedAt: previousStartedAt ? new Date(previousStartedAt) : null, spaceId, @@ -232,12 +247,15 @@ export class TaskRunner { event.event.outcome = 'success'; // Cleanup alert instances that are no longer scheduling actions to avoid over populating the alertInstances object - const instancesWithScheduledActions = pickBy(alertInstances, (alertInstance: AlertInstance) => - alertInstance.hasScheduledActions() + const instancesWithScheduledActions = pickBy( + alertInstances, + (alertInstance: AlertInstance) => + alertInstance.hasScheduledActions() ); const recoveredAlertInstances = pickBy( alertInstances, - (alertInstance: AlertInstance) => !alertInstance.hasScheduledActions() + (alertInstance: AlertInstance) => + !alertInstance.hasScheduledActions() ); logActiveAndRecoveredInstances({ @@ -272,7 +290,10 @@ export class TaskRunner { const instancesToExecute = notifyWhen === 'onActionGroupChange' ? Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const shouldExecuteAction = alertInstance.scheduledActionGroupOrSubgroupHasChanged(); if (!shouldExecuteAction) { this.logger.debug( @@ -283,7 +304,10 @@ export class TaskRunner { } ) : Object.entries(instancesWithScheduledActions).filter( - ([alertInstanceName, alertInstance]: [string, AlertInstance]) => { + ([alertInstanceName, alertInstance]: [ + string, + AlertInstance + ]) => { const throttled = alertInstance.isThrottled(throttle); const muted = mutedInstanceIdsSet.has(alertInstanceName); const shouldExecuteAction = !throttled && !muted; @@ -299,8 +323,9 @@ export class TaskRunner { ); await Promise.all( - instancesToExecute.map(([id, alertInstance]: [string, AlertInstance]) => - this.executeAlertInstance(id, alertInstance, executionHandler) + instancesToExecute.map( + ([id, alertInstance]: [string, AlertInstance]) => + this.executeAlertInstance(id, alertInstance, executionHandler) ) ); } else { @@ -309,17 +334,17 @@ export class TaskRunner { return { alertTypeState: updatedAlertTypeState || undefined, - alertInstances: mapValues, RawAlertInstance>( - instancesWithScheduledActions, - (alertInstance) => alertInstance.toRaw() - ), + alertInstances: mapValues< + Record>, + RawAlertInstance + >(instancesWithScheduledActions, (alertInstance) => alertInstance.toRaw()), }; } async validateAndExecuteAlert( services: Services, apiKey: RawAlert['apiKey'], - alert: SanitizedAlert, + alert: SanitizedAlert, event: Event ) { const { @@ -327,7 +352,7 @@ export class TaskRunner { } = this.taskInstance; // Validate - const validatedParams = validateAlertTypeParams(this.alertType, alert.params); + const validatedParams = validateAlertTypeParams(alert.params, this.alertType.validate?.params); const executionHandler = this.getExecutionHandler( alertId, alert.name, @@ -359,7 +384,7 @@ export class TaskRunner { } const [services, alertsClient] = this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); - let alert: SanitizedAlert; + let alert: SanitizedAlert; // Ensure API key is still valid and user has access try { @@ -501,19 +526,23 @@ export class TaskRunner { } } -interface GenerateNewAndRecoveredInstanceEventsParams { +interface GenerateNewAndRecoveredInstanceEventsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { eventLogger: IEventLogger; - originalAlertInstances: Dictionary; - currentAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + originalAlertInstances: Dictionary>; + currentAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertId: string; alertLabel: string; namespace: string | undefined; } -function generateNewAndRecoveredInstanceEvents( - params: GenerateNewAndRecoveredInstanceEventsParams -) { +function generateNewAndRecoveredInstanceEvents< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: GenerateNewAndRecoveredInstanceEventsParams) { const { eventLogger, alertId, @@ -584,16 +613,22 @@ function generateNewAndRecoveredInstanceEvents( } } -interface ScheduleActionsForRecoveredInstancesParams { +interface ScheduleActionsForRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; recoveryActionGroup: ActionGroup; - recoveredAlertInstances: Dictionary; + recoveredAlertInstances: Dictionary>; executionHandler: ReturnType; mutedInstanceIdsSet: Set; alertLabel: string; } -function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecoveredInstancesParams) { +function scheduleActionsForRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: ScheduleActionsForRecoveredInstancesParams) { const { logger, recoveryActionGroup, @@ -623,14 +658,20 @@ function scheduleActionsForRecoveredInstances(params: ScheduleActionsForRecovere } } -interface LogActiveAndRecoveredInstancesParams { +interface LogActiveAndRecoveredInstancesParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { logger: Logger; - activeAlertInstances: Dictionary; - recoveredAlertInstances: Dictionary; + activeAlertInstances: Dictionary>; + recoveredAlertInstances: Dictionary>; alertLabel: string; } -function logActiveAndRecoveredInstances(params: LogActiveAndRecoveredInstancesParams) { +function logActiveAndRecoveredInstances< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +>(params: LogActiveAndRecoveredInstancesParams) { const { logger, activeAlertInstances, recoveredAlertInstances, alertLabel } = params; const activeInstanceIds = Object.keys(activeAlertInstances); const recoveredInstanceIds = Object.keys(recoveredAlertInstances); diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts index 6c58b64fffa92c..3a5a130f582ed9 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts @@ -16,10 +16,10 @@ import { import { actionsMock } from '../../../actions/server/mocks'; import { alertsMock, alertsClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; import { alertTypeRegistryMock } from '../alert_type_registry.mock'; -const alertType: NormalizedAlertType = { +const alertType: UntypedNormalizedAlertType = { id: 'test', name: 'My test alert', actionGroups: [{ id: 'default', name: 'Default' }], diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts index 1fe94972bd4b07..e266608d80880e 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts @@ -17,7 +17,7 @@ import { AlertTypeRegistry, GetServicesFunction, SpaceIdToNamespaceFunction } fr import { TaskRunner } from './task_runner'; import { IEventLogger } from '../../../event_log/server'; import { AlertsClient } from '../alerts_client'; -import { NormalizedAlertType } from '../alert_type_registry'; +import { UntypedNormalizedAlertType } from '../alert_type_registry'; export interface TaskRunnerContext { logger: Logger; @@ -44,7 +44,7 @@ export class TaskRunnerFactory { this.taskRunnerContext = taskRunnerContext; } - public create(alertType: NormalizedAlertType, { taskInstance }: RunContext) { + public create(alertType: UntypedNormalizedAlertType, { taskInstance }: RunContext) { if (!this.isInitialized) { throw new Error('TaskRunnerFactory not initialized'); } diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 8704068c3e51a1..027f875e2d08db 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -61,10 +61,10 @@ export interface AlertServices< } export interface AlertExecutorOptions< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { alertId: string; startedAt: Date; @@ -85,26 +85,28 @@ export interface ActionVariable { description: string; } -// signature of the alert type executor function export type ExecutorType< - Params, - State, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > = ( options: AlertExecutorOptions ) => Promise; +export interface AlertTypeParamsValidator { + validate: (object: unknown) => Params; +} export interface AlertType< - Params extends AlertTypeParams = AlertTypeParams, - State extends AlertTypeState = AlertTypeState, - InstanceState extends AlertInstanceState = AlertInstanceState, - InstanceContext extends AlertInstanceContext = AlertInstanceContext + Params extends AlertTypeParams = never, + State extends AlertTypeState = never, + InstanceState extends AlertInstanceState = never, + InstanceContext extends AlertInstanceContext = never > { id: string; name: string; validate?: { - params?: { validate: (object: unknown) => Params }; + params?: AlertTypeParamsValidator; }; actionGroups: ActionGroup[]; defaultActionGroupId: ActionGroup['id']; @@ -119,6 +121,13 @@ export interface AlertType< minimumLicenseRequired: LicenseType; } +export type UntypedAlertType = AlertType< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + export interface RawAlertAction extends SavedObjectAttributes { group: string; actionRef: string; @@ -142,7 +151,8 @@ export interface RawAlertExecutionStatus extends SavedObjectAttributes { }; } -export type PartialAlert = Pick & Partial>; +export type PartialAlert = Pick, 'id'> & + Partial, 'id'>>; export interface RawAlert extends SavedObjectAttributes { enabled: boolean; diff --git a/x-pack/plugins/infra/public/alerting/inventory/index.ts b/x-pack/plugins/infra/public/alerting/inventory/index.ts index 13ce43f77c8b03..da85f363b16ec1 100644 --- a/x-pack/plugins/infra/public/alerting/inventory/index.ts +++ b/x-pack/plugins/infra/public/alerting/inventory/index.ts @@ -5,13 +5,21 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/inventory_metric_threshold/types'; +import { + InventoryMetricConditions, + METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/inventory_metric_threshold/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { AlertTypeParams } from '../../../../alerts/common'; import { validateMetricThreshold } from './components/validation'; -export function createInventoryMetricAlertType(): AlertTypeModel { +interface InventoryMetricAlertTypeParams extends AlertTypeParams { + criteria: InventoryMetricConditions[]; +} + +export function createInventoryMetricAlertType(): AlertTypeModel { return { id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.inventory.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts index cccd5fbc439d7a..9c32c473f45978 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts @@ -8,10 +8,18 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { validateMetricThreshold } from './components/validation'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/metric_threshold/types'; +import { AlertTypeParams } from '../../../../alerts/common'; +import { + MetricExpressionParams, + METRIC_THRESHOLD_ALERT_TYPE_ID, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/metric_threshold/types'; + +interface MetricThresholdAlertTypeParams extends AlertTypeParams { + criteria: MetricExpressionParams[]; +} -export function createMetricThresholdAlertType(): AlertTypeModel { +export function createMetricThresholdAlertType(): AlertTypeModel { return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.metrics.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 1941ec6326ddb6..54cf8658a3f0d6 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -9,7 +9,11 @@ import moment from 'moment'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { AlertStates, InventoryMetricConditions } from './types'; -import { RecoveredActionGroup } from '../../../../../alerts/common'; +import { + AlertInstanceContext, + AlertInstanceState, + RecoveredActionGroup, +} from '../../../../../alerts/common'; import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; import { InfraBackendLibs } from '../../infra_types'; @@ -35,7 +39,15 @@ interface InventoryMetricThresholdParams { export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) => async ({ services, params, -}: AlertExecutorOptions) => { +}: AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>) => { const { criteria, filterQuery, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts index 2d1df6e8cb4627..a2e8eff34ef982 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts @@ -5,7 +5,7 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { AlertType } from '../../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../../alerts/server'; import { createInventoryMetricThresholdExecutor, FIRED_ACTIONS, @@ -40,7 +40,17 @@ const condition = schema.object({ ), }); -export const registerMetricInventoryThresholdAlertType = (libs: InfraBackendLibs): AlertType => ({ +export const registerMetricInventoryThresholdAlertType = ( + libs: InfraBackendLibs +): AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +> => ({ id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.inventory.alertName', { defaultMessage: 'Inventory', diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index d3d34cd2aad589..dccab5168fb60a 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -9,7 +9,10 @@ import { AlertExecutorOptions, AlertServices, AlertInstance, + AlertTypeParams, + AlertTypeState, AlertInstanceContext, + AlertInstanceState, } from '../../../../../alerts/server'; import { AlertStates, @@ -34,6 +37,14 @@ import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { decodeOrThrow } from '../../../../common/runtime_types'; import { UNGROUPED_FACTORY_KEY } from '../common/utils'; +type LogThresholdAlertServices = AlertServices; +type LogThresholdAlertExecutorOptions = AlertExecutorOptions< + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; + const COMPOSITE_GROUP_SIZE = 40; const checkValueAgainstComparatorMap: { @@ -46,7 +57,7 @@ const checkValueAgainstComparatorMap: { }; export const createLogThresholdExecutor = (libs: InfraBackendLibs) => - async function ({ services, params }: AlertExecutorOptions) { + async function ({ services, params }: LogThresholdAlertExecutorOptions) { const { alertInstanceFactory, savedObjectsClient, callCluster } = services; const { sources } = libs; @@ -88,8 +99,8 @@ async function executeAlert( alertParams: CountAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { const query = getESQuery(alertParams, timestampField, indexPattern); @@ -118,8 +129,8 @@ async function executeRatioAlert( alertParams: RatioAlertParams, timestampField: string, indexPattern: string, - callCluster: AlertServices['callCluster'], - alertInstanceFactory: AlertServices['alertInstanceFactory'] + callCluster: LogThresholdAlertServices['callCluster'], + alertInstanceFactory: LogThresholdAlertServices['alertInstanceFactory'] ) { // Ratio alert params are separated out into two standard sets of alert params const numeratorParams: AlertParams = { @@ -175,7 +186,7 @@ const getESQuery = ( export const processUngroupedResults = ( results: UngroupedSearchQueryResponse, params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -204,7 +215,7 @@ export const processUngroupedRatioResults = ( numeratorResults: UngroupedSearchQueryResponse, denominatorResults: UngroupedSearchQueryResponse, params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -259,7 +270,7 @@ const getReducedGroupByResults = ( export const processGroupByResults = ( results: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: CountAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -292,7 +303,7 @@ export const processGroupByRatioResults = ( numeratorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], denominatorResults: GroupedSearchQueryResponse['aggregations']['groups']['buckets'], params: RatioAlertParams, - alertInstanceFactory: AlertExecutorOptions['services']['alertInstanceFactory'], + alertInstanceFactory: LogThresholdAlertExecutorOptions['services']['alertInstanceFactory'], alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; @@ -599,11 +610,17 @@ const getQueryMappingForComparator = (comparator: Comparator) => { return queryMappings[comparator]; }; -const getUngroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getUngroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { return decodeOrThrow(UngroupedSearchQueryResponseRT)(await callCluster('search', query)); }; -const getGroupedResults = async (query: object, callCluster: AlertServices['callCluster']) => { +const getGroupedResults = async ( + query: object, + callCluster: LogThresholdAlertServices['callCluster'] +) => { let compositeGroupBuckets: GroupedSearchQueryResponse['aggregations']['groups']['buckets'] = []; let lastAfterKey: GroupedSearchQueryResponse['aggregations']['groups']['after_key'] | undefined; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts index 49f82c7ccec0b8..d51d9435fc9043 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/evaluate_alert.ts @@ -12,7 +12,7 @@ import { import { InfraSource } from '../../../../../common/http_api/source_api'; import { InfraDatabaseSearchResponse } from '../../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../../utils/create_afterkey_handler'; -import { AlertServices, AlertExecutorOptions } from '../../../../../../alerts/server'; +import { AlertServices } from '../../../../../../alerts/server'; import { getAllCompositeData } from '../../../../utils/get_all_composite_data'; import { DOCUMENT_COUNT_I18N } from '../../common/messages'; import { UNGROUPED_FACTORY_KEY } from '../../common/utils'; @@ -35,17 +35,19 @@ interface CompositeAggregationsResponse { }; } -export const evaluateAlert = ( +export interface EvaluatedAlertParams { + criteria: MetricExpressionParams[]; + groupBy: string | undefined | string[]; + filterQuery: string | undefined; +} + +export const evaluateAlert = ( callCluster: AlertServices['callCluster'], - params: AlertExecutorOptions['params'], + params: Params, config: InfraSource['configuration'], timeframe?: { start: number; end: number } ) => { - const { criteria, groupBy, filterQuery } = params as { - criteria: MetricExpressionParams[]; - groupBy: string | undefined | string[]; - filterQuery: string | undefined; - }; + const { criteria, groupBy, filterQuery } = params; return Promise.all( criteria.map(async (criterion) => { const currentValues = await getMetric( diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index a1d6428f3b52be..6c9fac9d1133c1 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -7,13 +7,13 @@ import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { alertsMock, AlertServicesMock, AlertInstanceMock, } from '../../../../../alerts/server/mocks'; import { InfraSources } from '../../sources'; +import { MetricThresholdAlertExecutorOptions } from './register_metric_threshold_alert_type'; interface AlertTestInstance { instance: AlertInstanceMock; @@ -23,11 +23,23 @@ interface AlertTestInstance { let persistAlertInstances = false; +const mockOptions = { + alertId: '', + startedAt: new Date(), + previousStartedAt: null, + state: {}, + spaceId: '', + name: '', + tags: [], + createdBy: null, + updatedBy: null, +}; + describe('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => - executor({ + executor(({ services, params: { sourceId, @@ -39,7 +51,10 @@ describe('The metric threshold alert type', () => { }, ], }, - }); + /** + * TODO: Remove this use of `as` by utilizing a proper type + */ + } as unknown) as MetricThresholdAlertExecutorOptions); test('alerts as expected with the > comparator', async () => { await execute(Comparator.GT, [0.75]); expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); @@ -109,6 +124,7 @@ describe('The metric threshold alert type', () => { describe('querying with a groupBy parameter', () => { const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { groupBy: 'something', @@ -159,6 +175,7 @@ describe('The metric threshold alert type', () => { groupBy: string = '' ) => executor({ + ...mockOptions, services, params: { groupBy, @@ -216,6 +233,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -242,6 +260,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -268,6 +287,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (comparator: Comparator, threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -294,6 +314,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (alertOnNoData: boolean) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -323,6 +344,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { criteria: [ @@ -348,6 +370,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = (threshold: number[]) => executor({ + ...mockOptions, services, params: { criteria: [ @@ -392,6 +415,7 @@ describe('The metric threshold alert type', () => { const instanceID = '*'; const execute = () => executor({ + ...mockOptions, services, params: { sourceId: 'default', @@ -435,10 +459,7 @@ const mockLibs: any = { configuration: createMockStaticConfiguration({}), }; -const executor = createMetricThresholdExecutor(mockLibs) as (opts: { - params: AlertExecutorOptions['params']; - services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; -}) => Promise; +const executor = createMetricThresholdExecutor(mockLibs); const services: AlertServicesMock = alertsMock.createAlertServices(); services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 60790648d9a9b9..d63b42cd3b146a 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -7,7 +7,6 @@ import { first, last } from 'lodash'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { RecoveredActionGroup } from '../../../../../alerts/common'; -import { AlertExecutorOptions } from '../../../../../alerts/server'; import { InfraBackendLibs } from '../../infra_types'; import { buildErrorAlertReason, @@ -18,10 +17,16 @@ import { } from '../common/messages'; import { createFormatter } from '../../../../common/formatters'; import { AlertStates } from './types'; -import { evaluateAlert } from './lib/evaluate_alert'; +import { evaluateAlert, EvaluatedAlertParams } from './lib/evaluate_alert'; +import { + MetricThresholdAlertExecutorOptions, + MetricThresholdAlertType, +} from './register_metric_threshold_alert_type'; -export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => - async function (options: AlertExecutorOptions) { +export const createMetricThresholdExecutor = ( + libs: InfraBackendLibs +): MetricThresholdAlertType['executor'] => + async function (options: MetricThresholdAlertExecutorOptions) { const { services, params } = options; const { criteria } = params; if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); @@ -36,7 +41,11 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => sourceId || 'default' ); const config = source.configuration; - const alertResults = await evaluateAlert(services.callCluster, params, config); + const alertResults = await evaluateAlert( + services.callCluster, + params as EvaluatedAlertParams, + config + ); // Because each alert result has the same group definitions, just grab the groups from the first one. const groups = Object.keys(first(alertResults)!); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index f04a1015bcbcde..000c89f5899ef0 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -5,7 +5,12 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertInstanceState, + AlertInstanceContext, + AlertExecutorOptions, +} from '../../../../../alerts/server'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; @@ -21,7 +26,26 @@ import { thresholdActionVariableDescription, } from '../common/messages'; -export function registerMetricThresholdAlertType(libs: InfraBackendLibs): AlertType { +export type MetricThresholdAlertType = AlertType< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; +export type MetricThresholdAlertExecutorOptions = AlertExecutorOptions< + /** + * TODO: Remove this use of `any` by utilizing a proper type + */ + Record, + Record, + AlertInstanceState, + AlertInstanceContext +>; + +export function registerMetricThresholdAlertType(libs: InfraBackendLibs): MetricThresholdAlertType { const baseCriterion = { threshold: schema.arrayOf(schema.number()), comparator: oneOfLiterals(Object.values(Comparator)), diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts index 93807f9df12b08..df6e169f37f7a2 100644 --- a/x-pack/plugins/monitoring/common/types/alerts.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, SanitizedAlert } from '../../../alerts/common'; import { AlertParamType, AlertMessageTokenType, AlertSeverity } from '../enums'; -export type CommonAlert = Alert | SanitizedAlert; +export type CommonAlert = Alert | SanitizedAlert; export interface CommonAlertStatus { states: CommonAlertState[]; - rawAlert: Alert | SanitizedAlert; + rawAlert: Alert | SanitizedAlert; } export interface CommonAlertState { diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx index 4d22d422ecda6f..6d7751d91b7619 100644 --- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx @@ -9,8 +9,9 @@ import { i18n } from '@kbn/i18n'; import { Expression, Props } from '../components/duration/expression'; import { AlertTypeModel, ValidationResult } from '../../../../triggers_actions_ui/public'; import { ALERT_CCR_READ_EXCEPTIONS, ALERT_DETAILS } from '../../../common/constants'; +import { AlertTypeParams } from '../../../../alerts/common'; -interface ValidateOptions { +interface ValidateOptions extends AlertTypeParams { duration: string; } @@ -30,7 +31,7 @@ const validate = (inputValues: ValidateOptions): ValidationResult => { return validationResult; }; -export function createCCRReadExceptionsAlertType(): AlertTypeModel { +export function createCCRReadExceptionsAlertType(): AlertTypeModel { return { id: ALERT_CCR_READ_EXCEPTIONS, description: ALERT_DETAILS[ALERT_CCR_READ_EXCEPTIONS].description, diff --git a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx index 892ee0926ca385..c4e5ff343da176 100644 --- a/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx +++ b/x-pack/plugins/monitoring/public/alerts/components/duration/validation.tsx @@ -5,9 +5,11 @@ */ import { i18n } from '@kbn/i18n'; +import { AlertTypeParams } from '../../../../../alerts/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; +export type MonitoringAlertTypeParams = ValidateOptions & AlertTypeParams; interface ValidateOptions { duration: string; threshold: number; diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index 1fe40fc8777f4b..d2cec006b1b1d5 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -7,10 +7,10 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_CPU_USAGE, ALERT_DETAILS } from '../../../common/constants'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; -export function createCpuUsageAlertType(): AlertTypeModel { +export function createCpuUsageAlertType(): AlertTypeModel { return { id: ALERT_CPU_USAGE, description: ALERT_DETAILS[ALERT_CPU_USAGE].description, diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx index 5579b8e1275a39..bea399ee89f6a3 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_DISK_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createDiskUsageAlertType(): AlertTypeModel { +export function createDiskUsageAlertType(): AlertTypeModel { return { id: ALERT_DISK_USAGE, description: ALERT_DETAILS[ALERT_DISK_USAGE].description, diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index 0400810a8c3790..0428e4e7c733e4 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -5,14 +5,14 @@ */ import React from 'react'; -import { validate } from '../components/duration/validation'; +import { validate, MonitoringAlertTypeParams } from '../components/duration/validation'; import { Expression, Props } from '../components/duration/expression'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; import { ALERT_MEMORY_USAGE, ALERT_DETAILS } from '../../../common/constants'; -export function createMemoryUsageAlertType(): AlertTypeModel { +export function createMemoryUsageAlertType(): AlertTypeModel { return { id: ALERT_MEMORY_USAGE, description: ALERT_DETAILS[ALERT_MEMORY_USAGE].description, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index a3bcc310b80847..46adfebfd17bfa 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -13,7 +13,7 @@ import { AlertsClient, AlertServices, } from '../../../alerts/server'; -import { Alert, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; +import { Alert, AlertTypeParams, RawAlertInstance, SanitizedAlert } from '../../../alerts/common'; import { ActionsClient } from '../../../actions/server'; import { AlertState, @@ -135,7 +135,7 @@ export class BaseAlert { alertsClient: AlertsClient, actionsClient: ActionsClient, actions: AlertEnableAction[] - ): Promise { + ): Promise> { const existingAlertData = await alertsClient.find({ options: { search: this.alertOptions.id, @@ -170,7 +170,7 @@ export class BaseAlert { throttle = '1d', interval = '1m', } = this.alertOptions; - return await alertsClient.create({ + return await alertsClient.create({ data: { enabled: true, tags: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts index 5731a51aeabc19..f0895f13672892 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/create_notifications.ts @@ -6,7 +6,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, NOTIFICATIONS_ID } from '../../../../common/constants'; -import { CreateNotificationParams } from './types'; +import { CreateNotificationParams, RuleNotificationAlertTypeParams } from './types'; import { addTags } from './add_tags'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -17,8 +17,8 @@ export const createNotifications = async ({ ruleAlertId, interval, name, -}: CreateNotificationParams): Promise => - alertsClient.create({ +}: CreateNotificationParams): Promise> => + alertsClient.create({ data: { name, tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts index 5d3a328dd6fbb9..1bf07a1574be6f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/find_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FindResult } from '../../../../../alerts/server'; +import { AlertTypeParams, FindResult } from '../../../../../alerts/server'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { FindNotificationParams } from './types'; @@ -24,7 +24,7 @@ export const findNotifications = async ({ filter, sortField, sortOrder, -}: FindNotificationParams): Promise => +}: FindNotificationParams): Promise> => alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts index fe9101335b4f54..74df77bf9bf74d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/read_notifications.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SanitizedAlert } from '../../../../../alerts/common'; +import { AlertTypeParams, SanitizedAlert } from '../../../../../alerts/common'; import { ReadNotificationParams, isAlertType } from './types'; import { findNotifications } from './find_notifications'; import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; @@ -13,7 +13,7 @@ export const readNotifications = async ({ alertsClient, id, ruleAlertId, -}: ReadNotificationParams): Promise => { +}: ReadNotificationParams): Promise | null> => { if (id != null) { try { const notification = await alertsClient.get({ id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts index cc9fb149a7e1b3..e4e9df552101b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/types.ts @@ -8,18 +8,20 @@ import { AlertsClient, PartialAlert, AlertType, + AlertTypeParams, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, } from '../../../../../alerts/server'; import { Alert } from '../../../../../alerts/common'; import { NOTIFICATIONS_ID } from '../../../../common/constants'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -export interface RuleNotificationAlertType extends Alert { - params: { - ruleAlertId: string; - }; +export interface RuleNotificationAlertTypeParams extends AlertTypeParams { + ruleAlertId: string; } +export type RuleNotificationAlertType = Alert; export interface FindNotificationParams { alertsClient: AlertsClient; @@ -76,32 +78,36 @@ export interface ReadNotificationParams { } export const isAlertTypes = ( - partialAlert: PartialAlert[] + partialAlert: Array> ): partialAlert is RuleNotificationAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; export const isAlertType = ( - partialAlert: PartialAlert + partialAlert: PartialAlert ): partialAlert is RuleNotificationAlertType => { return partialAlert.alertTypeId === NOTIFICATIONS_ID; }; -export type NotificationExecutorOptions = Omit & { - params: { - ruleAlertId: string; - }; -}; +export type NotificationExecutorOptions = AlertExecutorOptions< + RuleNotificationAlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a NotificationAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. export const isNotificationAlertExecutor = ( obj: NotificationAlertTypeDefinition -): obj is AlertType => { +): obj is AlertType => { return true; }; -export type NotificationAlertTypeDefinition = Omit & { +export type NotificationAlertTypeDefinition = Omit< + AlertType, + 'executor' +> & { executor: ({ services, params, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts index d6c8973215117e..8528d53b51f5bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/update_notifications.ts @@ -6,7 +6,7 @@ import { PartialAlert } from '../../../../../alerts/server'; import { readNotifications } from './read_notifications'; -import { UpdateNotificationParams } from './types'; +import { RuleNotificationAlertTypeParams, UpdateNotificationParams } from './types'; import { addTags } from './add_tags'; import { createNotifications } from './create_notifications'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; @@ -18,11 +18,11 @@ export const updateNotifications = async ({ ruleAlertId, name, interval, -}: UpdateNotificationParams): Promise => { +}: UpdateNotificationParams): Promise | null> => { const notification = await readNotifications({ alertsClient, id: undefined, ruleAlertId }); if (interval && notification) { - return alertsClient.update({ + return alertsClient.update({ id: notification.id, data: { tags: addTags([], ruleAlertId), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index b185b8780abe29..3473948b000c75 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -22,6 +22,8 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { transformBulkError, createBulkErrorObject, buildSiemResponse } from '../utils'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { router.post( @@ -95,9 +97,12 @@ export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => }); } - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index b52248f6701882..c59d5d2a36fd5e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -19,6 +19,8 @@ import { createRulesSchema } from '../../../../../common/detection_engine/schema import { newTransformValidate } from './validate'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleTypeParams } from '../../types'; +import { Alert } from '../../../../../../alerts/common'; export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void => { router.post( @@ -85,9 +87,12 @@ export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void // This will create the endpoint list if it does not exist yet await context.lists?.getExceptionListClient().createEndpointList(); - const createdRule = await alertsClient.create({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const createdRule = (await alertsClient.create({ data: internalRule, - }); + })) as Alert; const ruleActions = await updateRulesNotifications({ ruleAlertId: createdRule.id, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index 7a6cd707eb1859..bd7f11b2d8756c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -31,6 +31,7 @@ import { OutputError, } from '../utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; type PromiseFromStreams = ImportRulesSchemaDecoded | Error; @@ -172,7 +173,7 @@ export const transformAlertsToRules = (alerts: RuleAlertType[]): Array, ruleActions: Array, ruleStatuses?: Array> ): { @@ -203,7 +204,7 @@ export const transformFindAlerts = ( }; export const transform = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): Partial | null => { @@ -220,7 +221,7 @@ export const transform = ( export const transformOrBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions: RuleActions, ruleStatus?: unknown ): Partial | BulkError => { @@ -241,7 +242,7 @@ export const transformOrBulkError = ( export const transformOrImportError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, existingImportSuccessError: ImportSuccessError ): ImportSuccessError => { if (isAlertType(alert)) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts index 8653bdc0427e44..51b08cb3fce899 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.test.ts @@ -15,6 +15,7 @@ import { RulesSchema } from '../../../../../common/detection_engine/schemas/resp import { getResult, getFindResultStatus } from '../__mocks__/request_responses'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../../common/detection_engine/schemas/types/threat.mock'; +import { RuleTypeParams } from '../../types'; export const ruleOutput = (): RulesSchema => ({ actions: [], @@ -88,7 +89,7 @@ describe('validate', () => { describe('transformValidateFindAlerts', () => { test('it should do a validation correctly of a find alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, @@ -111,7 +112,7 @@ describe('validate', () => { }); test('it should do an in-validation correctly of a partial alert', () => { - const findResult: FindResult = { + const findResult: FindResult = { data: [getResult()], page: 1, perPage: 0, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts index 382186df16cd18..3e3b85a407fc21 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/validate.ts @@ -31,9 +31,10 @@ import { import { createBulkErrorObject, BulkError } from '../utils'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; import { RuleActions } from '../../rule_actions/types'; +import { RuleTypeParams } from '../../types'; export const transformValidateFindAlerts = ( - findResults: FindResult, + findResults: FindResult, ruleActions: Array, ruleStatuses?: Array> ): [ @@ -63,7 +64,7 @@ export const transformValidateFindAlerts = ( }; export const transformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [RulesSchema | null, string | null] => { @@ -76,7 +77,7 @@ export const transformValidate = ( }; export const newTransformValidate = ( - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObject ): [FullResponseSchema | null, string | null] => { @@ -90,7 +91,7 @@ export const newTransformValidate = ( export const transformValidateBulkError = ( ruleId: string, - alert: PartialAlert, + alert: PartialAlert, ruleActions?: RuleActions | null, ruleStatus?: SavedObjectsFindResponse ): RulesSchema | BulkError => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index 0519a98df1fae8..6339e92eb012b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -9,6 +9,7 @@ import { Alert } from '../../../../../alerts/common'; import { SERVER_APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRulesOptions } from './types'; import { addTags } from './add_tags'; +import { PartialFilter, RuleTypeParams } from '../types'; export const createRules = async ({ alertsClient, @@ -59,8 +60,8 @@ export const createRules = async ({ version, exceptionsList, actions, -}: CreateRulesOptions): Promise => { - return alertsClient.create({ +}: CreateRulesOptions): Promise> => { + return alertsClient.create({ data: { name, tags: addTags(tags, ruleId, immutable), @@ -95,7 +96,10 @@ export const createRules = async ({ severityMapping, threat, threshold, - threatFilters, + /** + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + */ + threatFilters: threatFilters as PartialFilter[] | undefined, threatIndex, threatQuery, concurrentSearches, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts index 18b851c440e20a..5ab97262515dad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/find_rules.ts @@ -6,6 +6,7 @@ import { FindResult } from '../../../../../alerts/server'; import { SIGNALS_ID } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { FindRuleOptions } from './types'; export const getFilter = (filter: string | null | undefined) => { @@ -24,7 +25,7 @@ export const findRules = async ({ filter, sortField, sortOrder, -}: FindRuleOptions): Promise => { +}: FindRuleOptions): Promise> => { return alertsClient.find({ options: { fields, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 4c01318f02cdea..1f43706c17fec8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -5,7 +5,7 @@ */ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { Alert } from '../../../../../alerts/common'; +import { Alert, AlertTypeParams } from '../../../../../alerts/common'; import { AlertsClient } from '../../../../../alerts/server'; import { createRules } from './create_rules'; import { PartialFilter } from '../types'; @@ -14,8 +14,8 @@ export const installPrepackagedRules = ( alertsClient: AlertsClient, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string -): Array> => - rules.reduce>>((acc, rule) => { +): Array>> => + rules.reduce>>>((acc, rule) => { const { anomaly_threshold: anomalyThreshold, author, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index b2303d48b0517a..484dac8e31fb4c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -9,8 +9,9 @@ import { alertsClientMock } from '../../../../../alerts/server/mocks'; import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; import { SanitizedAlert } from '../../../../../alerts/common'; +import { RuleTypeParams } from '../types'; -const rule: SanitizedAlert = { +const rule: SanitizedAlert = { id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', name: 'Detect Root/Admin Users', tags: [`${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:false`], @@ -67,6 +68,8 @@ const rule: SanitizedAlert = { note: '# Investigative notes', version: 1, exceptionsList: [ + /** + TODO: fix this mock. Which the typing has revealed is wrong { field: 'source.ip', values_operator: 'included', @@ -96,8 +99,31 @@ const rule: SanitizedAlert = { ], }, ], - }, + },*/ ], + /** + * The fields below were missing as the type was partial and hence not technically correct + */ + author: [], + buildingBlockType: undefined, + eventCategoryOverride: undefined, + license: undefined, + savedId: undefined, + interval: undefined, + riskScoreMapping: undefined, + ruleNameOverride: undefined, + name: undefined, + severityMapping: undefined, + tags: undefined, + threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, + concurrentSearches: undefined, + itemsPerSearch: undefined, + timestampOverride: undefined, }, createdAt: new Date('2019-12-13T16:40:33.400Z'), updatedAt: new Date('2019-12-13T16:40:33.400Z'), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index c86526cee9302f..c1720c4fa35875 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -13,6 +13,7 @@ import { addTags } from './add_tags'; import { calculateVersion, calculateName, calculateInterval, removeUndefined } from './utils'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { internalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; class PatchError extends Error { public readonly statusCode: number; @@ -71,7 +72,7 @@ export const patchRules = async ({ anomalyThreshold, machineLearningJobId, actions, -}: PatchRulesOptions): Promise => { +}: PatchRulesOptions): Promise | null> => { if (rule == null) { return null; } @@ -185,10 +186,13 @@ export const patchRules = async ({ throw new PatchError(`Applying patch would create invalid rule: ${errors}`, 400); } - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: rule.id, data: validated, - }); + })) as PartialAlert; if (rule.enabled && enabled === false) { await alertsClient.disable({ id: rule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts index e4bb65a907e2d5..bc277bd2089a6f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/read_rules.ts @@ -6,6 +6,7 @@ import { SanitizedAlert } from '../../../../../alerts/common'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; +import { RuleTypeParams } from '../types'; import { findRules } from './find_rules'; import { isAlertType, ReadRuleOptions } from './types'; @@ -21,7 +22,7 @@ export const readRules = async ({ alertsClient, id, ruleId, -}: ReadRuleOptions): Promise => { +}: ReadRuleOptions): Promise | null> => { if (id != null) { try { const rule = await alertsClient.get({ id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index aaeb487ad9a78c..34dab20c279b46 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -103,9 +103,7 @@ import { SIGNALS_ID } from '../../../../common/constants'; import { RuleTypeParams, PartialFilter } from '../types'; import { ListArrayOrUndefined, ListArray } from '../../../../common/detection_engine/schemas/types'; -export interface RuleAlertType extends Alert { - params: RuleTypeParams; -} +export type RuleAlertType = Alert; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface IRuleStatusSOAttributes extends Record { @@ -173,11 +171,15 @@ export interface Clients { alertsClient: AlertsClient; } -export const isAlertTypes = (partialAlert: PartialAlert[]): partialAlert is RuleAlertType[] => { +export const isAlertTypes = ( + partialAlert: Array> +): partialAlert is RuleAlertType[] => { return partialAlert.every((rule) => isAlertType(rule)); }; -export const isAlertType = (partialAlert: PartialAlert): partialAlert is RuleAlertType => { +export const isAlertType = ( + partialAlert: PartialAlert +): partialAlert is RuleAlertType => { return partialAlert.alertTypeId === SIGNALS_ID; }; @@ -305,7 +307,7 @@ export interface PatchRulesOptions { version: VersionOrUndefined; exceptionsList: ListArrayOrUndefined; actions: RuleAlertAction[] | undefined; - rule: SanitizedAlert | null; + rule: SanitizedAlert | null; } export interface ReadRuleOptions { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index c63bd01cd18133..b3e0aaae715c98 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -15,13 +15,14 @@ import { addTags } from './add_tags'; import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_saved_objects_client'; import { typeSpecificSnakeToCamel } from '../schemas/rule_converters'; import { InternalRuleUpdate } from '../schemas/rule_schemas'; +import { RuleTypeParams } from '../types'; export const updateRules = async ({ alertsClient, savedObjectsClient, defaultOutputIndex, ruleUpdate, -}: UpdateRulesOptions): Promise => { +}: UpdateRulesOptions): Promise | null> => { const existingRule = await readRules({ alertsClient, ruleId: ruleUpdate.rule_id, @@ -77,10 +78,13 @@ export const updateRules = async ({ notifyWhen: null, }; - const update = await alertsClient.update({ + /** + * TODO: Remove this use of `as` by utilizing the proper type + */ + const update = (await alertsClient.update({ id: existingRule.id, data: newInternalRule, - }); + })) as PartialAlert; if (existingRule.enabled && enabled === false) { await alertsClient.disable({ id: existingRule.id }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts index 50e740e81830f8..aede91c5af1436 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts @@ -8,7 +8,7 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; -const signalSchema = schema.object({ +export const signalSchema = schema.object({ anomalyThreshold: schema.maybe(schema.number()), author: schema.arrayOf(schema.string(), { defaultValue: [] }), buildingBlockType: schema.nullable(schema.string()), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 9a40573095a1a8..9c2ea0945297e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -49,7 +49,10 @@ jest.mock('./find_ml_signals'); jest.mock('./bulk_create_ml_signals'); jest.mock('../../../../common/detection_engine/parse_schedule_dates'); -const getPayload = (ruleAlert: RuleAlertType, services: AlertServicesMock) => ({ +const getPayload = ( + ruleAlert: RuleAlertType, + services: AlertServicesMock +): RuleExecutorOptions => ({ alertId: ruleAlert.id, services, params: { 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 3928228357d4cb..476b9aa56f572d 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 @@ -66,6 +66,7 @@ import { getIndexVersion } from '../routes/index/get_index_version'; import { MIN_EQL_RULE_INDEX_VERSION } from '../routes/index/get_signals_template'; import { filterEventsAgainstList } from './filters/filter_events_against_list'; import { isOutdated } from '../migrations/helpers'; +import { RuleTypeParams } from '../types'; export const signalRulesAlertType = ({ logger, @@ -86,7 +87,16 @@ export const signalRulesAlertType = ({ actionGroups: siemRuleActionGroups, defaultActionGroupId: 'default', validate: { - params: signalParamsSchema(), + /** + * TODO: Fix typing inconsistancy between `RuleTypeParams` and `CreateRulesOptions` + * Once that's done, you should be able to do: + * ``` + * params: signalParamsSchema(), + * ``` + */ + params: (signalParamsSchema() as unknown) as { + validate: (object: unknown) => RuleTypeParams; + }, }, producer: SERVER_APP_ID, minimumLicenseRequired: 'basic', 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 4167d056df885c..62339f50d939cd 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 @@ -11,6 +11,8 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons import { AlertType, AlertTypeState, + AlertInstanceState, + AlertInstanceContext, AlertExecutorOptions, AlertServices, } from '../../../../../alerts/server'; @@ -128,19 +130,27 @@ export type BaseSignalHit = BaseHit; export type EqlSignalSearchResponse = EqlSearchResponse; -export type RuleExecutorOptions = Omit & { - params: RuleTypeParams; -}; +export type RuleExecutorOptions = AlertExecutorOptions< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; // This returns true because by default a RuleAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. -export const isAlertExecutor = (obj: SignalRuleAlertTypeDefinition): obj is AlertType => { +export const isAlertExecutor = ( + obj: SignalRuleAlertTypeDefinition +): obj is AlertType => { return true; }; -export type SignalRuleAlertTypeDefinition = Omit & { - executor: ({ services, params, state }: RuleExecutorOptions) => Promise; -}; +export type SignalRuleAlertTypeDefinition = AlertType< + RuleTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; export interface Ancestor { rule?: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts index 57535178c52801..90f2ce638855b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts @@ -51,10 +51,11 @@ import { import { LegacyCallAPIOptions } from '../../../../../../src/core/server'; import { Filter } from '../../../../../../src/plugins/data/server'; import { ListArrayOrUndefined } from '../../../common/detection_engine/schemas/types'; +import { AlertTypeParams } from '../../../../alerts/common'; export type PartialFilter = Partial; -export interface RuleTypeParams { +export interface RuleTypeParams extends AlertTypeParams { anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts index 89252f7c901042..d1f64c9298f15d 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/types.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; -export interface GeoContainmentAlertParams { +export interface GeoContainmentAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts index 5ac9c7fd29317d..3f487135f0474d 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_threshold/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; import { Query } from '../../../../../../src/plugins/data/common'; export enum TrackingEvent { @@ -12,7 +13,7 @@ export enum TrackingEvent { crossed = 'crossed', } -export interface GeoThresholdAlertParams { +export interface GeoThresholdAlertParams extends AlertTypeParams { index: string; indexId: string; geoField: string; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx index 3c84f2a5d4f9c0..12e021958f4971 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/expression.tsx @@ -71,6 +71,10 @@ interface KibanaDeps { http: HttpSetup; } +function isString(value: unknown): value is string { + return typeof value === 'string'; +} + export const IndexThresholdAlertTypeExpression: React.FunctionComponent< AlertTypeParamsExpressionProps > = ({ alertParams, alertInterval, setAlertParams, setAlertProperty, errors, charts, data }) => { @@ -190,10 +194,10 @@ export const IndexThresholdAlertTypeExpression: React.FunctionComponent< }; })} onChange={async (selected: EuiComboBoxOptionOption[]) => { - setAlertParams( - 'index', - selected.map((aSelected) => aSelected.value) - ); + const indicies: string[] = selected + .map((aSelected) => aSelected.value) + .filter(isString); + setAlertParams('index', indicies); const indices = selected.map((s) => s.value as string); // reset time field and expression fields if indices are deleted diff --git a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts index 356b0fbbc0845a..4868b92feaeb90 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts +++ b/x-pack/plugins/stack_alerts/public/alert_types/threshold/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { AlertTypeParams } from '../../../../alerts/common'; + export interface Comparator { text: string; value: string; @@ -24,7 +26,7 @@ export interface GroupByType { validNormalizedTypes: string[]; } -export interface IndexThresholdAlertParams { +export interface IndexThresholdAlertParams extends AlertTypeParams { index: string[]; timeField?: string; aggType: string; diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts index 51d7361bfe762b..85dcf1125becd0 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/alert_type.ts @@ -9,7 +9,13 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoContainmentExecutor } from './geo_containment'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + AlertTypeParams, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_CONTAINMENT_ID = '.geo-containment'; @@ -96,7 +102,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoContainmentParams { +export interface GeoContainmentParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -111,8 +117,34 @@ export interface GeoContainmentParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoContainmentState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; +} +export interface GeoContainmentInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + dateInShape: string | null; + docId: string; +} +export interface GeoContainmentInstanceContext extends AlertInstanceContext { + entityId: string; + entityDateTime: string | null; + entityDocumentId: string; + detectionDateTime: string; + entityLocation: string; + containingBoundaryId: string; + containingBoundaryName: unknown; +} + +export type GeoContainmentAlertType = AlertType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext +>; -export function getAlertType(logger: Logger): AlertType { +export function getAlertType(logger: Logger): GeoContainmentAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoContainment.alertTypeTitle', { defaultMessage: 'Tracking containment', }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts index ed951f340f8ed2..612eff30149856 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/geo_containment.ts @@ -8,15 +8,16 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_CONTAINMENT_ID, GeoContainmentParams } from './alert_type'; +import { AlertServices } from '../../../../alerts/server'; +import { + ActionGroupId, + GEO_CONTAINMENT_ID, + GeoContainmentInstanceState, + GeoContainmentAlertType, + GeoContainmentInstanceContext, +} from './alert_type'; -export interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoContainmentInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -97,9 +98,10 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { export function getActiveEntriesAndGenerateAlerts( prevLocationMap: Record, currLocationMap: Map, - alertInstanceFactory: ( - x: string - ) => { scheduleActions: (x: string, y: Record) => void }, + alertInstanceFactory: AlertServices< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >['alertInstanceFactory'], shapesIdsNamesMap: Record, currIntervalEndTime: Date ) { @@ -127,23 +129,8 @@ export function getActiveEntriesAndGenerateAlerts( }); return allActiveEntriesMap; } - -export const getGeoContainmentExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoContainmentParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoContainmentExecutor = (log: Logger): GeoContainmentAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts index 02116d0701bfab..86e9e4fa3d672f 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/index.ts @@ -6,7 +6,13 @@ import { Logger } from 'src/core/server'; import { AlertingSetup } from '../../types'; -import { GeoContainmentParams, getAlertType } from './alert_type'; +import { + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext, + getAlertType, +} from './alert_type'; interface RegisterParams { logger: Logger; @@ -15,5 +21,10 @@ interface RegisterParams { export function register(params: RegisterParams) { const { logger, alerts } = params; - alerts.registerType(getAlertType(logger)); + alerts.registerType< + GeoContainmentParams, + GeoContainmentState, + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(getAlertType(logger)); } diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts index 885081e859dd75..26b51060c2e734 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_containment/tests/geo_containment.test.ts @@ -10,6 +10,8 @@ import sampleJsonResponseWithNesting from './es_sample_response_with_nesting.jso import { getActiveEntriesAndGenerateAlerts, transformResults } from '../geo_containment'; import { SearchResponse } from 'elasticsearch'; import { OTHER_CATEGORY } from '../es_query_builder'; +import { alertsMock } from '../../../../../alerts/server/mocks'; +import { GeoContainmentInstanceContext, GeoContainmentInstanceState } from '../alert_type'; describe('geo_containment', () => { describe('transformResults', () => { @@ -191,8 +193,12 @@ describe('geo_containment', () => { const emptyShapesIdsNamesMap = {}; const alertInstanceFactory = (instanceId: string) => { - return { - scheduleActions: (actionGroupId: string, context: Record) => { + const alertInstance = alertsMock.createAlertInstanceFactory< + GeoContainmentInstanceState, + GeoContainmentInstanceContext + >(); + alertInstance.scheduleActions.mockImplementation( + (actionGroupId: string, context?: GeoContainmentInstanceContext) => { const contextKeys = Object.keys(expectedContext[0].context); const contextSubset = _.pickBy(context, (v, k) => contextKeys.includes(k)); testAlertActionArr.push({ @@ -200,9 +206,12 @@ describe('geo_containment', () => { instanceId, context: contextSubset, }); - }, - }; + return alertInstance; + } + ); + return alertInstance; }; + const currentDateTime = new Date(); it('should use currently active entities if no older entity entries', () => { diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts index bf5e2fe2289db8..2eccf2ff96fb03 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/alert_type.ts @@ -9,7 +9,13 @@ import { schema } from '@kbn/config-schema'; import { Logger } from 'src/core/server'; import { STACK_ALERTS_FEATURE_ID } from '../../../common'; import { getGeoThresholdExecutor } from './geo_threshold'; -import { AlertType } from '../../../../alerts/server'; +import { + AlertType, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, + AlertTypeParams, +} from '../../../../alerts/server'; import { Query } from '../../../../../../src/plugins/data/common/query'; export const GEO_THRESHOLD_ID = '.geo-threshold'; @@ -155,7 +161,7 @@ export const ParamsSchema = schema.object({ boundaryIndexQuery: schema.maybe(schema.any({})), }); -export interface GeoThresholdParams { +export interface GeoThresholdParams extends AlertTypeParams { index: string; indexId: string; geoField: string; @@ -171,8 +177,41 @@ export interface GeoThresholdParams { indexQuery?: Query; boundaryIndexQuery?: Query; } +export interface GeoThresholdState extends AlertTypeState { + shapesFilters: Record; + shapesIdsNamesMap: Record; + prevLocationArr: GeoThresholdInstanceState[]; +} +export interface GeoThresholdInstanceState extends AlertInstanceState { + location: number[]; + shapeLocationId: string; + entityName: string; + dateInShape: string | null; + docId: string; +} +export interface GeoThresholdInstanceContext extends AlertInstanceContext { + entityId: string; + timeOfDetection: number; + crossingLine: string; + toEntityLocation: string; + toEntityDateTime: string | null; + toEntityDocumentId: string; + toBoundaryId: string; + toBoundaryName: unknown; + fromEntityLocation: string; + fromEntityDateTime: string | null; + fromEntityDocumentId: string; + fromBoundaryId: string; + fromBoundaryName: unknown; +} -export function getAlertType(logger: Logger): AlertType { +export type GeoThresholdAlertType = AlertType< + GeoThresholdParams, + GeoThresholdState, + GeoThresholdInstanceState, + GeoThresholdInstanceContext +>; +export function getAlertType(logger: Logger): GeoThresholdAlertType { const alertTypeName = i18n.translate('xpack.stackAlerts.geoThreshold.alertTypeTitle', { defaultMessage: 'Tracking threshold', }); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts index 5cb4156e84623a..a2375537ae6e5c 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/geo_threshold/geo_threshold.ts @@ -8,16 +8,14 @@ import _ from 'lodash'; import { SearchResponse } from 'elasticsearch'; import { Logger } from 'src/core/server'; import { executeEsQueryFactory, getShapesFilters, OTHER_CATEGORY } from './es_query_builder'; -import { AlertServices, AlertTypeState } from '../../../../alerts/server'; -import { ActionGroupId, GEO_THRESHOLD_ID, GeoThresholdParams } from './alert_type'; +import { + ActionGroupId, + GEO_THRESHOLD_ID, + GeoThresholdAlertType, + GeoThresholdInstanceState, +} from './alert_type'; -interface LatestEntityLocation { - location: number[]; - shapeLocationId: string; - entityName: string; - dateInShape: string | null; - docId: string; -} +export type LatestEntityLocation = GeoThresholdInstanceState; // Flatten agg results and get latest locations for each entity export function transformResults( @@ -172,22 +170,8 @@ function getOffsetTime(delayOffsetWithUnits: string, oldTime: Date): Date { return adjustedDate; } -export const getGeoThresholdExecutor = (log: Logger) => - async function ({ - previousStartedAt, - startedAt, - services, - params, - alertId, - state, - }: { - previousStartedAt: Date | null; - startedAt: Date; - services: AlertServices; - params: GeoThresholdParams; - alertId: string; - state: AlertTypeState; - }): Promise { +export const getGeoThresholdExecutor = (log: Logger): GeoThresholdAlertType['executor'] => + async function ({ previousStartedAt, startedAt, services, params, alertId, state }) { const { shapesFilters, shapesIdsNamesMap } = state.shapesFilters ? state : await getShapesFilters( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index 538c6be89ab4b5..ea654bb21e88b9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Alert, AlertType } from '../../types'; +import { Alert, AlertType, AlertUpdates } from '../../types'; import { httpServiceMock } from '../../../../../../src/core/public/mocks'; import { createAlert, @@ -538,7 +538,7 @@ describe('deleteAlerts', () => { describe('createAlert', () => { test('should call create alert API', async () => { - const alertToCreate = { + const alertToCreate: AlertUpdates = { name: 'test', consumer: 'alerts', tags: ['foo'], @@ -553,10 +553,13 @@ describe('createAlert', () => { notifyWhen: 'onActionGroupChange' as AlertNotifyWhenType, createdAt: new Date('1970-01-01T00:00:00.000Z'), updatedAt: new Date('1970-01-01T00:00:00.000Z'), - apiKey: null, apiKeyOwner: null, + createdBy: null, + updatedBy: null, + muteAll: false, + mutedInstanceIds: [], }; - const resolvedValue: Alert = { + const resolvedValue = { ...alertToCreate, id: '123', createdBy: null, @@ -576,7 +579,7 @@ describe('createAlert', () => { Array [ "/api/alerts/alert", Object { - "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKey\\":null,\\"apiKeyOwner\\":null}", + "body": "{\\"name\\":\\"test\\",\\"consumer\\":\\"alerts\\",\\"tags\\":[\\"foo\\"],\\"enabled\\":true,\\"alertTypeId\\":\\"test\\",\\"schedule\\":{\\"interval\\":\\"1m\\"},\\"actions\\":[],\\"params\\":{},\\"throttle\\":null,\\"notifyWhen\\":\\"onActionGroupChange\\",\\"createdAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"updatedAt\\":\\"1970-01-01T00:00:00.000Z\\",\\"apiKeyOwner\\":null,\\"createdBy\\":null,\\"updatedBy\\":null,\\"muteAll\\":false,\\"mutedInstanceIds\\":[]}", }, ] `); diff --git a/x-pack/plugins/triggers_actions_ui/public/plugin.ts b/x-pack/plugins/triggers_actions_ui/public/plugin.ts index f2c8957400fa50..05bfba27420c7f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/plugin.ts +++ b/x-pack/plugins/triggers_actions_ui/public/plugin.ts @@ -35,12 +35,12 @@ import { AlertEditProps } from './application/sections/alert_form/alert_edit'; export interface TriggersAndActionsUIPublicPluginSetup { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; } export interface TriggersAndActionsUIPublicPluginStart { actionTypeRegistry: TypeRegistry; - alertTypeRegistry: TypeRegistry; + alertTypeRegistry: TypeRegistry>; getAddConnectorFlyout: ( props: Omit ) => ReactElement; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 3fffe9fe230b4b..43780b324aa569 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -8,12 +8,12 @@ import type { DocLinksStart } from 'kibana/public'; import { ComponentType } from 'react'; import { ChartsPluginSetup } from 'src/plugins/charts/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { ActionGroup, AlertActionParam } from '../../alerts/common'; +import { ActionGroup, AlertActionParam, AlertTypeParams } from '../../alerts/common'; import { ActionType } from '../../actions/common'; import { TypeRegistry } from './application/type_registry'; import { AlertType as CommonAlertType } from '../../alerts/common'; import { - SanitizedAlert as Alert, + SanitizedAlert, AlertAction, AlertAggregations, AlertTaskState, @@ -23,6 +23,11 @@ import { AlertingFrameworkHealth, AlertNotifyWhenType, } from '../../alerts/common'; + +// In Triggers and Actions we treat all `Alert`s as `SanitizedAlert` +// so the `Params` is a black-box of Record +type Alert = SanitizedAlert; + export { Alert, AlertAction, @@ -169,14 +174,17 @@ export interface AlertTableItem extends Alert { } export interface AlertTypeParamsExpressionProps< - AlertParamsType = unknown, + Params extends AlertTypeParams = AlertTypeParams, MetaData = Record > { - alertParams: AlertParamsType; + alertParams: Params; alertInterval: string; alertThrottle: string; - setAlertParams: (property: string, value: any) => void; - setAlertProperty: (key: Key, value: Alert[Key] | null) => void; + setAlertParams: (property: Key, value: Params[Key] | undefined) => void; + setAlertProperty: ( + key: Prop, + value: SanitizedAlert[Prop] | null + ) => void; errors: IErrorObject; defaultActionGroupId: string; actionGroups: ActionGroup[]; @@ -185,15 +193,15 @@ export interface AlertTypeParamsExpressionProps< data: DataPublicPluginStart; } -export interface AlertTypeModel { +export interface AlertTypeModel { id: string; description: string; iconClass: string; documentationUrl: string | ((docLinks: DocLinksStart) => string) | null; - validate: (alertParams: AlertParamsType) => ValidationResult; + validate: (alertParams: Params) => ValidationResult; alertParamsExpression: | React.FunctionComponent - | React.LazyExoticComponent>>; + | React.LazyExoticComponent>>; requiresAppContext: boolean; defaultActionMessage?: string; } diff --git a/x-pack/plugins/uptime/public/state/actions/types.ts b/x-pack/plugins/uptime/public/state/actions/types.ts index 8d87ae5d52cb20..322f1eb0136b4e 100644 --- a/x-pack/plugins/uptime/public/state/actions/types.ts +++ b/x-pack/plugins/uptime/public/state/actions/types.ts @@ -6,7 +6,8 @@ import { Action } from 'redux-actions'; import { IHttpFetchError } from 'src/core/public'; -import { Alert } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; +import { UptimeAlertTypeParams } from '../alerts/alerts'; export interface AsyncAction { get: (payload: Payload) => Action; @@ -59,5 +60,5 @@ export interface AlertsResult { page: number; perPage: number; total: number; - data: Alert[]; + data: Array>; } diff --git a/x-pack/plugins/uptime/public/state/alerts/alerts.ts b/x-pack/plugins/uptime/public/state/alerts/alerts.ts index aeb81bb413aa7b..43186e0ef5422a 100644 --- a/x-pack/plugins/uptime/public/state/alerts/alerts.ts +++ b/x-pack/plugins/uptime/public/state/alerts/alerts.ts @@ -20,10 +20,8 @@ import { fetchMonitorAlertRecords, NewAlertParams, } from '../api/alerts'; -import { - ActionConnector as RawActionConnector, - Alert, -} from '../../../../triggers_actions_ui/public'; +import { ActionConnector as RawActionConnector } from '../../../../triggers_actions_ui/public'; +import { Alert } from '../../../../alerts/common'; import { kibanaService } from '../kibana_service'; import { monitorIdSelector } from '../selectors'; import { AlertsResult, MonitorIdParam } from '../actions/types'; @@ -31,13 +29,22 @@ import { simpleAlertEnabled } from '../../lib/alert_types/alert_messages'; export type ActionConnector = Omit; -export const createAlertAction = createAsyncAction('CREATE ALERT'); +/** + * TODO: Use actual AlertType Params type that's specific to Uptime instead of `any` + */ +export type UptimeAlertTypeParams = Record; + +export const createAlertAction = createAsyncAction< + NewAlertParams, + Alert | null +>('CREATE ALERT'); export const getConnectorsAction = createAsyncAction<{}, ActionConnector[]>('GET CONNECTORS'); export const getMonitorAlertsAction = createAsyncAction<{}, AlertsResult | null>('GET ALERTS'); -export const getAnomalyAlertAction = createAsyncAction( - 'GET EXISTING ALERTS' -); +export const getAnomalyAlertAction = createAsyncAction< + MonitorIdParam, + Alert +>('GET EXISTING ALERTS'); export const deleteAlertAction = createAsyncAction<{ alertId: string }, string | null>( 'DELETE ALERTS' ); @@ -47,9 +54,9 @@ export const deleteAnomalyAlertAction = createAsyncAction<{ alertId: string }, a interface AlertState { connectors: AsyncInitState; - newAlert: AsyncInitState; + newAlert: AsyncInitState>; alerts: AsyncInitState; - anomalyAlert: AsyncInitState; + anomalyAlert: AsyncInitState>; alertDeletion: AsyncInitState; anomalyAlertDeletion: AsyncInitState; } diff --git a/x-pack/plugins/uptime/public/state/api/alerts.ts b/x-pack/plugins/uptime/public/state/api/alerts.ts index da86da12a70cae..9d4dd3a1253c31 100644 --- a/x-pack/plugins/uptime/public/state/api/alerts.ts +++ b/x-pack/plugins/uptime/public/state/api/alerts.ts @@ -9,9 +9,10 @@ import { apiService } from './utils'; import { ActionConnector } from '../alerts/alerts'; import { AlertsResult, MonitorIdParam } from '../actions/types'; -import { Alert, AlertAction } from '../../../../triggers_actions_ui/public'; +import { AlertAction } from '../../../../triggers_actions_ui/public'; import { API_URLS } from '../../../common/constants'; import { MonitorStatusTranslations } from '../../../common/translations'; +import { Alert, AlertTypeParams } from '../../../../alerts/common'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -21,7 +22,7 @@ export const fetchConnectors = async () => { return await apiService.get(API_URLS.ALERT_ACTIONS); }; -export interface NewAlertParams { +export interface NewAlertParams extends AlertTypeParams { monitorId: string; monitorName?: string; defaultActions: ActionConnector[]; @@ -80,7 +81,9 @@ export const fetchMonitorAlertRecords = async (): Promise => { return await apiService.get(API_URLS.ALERTS_FIND, data); }; -export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise => { +export const fetchAlertRecords = async ({ + monitorId, +}: MonitorIdParam): Promise> => { const data = { page: 1, per_page: 500, @@ -90,7 +93,7 @@ export const fetchAlertRecords = async ({ monitorId }: MonitorIdParam): Promise< sort_order: 'asc', }; const alerts = await apiService.get(API_URLS.ALERTS_FIND, data); - return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); + return alerts.data.find((alert: Alert) => alert.params.monitorId === monitorId); }; export const disableAlertById = async ({ alertId }: { alertId: string }) => { diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts index 4f9fefa4188e5a..d5fd92476dd162 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/status_check.test.ts @@ -11,7 +11,13 @@ import { getStatusMessage, getUniqueIdsByLoc, } from '../status_check'; -import { AlertType } from '../../../../../alerts/server'; +import { + AlertType, + AlertTypeParams, + AlertTypeState, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../../alerts/server'; import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../../lib'; import { UptimeCorePlugins, UptimeCoreSetup } from '../../adapters'; @@ -868,7 +874,7 @@ describe('status check alert', () => { }); describe('alert factory', () => { - let alert: AlertType; + let alert: AlertType; beforeEach(() => { const { server, libs, plugins } = bootstrapDependencies(); diff --git a/x-pack/plugins/uptime/server/lib/alerts/types.ts b/x-pack/plugins/uptime/server/lib/alerts/types.ts index 0a80b36046860d..d143e33fb8e968 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/types.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/types.ts @@ -6,10 +6,17 @@ import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; import { UMServerLibs } from '../lib'; -import { AlertType } from '../../../../alerts/server'; +import { AlertType, AlertInstanceState, AlertInstanceContext } from '../../../../alerts/server'; +export type UptimeAlertTypeParam = Record; +export type UptimeAlertTypeState = Record; export type UptimeAlertTypeFactory = ( server: UptimeCoreSetup, libs: UMServerLibs, plugins: UptimeCorePlugins -) => AlertType; +) => AlertType< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext +>; diff --git a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts index 965287ffbde8e3..a4a2f2c64db1be 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/uptime_alert_wrapper.ts @@ -5,28 +5,46 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { AlertExecutorOptions, AlertType, AlertTypeState } from '../../../../alerts/server'; +import { + AlertExecutorOptions, + AlertInstanceState, + AlertInstanceContext, +} from '../../../../alerts/server'; import { savedObjectsAdapter } from '../saved_objects'; import { DynamicSettings } from '../../../common/runtime_types'; import { createUptimeESClient, UptimeESClient } from '../lib'; +import { UptimeAlertTypeFactory, UptimeAlertTypeParam, UptimeAlertTypeState } from './types'; -export interface UptimeAlertType extends Omit { +export interface UptimeAlertType + extends Omit, 'executor' | 'producer'> { executor: ({ options, uptimeEsClient, dynamicSettings, }: { - options: AlertExecutorOptions; + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + >; uptimeEsClient: UptimeESClient; dynamicSettings: DynamicSettings; savedObjectsClient: SavedObjectsClientContract; - }) => Promise; + }) => Promise; } export const uptimeAlertWrapper = (uptimeAlert: UptimeAlertType) => ({ ...uptimeAlert, producer: 'uptime', - executor: async (options: AlertExecutorOptions) => { + executor: async ( + options: AlertExecutorOptions< + UptimeAlertTypeParam, + UptimeAlertTypeState, + AlertInstanceState, + AlertInstanceContext + > + ) => { const { services: { scopedClusterClient: esClient, savedObjectsClient }, } = options; diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts index b4ee273e57d614..30c19f735b75db 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts @@ -13,6 +13,8 @@ import { AlertType, AlertInstanceState, AlertInstanceContext, + AlertTypeState, + AlertTypeParams, } from '../../../../../../../plugins/alerts/server'; export const EscapableStrings = { @@ -50,7 +52,7 @@ function getAlwaysFiringAlertType() { groupsToScheduleActionsInSeries: schema.maybe(schema.arrayOf(schema.nullable(schema.string()))), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { groupInSeriesIndex?: number; } interface InstanceState extends AlertInstanceState { @@ -59,7 +61,7 @@ function getAlwaysFiringAlertType() { interface InstanceContext extends AlertInstanceContext { instanceContextValue: boolean; } - const result: AlertType = { + const result: AlertType = { id: 'test.always-firing', name: 'Test: Always Firing', actionGroups: [ @@ -141,7 +143,7 @@ async function alwaysFiringExecutor(alertExecutorOptions: any) { } function getCumulativeFiringAlertType() { - interface State { + interface State extends AlertTypeState { runCount?: number; } interface InstanceState extends AlertInstanceState { @@ -175,7 +177,7 @@ function getCumulativeFiringAlertType() { }; }, }; - return result as AlertType; + return result; } function getNeverFiringAlertType() { @@ -184,7 +186,7 @@ function getNeverFiringAlertType() { reference: schema.string(), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { globalStateValue: boolean; } const result: AlertType = { @@ -385,7 +387,7 @@ function getPatternFiringAlertType() { reference: schema.maybe(schema.string()), }); type ParamsType = TypeOf; - interface State { + interface State extends AlertTypeState { patternIndex?: number; } const result: AlertType = { diff --git a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts index f6cbc52e7a421a..cf09286fe1ba67 100644 --- a/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts +++ b/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/server/plugin.ts @@ -27,7 +27,14 @@ export const noopAlertType: AlertType = { producer: 'alerts', }; -export const alwaysFiringAlertType: AlertType = { +export const alwaysFiringAlertType: AlertType< + { instances: Array<{ id: string; state: any }> }, + { + globalStateValue: boolean; + groupInSeriesIndex: number; + }, + { instanceStateValue: boolean; globalStateValue: boolean; groupInSeriesIndex: number } +> = { id: 'test.always-firing', name: 'Always Firing', actionGroups: [ @@ -37,7 +44,7 @@ export const alwaysFiringAlertType: AlertType = { defaultActionGroupId: 'default', producer: 'alerts', minimumLicenseRequired: 'basic', - async executor(alertExecutorOptions: any) { + async executor(alertExecutorOptions) { const { services, state, params } = alertExecutorOptions; (params.instances || []).forEach((instance: { id: string; state: any }) => { From 7930854bea995ef676f52985299f294358af288f Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 21 Dec 2020 12:22:14 -0700 Subject: [PATCH 021/100] Migrates search telemetry usage collector es client from legacy to new (#86597) --- .../data/server/search/collectors/fetch.ts | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/plugins/data/server/search/collectors/fetch.ts b/src/plugins/data/server/search/collectors/fetch.ts index 344bc18c7b4b66..9d0d431cf4eaff 100644 --- a/src/plugins/data/server/search/collectors/fetch.ts +++ b/src/plugins/data/server/search/collectors/fetch.ts @@ -20,31 +20,34 @@ import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { SharedGlobalConfig } from 'kibana/server'; +import { SearchResponse } from 'elasticsearch'; import { CollectorFetchContext } from 'src/plugins/usage_collection/server'; import { Usage } from './register'; - -interface SearchTelemetrySavedObject { +interface SearchTelemetry { 'search-telemetry': Usage; } +type ESResponse = SearchResponse; export function fetchProvider(config$: Observable) { - return async ({ callCluster }: CollectorFetchContext): Promise => { + return async ({ esClient }: CollectorFetchContext): Promise => { const config = await config$.pipe(first()).toPromise(); - - const response = await callCluster('search', { - index: config.kibana.index, - body: { - query: { term: { type: { value: 'search-telemetry' } } }, + const { body: esResponse } = await esClient.search( + { + index: config.kibana.index, + body: { + query: { term: { type: { value: 'search-telemetry' } } }, + }, }, - ignore: [404], - }); - - return response.hits.hits.length - ? response.hits.hits[0]._source['search-telemetry'] - : { - successCount: 0, - errorCount: 0, - averageDuration: null, - }; + { ignore: [404] } + ); + const size = esResponse?.hits?.hits?.length ?? 0; + if (!size) { + return { + successCount: 0, + errorCount: 0, + averageDuration: null, + }; + } + return esResponse.hits.hits[0]._source['search-telemetry']; }; } From 8760fa5a4d3420f49d4f01ad15678a176255ec7d Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Mon, 21 Dec 2020 13:30:41 -0600 Subject: [PATCH 022/100] [ML] Fix Single Metric Viewer y domain extending beyond the visible focus area (#86655) --- .../components/timeseries_chart/timeseries_chart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 32fa27b70989e7..768de330cce1d0 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -689,7 +689,7 @@ class TimeseriesChartIntl extends Component { const levels = getAnnotationLevels(focusAnnotationData); const maxLevel = d3.max(Object.keys(levels).map((key) => levels[key])); // TODO needs revisiting to be a more robust normalization - yMax = yMax * (1 + (maxLevel + 1) / 5); + yMax += Math.abs(yMax - yMin) * ((maxLevel + 1) / 5); } this.focusYScale.domain([yMin, yMax]); } else { From 58b429749f4499347620b3d183c020e9a631192f Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 21 Dec 2020 20:59:23 +0100 Subject: [PATCH 023/100] [ML] Support legacy watcher URL (#86661) * [ML] update watcher URL, support legacy state * [ML] fix comment --- .../explorer/hooks/use_explorer_url_state.ts | 13 ++++++++++++- .../components/create_watch_flyout/email.html | 2 +- .../ml/public/application/util/url_state.tsx | 9 ++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_explorer_url_state.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_explorer_url_state.ts index d51be619c39eea..805f13f48b01cf 100644 --- a/x-pack/plugins/ml/public/application/explorer/hooks/use_explorer_url_state.ts +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_explorer_url_state.ts @@ -9,5 +9,16 @@ import { ExplorerAppState } from '../../../../common/types/ml_url_generator'; import { ML_PAGES } from '../../../../common/constants/ml_url_generator'; export function useExplorerUrlState() { - return usePageUrlState(ML_PAGES.ANOMALY_EXPLORER); + /** + * Originally `mlExplorerSwimlane` resided directly in the app URL state (`_a` URL state key). + * With current URL structure it has been moved under the `explorer` key of the app state (_a). + */ + const [legacyExplorerState] = usePageUrlState( + 'mlExplorerSwimlane' + ); + + return usePageUrlState(ML_PAGES.ANOMALY_EXPLORER, { + mlExplorerSwimlane: legacyExplorerState, + mlExplorerFilter: {}, + }); } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html index 5ce7d9f81f367c..2e93c7eefcf1e5 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html @@ -22,7 +22,7 @@

- + <%= openInAnomalyExplorerLinkText %>
diff --git a/x-pack/plugins/ml/public/application/util/url_state.tsx b/x-pack/plugins/ml/public/application/util/url_state.tsx index 6cdc069096dccd..569e7bcc7b7e1a 100644 --- a/x-pack/plugins/ml/public/application/util/url_state.tsx +++ b/x-pack/plugins/ml/public/application/util/url_state.tsx @@ -162,7 +162,14 @@ export const useUrlState = (accessor: Accessor) => { return [urlState, setUrlState]; }; -type AppStateKey = 'mlSelectSeverity' | 'mlSelectInterval' | 'mlAnomaliesTable' | MlPages; +type LegacyUrlKeys = 'mlExplorerSwimlane'; + +type AppStateKey = + | 'mlSelectSeverity' + | 'mlSelectInterval' + | 'mlAnomaliesTable' + | MlPages + | LegacyUrlKeys; /** * Hook for managing the URL state of the page. From 68ec9d2f226a0cb6af31558153f4cf70d2e30f2b Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 21 Dec 2020 13:06:59 -0700 Subject: [PATCH 024/100] Uses the new es client in canvas usage collector's fetch methods (#86668) --- x-pack/plugins/canvas/server/collectors/collector.ts | 4 ++-- .../server/collectors/custom_element_collector.ts | 10 ++++++---- .../canvas/server/collectors/workpad_collector.ts | 9 +++++---- x-pack/plugins/canvas/types/telemetry.ts | 4 ++-- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/canvas/server/collectors/collector.ts b/x-pack/plugins/canvas/server/collectors/collector.ts index a084e8fe3349eb..9cb015eed4c5b5 100644 --- a/x-pack/plugins/canvas/server/collectors/collector.ts +++ b/x-pack/plugins/canvas/server/collectors/collector.ts @@ -37,9 +37,9 @@ export function registerCanvasUsageCollector( const canvasCollector = usageCollection.makeUsageCollector({ type: 'canvas', isReady: () => true, - fetch: async ({ callCluster }: CollectorFetchContext) => { + fetch: async ({ esClient }: CollectorFetchContext) => { const collectorResults = await Promise.all( - collectors.map((collector) => collector(kibanaIndex, callCluster)) + collectors.map((collector) => collector(kibanaIndex, esClient)) ); return collectorResults.reduce((reduction, usage) => { diff --git a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts index d3ed1e17785ee9..8992f78332518b 100644 --- a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchParams } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; import { get } from 'lodash'; import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { collectFns } from './collector_helpers'; @@ -114,9 +114,9 @@ export function summarizeCustomElements( const customElementCollector: TelemetryCollector = async function customElementCollector( kibanaIndex, - callCluster + esClient ) { - const customElementParams: SearchParams = { + const customElementParams = { size: 10000, index: kibanaIndex, ignoreUnavailable: true, @@ -124,7 +124,9 @@ const customElementCollector: TelemetryCollector = async function customElementC body: { query: { bool: { filter: { term: { type: CUSTOM_ELEMENT_TYPE } } } } }, }; - const esResponse = await callCluster('search', customElementParams); + const { body: esResponse } = await esClient.search>( + customElementParams + ); if (get(esResponse, 'hits.hits.length') > 0) { const customElements = esResponse.hits.hits.map((hit) => hit._source[CUSTOM_ELEMENT_TYPE]); diff --git a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts index 04794115288022..5d633a0d4dd1d3 100644 --- a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SearchParams } from 'elasticsearch'; +import { SearchResponse } from 'elasticsearch'; import { sum as arraySum, min as arrayMin, max as arrayMax, get } from 'lodash'; import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; import { CANVAS_TYPE } from '../../common/lib/constants'; @@ -229,9 +229,10 @@ export function summarizeWorkpads(workpadDocs: CanvasWorkpad[]): WorkpadTelemetr variables: variableInfo, }; } +type ESResponse = SearchResponse; -const workpadCollector: TelemetryCollector = async function (kibanaIndex, callCluster) { - const searchParams: SearchParams = { +const workpadCollector: TelemetryCollector = async function (kibanaIndex, esClient) { + const searchParams = { size: 10000, // elasticsearch index.max_result_window default value index: kibanaIndex, ignoreUnavailable: true, @@ -239,7 +240,7 @@ const workpadCollector: TelemetryCollector = async function (kibanaIndex, callCl body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } }, }; - const esResponse = await callCluster('search', searchParams); + const { body: esResponse } = await esClient.search(searchParams); if (get(esResponse, 'hits.hits.length') > 0) { const workpads = esResponse.hits.hits.map((hit) => hit._source[CANVAS_TYPE]); diff --git a/x-pack/plugins/canvas/types/telemetry.ts b/x-pack/plugins/canvas/types/telemetry.ts index 3b635b2e579269..e323372e879939 100644 --- a/x-pack/plugins/canvas/types/telemetry.ts +++ b/x-pack/plugins/canvas/types/telemetry.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; /** Function for collecting information about canvas usage @@ -13,7 +13,7 @@ export type TelemetryCollector = ( /** The server instance */ kibanaIndex: string, /** Function for calling elasticsearch */ - callCluster: LegacyAPICaller + esClient: ElasticsearchClient ) => Record; export interface TelemetryCustomElementDocument { From 5725e14b69efd0042f3383c37728d72630abb304 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 21 Dec 2020 12:15:10 -0800 Subject: [PATCH 025/100] [ML] Edits saved object synchronization message (#86664) --- .../saved_objects_warning/saved_objects_warning.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx b/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx index 7aabc6591ea1be..bfc03fe6119e15 100644 --- a/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx +++ b/x-pack/plugins/ml/public/application/components/saved_objects_warning/saved_objects_warning.tsx @@ -53,7 +53,7 @@ export const SavedObjectsWarning: FC = ({ jobType }) => { title={ } color="warning" @@ -62,13 +62,13 @@ export const SavedObjectsWarning: FC = ({ jobType }) => {
), From 27b3a366a383d4e55daeca364ab11869763c100a Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 21 Dec 2020 12:17:41 -0800 Subject: [PATCH 026/100] [ML] Add doc link for classification AUC ROC evaluation (#86660) --- src/core/public/doc_links/doc_links_service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index b8843b5c85595d..2893f4c9a9878c 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -147,6 +147,7 @@ export class DocLinksService { featureImportance: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-feature-importance.html`, outlierDetectionRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-roc`, regressionEvaluation: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-regression-evaluation`, + classificationAucRoc: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-class-aucroc`, }, transforms: { guide: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/transforms.html`, From 26b784b2a41f31a2acea3f7bd6e4e499eebd21ef Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 21 Dec 2020 15:18:56 -0600 Subject: [PATCH 027/100] [build] Remove grunt docker:docs task (#85848) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- tasks/docker_docs.js | 53 ---------------------------- tasks/docker_docs/Dockerfile | 18 ---------- tasks/docker_docs/docker-compose.yml | 8 ----- test/scripts/jenkins_docs.sh | 6 ---- 4 files changed, 85 deletions(-) delete mode 100644 tasks/docker_docs.js delete mode 100644 tasks/docker_docs/Dockerfile delete mode 100644 tasks/docker_docs/docker-compose.yml delete mode 100755 test/scripts/jenkins_docs.sh diff --git a/tasks/docker_docs.js b/tasks/docker_docs.js deleted file mode 100644 index 3a2041abc93018..00000000000000 --- a/tasks/docker_docs.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import del from 'del'; -import { join } from 'path'; -import { execFileSync as exec } from 'child_process'; - -export default function (grunt) { - grunt.registerTask('docker:docs', 'Build docs from docker', function () { - const rootPath = grunt.config.get('root'); - const composePath = join(rootPath, 'tasks/docker_docs/docker-compose.yml'); - const htmlDocsDir = join(rootPath, 'html_docs'); - - const env = Object.assign(process.env, { - KIBANA_DOCS_CONTAINER_NAME: 'kibana_docs', - KIBANA_DOCS_CONTEXT: rootPath, - }); - const stdio = [0, 1, 2]; - const execOptions = { env, stdio }; - - exec('docker-compose', ['-f', composePath, 'up'], execOptions); - - const containerId = String( - exec('docker-compose', ['-f', composePath, 'ps', '-q', env.KIBANA_DOCS_CONTAINER_NAME], { - env, - }) - ).trim(); - - grunt.log.write('Clearing old docs ... '); - del.sync(htmlDocsDir); - grunt.log.writeln('done'); - - grunt.log.write('Copying new docs ... '); - exec('docker', ['cp', `${containerId}:/home/kibana/html_docs`, htmlDocsDir]); - grunt.log.writeln('done'); - }); -} diff --git a/tasks/docker_docs/Dockerfile b/tasks/docker_docs/Dockerfile deleted file mode 100644 index 435b78f89f2e79..00000000000000 --- a/tasks/docker_docs/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM alpine:3.6 - -RUN apk add --no-cache \ - git \ - libxslt \ - curl \ - python \ - libxml2-utils \ - perl - -RUN addgroup kibana && \ - adduser -D -G kibana -s /bin/bash -h /home/kibana kibana - -USER kibana -RUN git clone --depth 1 https://github.com/elastic/docs.git /home/kibana/docs_builder - -WORKDIR /home/kibana/docs_builder -CMD git pull origin master && ./build_docs.pl --doc /home/kibana/ascii_docs/index.asciidoc --out /home/kibana/html_docs --chunk=1 diff --git a/tasks/docker_docs/docker-compose.yml b/tasks/docker_docs/docker-compose.yml deleted file mode 100644 index 8d0bd2c1365965..00000000000000 --- a/tasks/docker_docs/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '2' -services: - kibana_docs: - container_name: $KIBANA_DOCS_CONTAINER_NAME - build: - context: $KIBANA_DOCS_CONTEXT/tasks/docker_docs - volumes: - - $KIBANA_DOCS_CONTEXT/docs:/home/kibana/ascii_docs diff --git a/test/scripts/jenkins_docs.sh b/test/scripts/jenkins_docs.sh deleted file mode 100755 index f447afda1f948d..00000000000000 --- a/test/scripts/jenkins_docs.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -e -source "$(dirname $0)/../../src/dev/ci_setup/setup.sh" - -"$(FORCE_COLOR=0 yarn bin)/grunt" docker:docs; From 52cd716bc3a64bec7128bcf93056a4760bba9b7a Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 21 Dec 2020 15:21:27 -0600 Subject: [PATCH 028/100] [build] Remove grunt checkPlugins task (#85852) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- tasks/check_plugins.js | 58 ------------------------------------------ 1 file changed, 58 deletions(-) delete mode 100644 tasks/check_plugins.js diff --git a/tasks/check_plugins.js b/tasks/check_plugins.js deleted file mode 100644 index 20fb8a895af6c4..00000000000000 --- a/tasks/check_plugins.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import fs from 'fs'; -import path from 'path'; - -export default function checkPlugins(grunt) { - grunt.registerTask( - 'checkPlugins', - 'Checks for plugins which may disrupt tests', - function checkPlugins() { - const done = this.async(); - const pluginsDir = path.resolve('./plugins/'); - - fs.readdir(pluginsDir, (err, files) => { - if (!files) { - return done(); - } - - const plugins = files.filter((file) => { - return fs.statSync(path.join(pluginsDir, file)).isDirectory(); - }); - - if (plugins.length) { - grunt.log.error( - '===================================================================================================' - ); - plugins.forEach((plugin) => { - grunt.log.error( - `The ${plugin} plugin may disrupt the test process. Consider removing it and re-running your tests.` - ); - }); - grunt.log.error( - '===================================================================================================' - ); - } - - done(); - }); - } - ); -} From a76c666a6153b062d2f9d97b5c7e87620339a181 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Mon, 21 Dec 2020 15:06:31 -0800 Subject: [PATCH 029/100] [CI] Removes script previously used for Karma (#86412) Signed-off-by: Tyler Smalley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/scripts/test/xpack_karma.sh | 6 ------ 1 file changed, 6 deletions(-) delete mode 100755 test/scripts/test/xpack_karma.sh diff --git a/test/scripts/test/xpack_karma.sh b/test/scripts/test/xpack_karma.sh deleted file mode 100755 index 9078f01f1b870f..00000000000000 --- a/test/scripts/test/xpack_karma.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -cd x-pack -checks-reporter-with-killswitch "X-Pack Karma Tests" yarn test:karma From 42c1414e091510bfbbfb4f6ea2033d02fb2519db Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Mon, 21 Dec 2020 15:07:24 -0800 Subject: [PATCH 030/100] Uses @elastic/elasticsearch-canary (#86398) Signed-off-by: Tyler Smalley Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6e8809063ca575..d6e544ddb57e45 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@elastic/datemath": "link:packages/elastic-datemath", - "@elastic/elasticsearch": "7.10.0", + "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary", "@elastic/ems-client": "7.11.0", "@elastic/eui": "30.6.0", "@elastic/filesaver": "1.1.2", diff --git a/yarn.lock b/yarn.lock index 6e4df2f5b197a4..074fb951d782b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1411,10 +1411,10 @@ version "0.0.0" uid "" -"@elastic/elasticsearch@7.10.0": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.10.0.tgz#da105a9c1f14146f9f2cab4e7026cb7949121b8d" - integrity sha512-vXtMAQf5/DwqeryQgRriMtnFppJNLc/R7/R0D8E+wG5/kGM5i7mg+Hi7TM4NZEuXgtzZ2a/Nf7aR0vLyrxOK/w== +"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@^8.0.0-canary": + version "8.0.0-canary.1" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.0.0-canary.1.tgz#5cd0eda62531b71af66a08da6c3cebc26a73d4c0" + integrity sha512-VhQ42wH+0OGmHSlc4It3bqGTL7mLuC2RIionJZBIuY5P6lwUMz7goelfyfTHoo+LStxz5QQ8Zt2xcnAnShTBJg== dependencies: debug "^4.1.1" hpagent "^0.1.1" From ecb452291085d991c61128e434ee6a22e1316af0 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Mon, 21 Dec 2020 20:54:35 -0500 Subject: [PATCH 031/100] [Monitoring] Convert Kibana-related server files that read from _source to typescript (#86364) * Identifying kibana source fields * Fix types * Fix types, take 2 * Should really fix it now Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/monitoring/common/types/es.ts | 80 +++++ .../monitoring/public/alerts/status.tsx | 4 +- .../components/kibana/instances/instances.js | 294 ---------------- .../components/kibana/instances/instances.tsx | 314 ++++++++++++++++++ ...{get_kibana_info.js => get_kibana_info.ts} | 23 +- x-pack/plugins/monitoring/server/plugin.ts | 1 + .../v1/kibana/{instance.js => instance.ts} | 9 +- x-pack/plugins/monitoring/server/types.ts | 95 ++---- 8 files changed, 443 insertions(+), 377 deletions(-) create mode 100644 x-pack/plugins/monitoring/common/types/es.ts delete mode 100644 x-pack/plugins/monitoring/public/components/kibana/instances/instances.js create mode 100644 x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx rename x-pack/plugins/monitoring/server/lib/kibana/{get_kibana_info.js => get_kibana_info.ts} (68%) rename x-pack/plugins/monitoring/server/routes/api/v1/kibana/{instance.js => instance.ts} (89%) diff --git a/x-pack/plugins/monitoring/common/types/es.ts b/x-pack/plugins/monitoring/common/types/es.ts new file mode 100644 index 00000000000000..853e140ec66c7d --- /dev/null +++ b/x-pack/plugins/monitoring/common/types/es.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ElasticsearchSourceKibanaStats { + timestamp?: string; + kibana?: { + name?: string; + status?: string; + uuid?: string; + response_times?: { + max?: number; + }; + }; + os?: { + memory?: { + free_in_bytes?: number; + }; + }; + process?: { + uptime_in_millis?: number; + }; +} + +export interface ElasticsearchSource { + timestamp: string; + kibana_stats?: ElasticsearchSourceKibanaStats; + beats_stats?: { + timestamp?: string; + beat?: { + uuid?: string; + name?: string; + type?: string; + version?: string; + host?: string; + }; + metrics?: { + beat?: { + memstats?: { + memory_alloc?: number; + }; + info?: { + uptime?: { + ms?: number; + }; + }; + handles?: { + limit?: { + hard?: number; + soft?: number; + }; + }; + }; + libbeat?: { + config?: { + reloads?: number; + }; + output?: { + type?: string; + write?: { + bytes?: number; + errors?: number; + }; + read?: { + errors?: number; + }; + }; + pipeline?: { + events?: { + total?: number; + published?: number; + dropped?: number; + }; + }; + }; + }; + }; +} diff --git a/x-pack/plugins/monitoring/public/alerts/status.tsx b/x-pack/plugins/monitoring/public/alerts/status.tsx index 4d51069efb972b..f67f1df11cfe1b 100644 --- a/x-pack/plugins/monitoring/public/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/alerts/status.tsx @@ -16,8 +16,8 @@ import { SetupModeContext } from '../components/setup_mode/setup_mode_context'; interface Props { alerts: { [alertTypeId: string]: CommonAlertStatus }; showBadge: boolean; - showOnlyCount: boolean; - stateFilter: (state: AlertState) => boolean; + showOnlyCount?: boolean; + stateFilter?: (state: AlertState) => boolean; } export const AlertsStatus: React.FC = (props: Props) => { const { alerts, showBadge = false, showOnlyCount = false, stateFilter = () => true } = props; diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js deleted file mode 100644 index cde7952aa1839c..00000000000000 --- a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { PureComponent, Fragment } from 'react'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPanel, - EuiSpacer, - EuiLink, - EuiCallOut, - EuiScreenReaderOnly, - EuiToolTip, - EuiHealth, -} from '@elastic/eui'; -import { capitalize, get } from 'lodash'; -import { ClusterStatus } from '../cluster_status'; -import { EuiMonitoringTable } from '../../table'; -import { StatusIcon } from '../../status_icon'; -import { formatMetric, formatNumber } from '../../../lib/format_number'; -import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { SetupModeBadge } from '../../setup_mode/badge'; -import { KIBANA_SYSTEM_ID } from '../../../../common/constants'; -import { ListingCallOut } from '../../setup_mode/listing_callout'; -import { AlertsStatus } from '../../../alerts/status'; -import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; -import { SetupModeFeature } from '../../../../common/enums'; - -const getColumns = (setupMode, alerts) => { - const columns = [ - { - name: i18n.translate('xpack.monitoring.kibana.listing.nameColumnTitle', { - defaultMessage: 'Name', - }), - field: 'name', - render: (name, kibana) => { - let setupModeStatus = null; - if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - const list = get(setupMode, 'data.byUuid', {}); - const uuid = get(kibana, 'kibana.uuid'); - const status = list[uuid] || {}; - const instance = { - uuid, - name: kibana.name, - }; - - setupModeStatus = ( -
- -
- ); - if (status.isNetNewUser) { - return ( -
- {name} - {setupModeStatus} -
- ); - } - } - - return ( -
- - {name} - - {setupModeStatus} -
- ); - }, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.alertsColumnTitle', { - defaultMessage: 'Alerts', - }), - field: 'isOnline', - width: '175px', - sortable: true, - render: () => , - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.statusColumnTitle', { - defaultMessage: 'Status', - }), - field: 'status', - render: (status, kibana) => { - return ( - - - {capitalize(status)} - - - ); - }, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.loadAverageColumnTitle', { - defaultMessage: 'Load Average', - }), - field: 'os.load.1m', - render: (value) => {formatMetric(value, '0.00')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.memorySizeColumnTitle', { - defaultMessage: 'Memory Size', - }), - field: 'process.memory.resident_set_size_in_bytes', - render: (value) => {formatNumber(value, 'byte')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.requestsColumnTitle', { - defaultMessage: 'Requests', - }), - field: 'requests.total', - render: (value) => {formatNumber(value, 'int_commas')}, - }, - { - name: i18n.translate('xpack.monitoring.kibana.listing.responseTimeColumnTitle', { - defaultMessage: 'Response Times', - }), - // It is possible this does not exist through MB collection - field: 'response_times.average', - render: (value, kibana) => { - if (!value) { - return null; - } - - return ( -
-
- {formatNumber(value, 'int_commas') + ' ms avg'} -
-
- {formatNumber(kibana.response_times.max, 'int_commas')} ms max -
-
- ); - }, - }, - ]; - - return columns; -}; - -export class KibanaInstances extends PureComponent { - render() { - const { clusterStatus, alerts, setupMode, sorting, pagination, onTableChange } = this.props; - - let setupModeCallOut = null; - // Merge the instances data with the setup data if enabled - const instances = this.props.instances || []; - if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - // We want to create a seamless experience for the user by merging in the setup data - // and the node data from monitoring indices in the likely scenario where some instances - // are using MB collection and some are using no collection - const instancesByUuid = instances.reduce( - (byUuid, instance) => ({ - ...byUuid, - [get(instance, 'kibana.uuid')]: instance, - }), - {} - ); - - instances.push( - ...Object.entries(setupMode.data.byUuid).reduce((instances, [nodeUuid, instance]) => { - if (!instancesByUuid[nodeUuid]) { - instances.push({ - kibana: { - ...instance.instance.kibana, - status: StatusIcon.TYPES.GRAY, - }, - }); - } - return instances; - }, []) - ); - - setupModeCallOut = ( - { - const customRenderResponse = { - shouldRender: false, - componentToRender: null, - }; - - const hasInstances = setupMode.data.totalUniqueInstanceCount > 0; - if (!hasInstances) { - customRenderResponse.shouldRender = true; - customRenderResponse.componentToRender = ( - - -

- {i18n.translate( - 'xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription', - { - defaultMessage: `The following instances are not monitored. - Click 'Monitor with Metricbeat' below to start monitoring.`, - } - )} -

-
- -
- ); - } - - return customRenderResponse; - }} - /> - ); - } - - const dataFlattened = instances.map((item) => ({ - ...item, - name: item.kibana.name, - status: item.kibana.status, - })); - - return ( - - - -

- -

-
- - - - - {setupModeCallOut} - - - -
-
- ); - } -} diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx new file mode 100644 index 00000000000000..102f80b4722d4c --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.tsx @@ -0,0 +1,314 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPanel, + EuiSpacer, + EuiLink, + EuiCallOut, + EuiScreenReaderOnly, + EuiToolTip, + EuiHealth, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { capitalize, get } from 'lodash'; +// @ts-ignore +import { ClusterStatus } from '../cluster_status'; +// @ts-ignore +import { EuiMonitoringTable } from '../../table'; +// @ts-ignore +import { StatusIcon } from '../../status_icon'; +// @ts-ignore +import { formatMetric, formatNumber } from '../../../lib/format_number'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; +// @ts-ignore +import { SetupModeBadge } from '../../setup_mode/badge'; +import { KIBANA_SYSTEM_ID } from '../../../../common/constants'; +import { CommonAlertStatus } from '../../../../common/types/alerts'; +import { ElasticsearchSourceKibanaStats } from '../../../../common/types/es'; +// @ts-ignore +import { ListingCallOut } from '../../setup_mode/listing_callout'; +import { AlertsStatus } from '../../../alerts/status'; +import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; +import { SetupModeFeature } from '../../../../common/enums'; + +const getColumns = (setupMode: any, alerts: { [alertTypeId: string]: CommonAlertStatus }) => { + const columns = [ + { + name: i18n.translate('xpack.monitoring.kibana.listing.nameColumnTitle', { + defaultMessage: 'Name', + }), + field: 'name', + render: (name: string, kibana: any) => { + let setupModeStatus = null; + if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { + const list = get(setupMode, 'data.byUuid', {}); + const uuid = get(kibana, 'kibana.uuid'); + const status = list[uuid] || {}; + const instance = { + uuid, + name: kibana.name, + }; + + setupModeStatus = ( +
+ +
+ ); + if (status.isNetNewUser) { + return ( +
+ {name} + {setupModeStatus} +
+ ); + } + } + + return ( +
+ + {name} + + {setupModeStatus} +
+ ); + }, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.alertsColumnTitle', { + defaultMessage: 'Alerts', + }), + field: 'isOnline', + width: '175px', + sortable: true, + render: () => , + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.statusColumnTitle', { + defaultMessage: 'Status', + }), + field: 'status', + render: ( + status: string, + kibana: Pick & { availability: boolean } + ) => { + return ( + + + {capitalize(status)} + + + ); + }, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.loadAverageColumnTitle', { + defaultMessage: 'Load Average', + }), + field: 'os.load.1m', + render: (value: string) => {formatMetric(value, '0.00')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.memorySizeColumnTitle', { + defaultMessage: 'Memory Size', + }), + field: 'process.memory.resident_set_size_in_bytes', + render: (value: string) => {formatNumber(value, 'byte')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.requestsColumnTitle', { + defaultMessage: 'Requests', + }), + field: 'requests.total', + render: (value: string) => {formatNumber(value, 'int_commas')}, + }, + { + name: i18n.translate('xpack.monitoring.kibana.listing.responseTimeColumnTitle', { + defaultMessage: 'Response Times', + }), + // It is possible this does not exist through MB collection + field: 'response_times.average', + render: (value: string, kibana: ElasticsearchSourceKibanaStats['kibana']) => { + if (!value) { + return null; + } + + return ( +
+
+ {formatNumber(value, 'int_commas') + ' ms avg'} +
+
+ {formatNumber(kibana?.response_times?.max, 'int_commas')} ms max +
+
+ ); + }, + }, + ]; + + return columns; +}; + +interface Props { + clusterStatus: any; + alerts: { [alertTypeId: string]: CommonAlertStatus }; + setupMode: any; + sorting: any; + pagination: any; + onTableChange: any; + instances: ElasticsearchSourceKibanaStats[]; +} + +export const KibanaInstances: React.FC = (props: Props) => { + const { clusterStatus, alerts, setupMode, sorting, pagination, onTableChange } = props; + + let setupModeCallOut = null; + // Merge the instances data with the setup data if enabled + const instances = props.instances || []; + if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { + // We want to create a seamless experience for the user by merging in the setup data + // and the node data from monitoring indices in the likely scenario where some instances + // are using MB collection and some are using no collection + const instancesByUuid = instances.reduce( + (byUuid: { [uuid: string]: ElasticsearchSourceKibanaStats }, instance) => ({ + ...byUuid, + [instance.kibana?.uuid ?? '']: instance, + }), + {} + ); + + instances.push( + ...Object.entries(setupMode.data.byUuid).reduce((_instances: any, [nodeUuid, instance]) => { + if (!instancesByUuid[nodeUuid]) { + _instances.push({ + kibana: { + ...(instance as any).instance.kibana, + status: StatusIcon.TYPES.GRAY, + }, + }); + } + return _instances; + }, []) + ); + + setupModeCallOut = ( + { + const customRenderResponse = { + shouldRender: false, + componentToRender: null, + }; + + const hasInstances = setupMode.data.totalUniqueInstanceCount > 0; + if (!hasInstances) { + customRenderResponse.shouldRender = true; + // @ts-ignore + customRenderResponse.componentToRender = ( + + +

+ {i18n.translate( + 'xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription', + { + defaultMessage: `The following instances are not monitored. + Click 'Monitor with Metricbeat' below to start monitoring.`, + } + )} +

+
+ +
+ ); + } + + return customRenderResponse; + }} + /> + ); + } + + const dataFlattened = instances.map((item) => ({ + ...item, + name: item.kibana?.name, + status: item.kibana?.status, + })); + + return ( + + + +

+ +

+
+ + + + + {setupModeCallOut} + + + +
+
+ ); +}; diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts similarity index 68% rename from x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js rename to x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts index 5a3e2dea930e0d..0e8903908a55eb 100644 --- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.js +++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts @@ -4,21 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, merge } from 'lodash'; +import { merge } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { calculateAvailability } from '../calculate_availability'; +import { LegacyRequest, ElasticsearchResponse } from '../../types'; -export function handleResponse(resp) { - const source = get(resp, 'hits.hits[0]._source.kibana_stats'); - const kibana = get(source, 'kibana'); +export function handleResponse(resp: ElasticsearchResponse) { + const source = resp.hits?.hits[0]._source.kibana_stats; + const kibana = source?.kibana; return merge(kibana, { - availability: calculateAvailability(get(source, 'timestamp')), - os_memory_free: get(source, 'os.memory.free_in_bytes'), - uptime: get(source, 'process.uptime_in_millis'), + availability: calculateAvailability(source?.timestamp), + os_memory_free: source?.os?.memory?.free_in_bytes, + uptime: source?.process?.uptime_in_millis, }); } -export function getKibanaInfo(req, kbnIndexPattern, { clusterUuid, kibanaUuid }) { +export function getKibanaInfo( + req: LegacyRequest, + kbnIndexPattern: string, + { clusterUuid, kibanaUuid }: { clusterUuid: string; kibanaUuid: string } +) { checkParam(kbnIndexPattern, 'kbnIndexPattern in getKibanaInfo'); const params = { diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 22ea6c31dbe69d..9af95019dafd88 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -335,6 +335,7 @@ export class Plugin { } }, server: { + route: () => {}, config: legacyConfigWrapper, newPlatform: { setup: { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js b/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts similarity index 89% rename from x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js rename to x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts index 02229de3728626..845908e6df3419 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/kibana/instance.ts @@ -6,11 +6,16 @@ import { schema } from '@kbn/config-schema'; import { getKibanaInfo } from '../../../../lib/kibana/get_kibana_info'; +// @ts-ignore import { handleError } from '../../../../lib/errors'; +// @ts-ignore import { getMetrics } from '../../../../lib/details/get_metrics'; +// @ts-ignore import { prefixIndexPattern } from '../../../../lib/ccs_utils'; +// @ts-ignore import { metricSet } from './metric_set_instance'; import { INDEX_PATTERN_KIBANA } from '../../../../../common/constants'; +import { LegacyRequest, LegacyServer } from '../../../../types'; /** * Kibana instance: This will fetch all data required to display a Kibana @@ -18,7 +23,7 @@ import { INDEX_PATTERN_KIBANA } from '../../../../../common/constants'; * - Kibana Instance Summary (Status) * - Metrics */ -export function kibanaInstanceRoute(server) { +export function kibanaInstanceRoute(server: LegacyServer) { server.route({ method: 'POST', path: '/api/monitoring/v1/clusters/{clusterUuid}/kibana/{kibanaUuid}', @@ -37,7 +42,7 @@ export function kibanaInstanceRoute(server) { }), }, }, - async handler(req) { + async handler(req: LegacyRequest) { const config = server.config(); const ccs = req.payload.ccs; const clusterUuid = req.params.clusterUuid; diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 84b331df8ba427..4fbc1c494f14cb 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -17,6 +17,7 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; import { CloudSetup } from '../../cloud/server'; +import { ElasticsearchSource } from '../common/types/es'; export interface MonitoringLicenseService { refresh: () => Promise; @@ -81,30 +82,36 @@ export interface LegacyRequest { payload: { [key: string]: any; }; + params: { + [key: string]: string; + }; getKibanaStatsCollector: () => any; getUiSettingsService: () => any; getActionTypeRegistry: () => any; getAlertsClient: () => any; getActionsClient: () => any; - server: { - config: () => { - get: (key: string) => string | undefined; + server: LegacyServer; +} + +export interface LegacyServer { + route: (params: any) => void; + config: () => { + get: (key: string) => string | undefined; + }; + newPlatform: { + setup: { + plugins: PluginsSetup; }; - newPlatform: { - setup: { - plugins: PluginsSetup; - }; + }; + plugins: { + monitoring: { + info: MonitoringLicenseService; }; - plugins: { - monitoring: { - info: MonitoringLicenseService; - }; - elasticsearch: { - getCluster: ( - name: string - ) => { - callWithRequest: (req: any, endpoint: string, params: any) => Promise; - }; + elasticsearch: { + getCluster: ( + name: string + ) => { + callWithRequest: (req: any, endpoint: string, params: any) => Promise; }; }; }; @@ -132,57 +139,3 @@ export interface ElasticsearchResponseHit { }; }; } - -export interface ElasticsearchSource { - timestamp: string; - beats_stats?: { - timestamp?: string; - beat?: { - uuid?: string; - name?: string; - type?: string; - version?: string; - host?: string; - }; - metrics?: { - beat?: { - memstats?: { - memory_alloc?: number; - }; - info?: { - uptime?: { - ms?: number; - }; - }; - handles?: { - limit?: { - hard?: number; - soft?: number; - }; - }; - }; - libbeat?: { - config?: { - reloads?: number; - }; - output?: { - type?: string; - write?: { - bytes?: number; - errors?: number; - }; - read?: { - errors?: number; - }; - }; - pipeline?: { - events?: { - total?: number; - published?: number; - dropped?: number; - }; - }; - }; - }; - }; -} From 5708d4c7b778d2266dceb4f13183cc814b6c1c13 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Dec 2020 09:45:55 +0200 Subject: [PATCH 032/100] Update dependency vega to ^5.17.1 (#86715) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 37 ++++++++++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index d6e544ddb57e45..985998e54cb880 100644 --- a/package.json +++ b/package.json @@ -824,7 +824,7 @@ "url-loader": "^2.2.0", "use-resize-observer": "^6.0.0", "val-loader": "^1.1.1", - "vega": "^5.17.0", + "vega": "^5.17.1", "vega-lite": "^4.17.0", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.24.2", diff --git a/yarn.lock b/yarn.lock index 074fb951d782b1..de9efb26c384b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28347,6 +28347,13 @@ vega-expression@^3.0.0, vega-expression@~3.0.0: dependencies: vega-util "^1.15.2" +vega-expression@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-3.0.1.tgz#bbccd8f59371a537eab16f3d9eff5cbeaa27532d" + integrity sha512-+UwOFEkBnAWo8Zud6i8O4Pd2W6QqmPUOaAhjNtj0OxRL+d+Duoy7M4edUDZ+YuoUcMnjjBFfDQu7oRAA1fIMEQ== + dependencies: + vega-util "^1.15.2" + vega-force@~4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.0.7.tgz#6dc39ecb7889d9102661244d62fbc8d8714162ee" @@ -28384,10 +28391,10 @@ vega-functions@^5.8.0, vega-functions@~5.8.0: vega-time "^2.0.4" vega-util "^1.15.2" -vega-geo@~4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.7.tgz#4220137458a17d422fa15705f24905ba2595ca40" - integrity sha512-5HC1D9Z/WYuM1Gmlk8PxuRKgeN8snNWsfKO4E9PTmR7wo7tuU/2SGlRoE27aTsgwMMpBIrpRbSgKtgh5l/fMUQ== +vega-geo@~4.3.8: + version "4.3.8" + resolved "https://registry.yarnpkg.com/vega-geo/-/vega-geo-4.3.8.tgz#5629d18327bb4f3700cdf05db4aced0a43abbf4a" + integrity sha512-fsGxV96Q/QRgPqOPtMBZdI+DneIiROKTG3YDZvGn0EdV16OG5LzFhbNgLT5GPzI+kTwgLpAsucBHklexlB4kfg== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" @@ -28558,10 +28565,10 @@ vega-transforms@~4.9.3: vega-time "^2.0.4" vega-util "^1.15.2" -vega-typings@~0.19.0: - version "0.19.1" - resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.19.1.tgz#a53949143fa37721ae7bd146bbb9add5c78aca52" - integrity sha512-OSyNYwMJ8FayTTNU/gohprbt1EFQBpoiMPP9p2vqo1O9z45XVnotQ92jYHAhraI6gWiMIIfo4OjPbSe/GX7etg== +vega-typings@~0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/vega-typings/-/vega-typings-0.19.2.tgz#374fc1020c1abb263a0be87de28d1a4bd0526c3f" + integrity sha512-YU/S9rDk4d+t4+4eTa9fzuw87PMNteeVtpcL51kUO8H7HvGaoW7ll8RHKLkR0NYBEGPRoFDKUxnoyMvhgjsdYw== dependencies: vega-util "^1.15.2" @@ -28613,20 +28620,20 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.0.tgz#2b33296e257c97b79ee6501d4d1905fb1414d080" - integrity sha512-2Rm9aS3cSMXE55YgjfkuOmvSBMtiM/85/qX/WHLc+YiJacKGiwY9yzeC+w2Ft50JUs3nKZc1KB90ePgf5mfo0Q== +vega@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.1.tgz#ac95144b40137201b9d71a13615cc5b6eac6e5f7" + integrity sha512-ev1S6ohnsyeqps/bUVbhByoAbucap8vXPuiAJcxxft/EpgQGbIX/x42l0ijc3U1QHow2Lr3khtE1RshyU4lW2w== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" vega-encode "~4.8.3" vega-event-selector "~2.0.6" - vega-expression "~3.0.0" + vega-expression "~3.0.1" vega-force "~4.0.7" vega-format "~1.0.4" vega-functions "~5.8.0" - vega-geo "~4.3.7" + vega-geo "~4.3.8" vega-hierarchy "~4.0.9" vega-label "~1.0.0" vega-loader "~4.4.0" @@ -28639,7 +28646,7 @@ vega@^5.17.0: vega-statistics "~1.7.9" vega-time "~2.0.4" vega-transforms "~4.9.3" - vega-typings "~0.19.0" + vega-typings "~0.19.2" vega-util "~1.16.0" vega-view "~5.9.0" vega-view-transforms "~4.5.8" From c4a8c688e971f23f4c9dbf354fd85dab9e97eab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 22 Dec 2020 09:34:03 +0100 Subject: [PATCH 033/100] fixing blank page (#86640) --- .../selectors/throuput_chart_selectors.test.ts | 8 ++++++++ .../apm/public/selectors/throuput_chart_selectors.ts | 12 +----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts index ac85142f3050b7..03877b9e5bff2f 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.test.ts @@ -37,6 +37,14 @@ describe('getThrouputChartSelector', () => { expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); }); + it('returns default values when timeseries is empty', () => { + const throughputTimeseries = getThrouputChartSelector({ + theme, + throuputChart: { throughputTimeseries: [] }, + }); + expect(throughputTimeseries).toEqual({ throughputTimeseries: [] }); + }); + it('return throughput time series', () => { const throughputTimeseries = getThrouputChartSelector({ theme, diff --git a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts index 701558b1546773..a392f247aec428 100644 --- a/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts +++ b/x-pack/plugins/apm/public/selectors/throuput_chart_selectors.ts @@ -8,7 +8,6 @@ import { difference, zipObject } from 'lodash'; import { EuiTheme } from '../../../observability/public'; import { asTransactionRate } from '../../common/utils/formatters'; import { TimeSeries } from '../../typings/timeseries'; -import { getEmptySeries } from '../components/shared/charts/helper/get_empty_series'; import { APIReturnType } from '../services/rest/createCallApmApi'; import { httpStatusCodeToColor } from '../utils/httpStatusCodeToColor'; @@ -34,7 +33,7 @@ export function getThrouputChartSelector({ }; } -export function getThroughputTimeseries({ +function getThroughputTimeseries({ throuputChart, theme, }: { @@ -45,15 +44,6 @@ export function getThroughputTimeseries({ const bucketKeys = throughputTimeseries.map(({ key }) => key); const getColor = getColorByKey(bucketKeys, theme); - if (!throughputTimeseries.length) { - const start = throughputTimeseries[0].dataPoints[0].x; - const end = - throughputTimeseries[0].dataPoints[ - throughputTimeseries[0].dataPoints.length - 1 - ].x; - return getEmptySeries(start, end); - } - return throughputTimeseries.map((bucket) => { return { title: bucket.key, From a79e8a341cb058f503ee69e1e50d19c2b8f14941 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 22 Dec 2020 11:15:28 +0200 Subject: [PATCH 034/100] [Visualizations] Remove charts - editor plugins cyclic dependencies (#84887) * Remove charts - editor cyclic dependencies * Move translations from the charts plugin to the editor plugin * Remove the dependency from the script as it is gone * Fix types Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../run_find_plugins_with_circular_deps.ts | 1 - src/plugins/charts/kibana.json | 3 +- .../charts/public/static/components/index.ts | 9 ------ src/plugins/maps_legacy/kibana.json | 2 +- .../components/wms_internal_options.tsx | 2 +- .../public/components/wms_options.tsx | 2 +- .../public/components/region_map_options.tsx | 2 +- .../public/components/tile_map_options.tsx | 9 ++++-- .../components/options}/basic_options.tsx | 7 ++--- .../components/options}/color_ranges.tsx | 4 +-- .../components/options}/color_schema.tsx | 13 ++++----- .../public/components/options/index.ts | 28 +++++++++++++++++++ .../components/options}/number_input.tsx | 0 .../public/components/options}/range.tsx | 2 +- .../options}/required_number_input.tsx | 0 .../public/components/options}/select.tsx | 0 .../public/components/options}/switch.tsx | 0 .../public/components/options}/text_input.tsx | 0 .../vis_default_editor/public/index.ts | 1 + src/plugins/vis_type_markdown/kibana.json | 2 +- .../public/settings_options.tsx | 3 +- .../public/components/metric_vis_options.tsx | 12 ++++---- src/plugins/vis_type_table/kibana.json | 1 - .../public/components/table_vis_options.tsx | 8 ++++-- .../public/components/tag_cloud_options.tsx | 3 +- .../editor/components/gauge/labels_panel.tsx | 3 +- .../editor/components/gauge/ranges_panel.tsx | 8 ++---- .../editor/components/gauge/style_panel.tsx | 2 +- .../editor/components/heatmap/index.tsx | 12 ++++---- .../components/heatmap/labels_panel.tsx | 3 +- .../public/editor/components/pie.tsx | 3 +- .../metrics_axes/category_axis_panel.tsx | 7 +++-- .../options/metrics_axes/chart_options.tsx | 2 +- .../metrics_axes/custom_extents_options.tsx | 2 +- .../options/metrics_axes/label_options.tsx | 3 +- .../metrics_axes/line_options.test.tsx | 2 +- .../options/metrics_axes/line_options.tsx | 6 +++- .../metrics_axes/value_axis_options.test.tsx | 2 +- .../metrics_axes/value_axis_options.tsx | 6 +++- .../options/metrics_axes/y_extents.test.tsx | 3 +- .../options/metrics_axes/y_extents.tsx | 2 +- .../point_series/elastic_charts_options.tsx | 2 +- .../options/point_series/grid_panel.tsx | 3 +- .../options/point_series/point_series.tsx | 2 +- .../options/point_series/threshold_panel.tsx | 3 +- .../translations/translations/ja-JP.json | 16 +++++------ .../translations/translations/zh-CN.json | 16 +++++------ 47 files changed, 124 insertions(+), 98 deletions(-) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/basic_options.tsx (86%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/color_ranges.tsx (94%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/color_schema.tsx (85%) create mode 100644 src/plugins/vis_default_editor/public/components/options/index.ts rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/number_input.tsx (100%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/range.tsx (96%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/required_number_input.tsx (100%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/select.tsx (100%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/switch.tsx (100%) rename src/plugins/{charts/public/static/components => vis_default_editor/public/components/options}/text_input.tsx (100%) diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts index 1a087e2a01fb27..4da887c631d779 100644 --- a/src/dev/run_find_plugins_with_circular_deps.ts +++ b/src/dev/run_find_plugins_with_circular_deps.ts @@ -32,7 +32,6 @@ type CircularDepList = Set; const allowedList: CircularDepList = new Set([ 'src/plugins/charts -> src/plugins/discover', - 'src/plugins/charts -> src/plugins/vis_default_editor', 'src/plugins/vis_default_editor -> src/plugins/visualizations', 'src/plugins/vis_default_editor -> src/plugins/visualize', 'src/plugins/visualizations -> src/plugins/visualize', diff --git a/src/plugins/charts/kibana.json b/src/plugins/charts/kibana.json index a6d4dbba7238fe..4510a1ea7d0653 100644 --- a/src/plugins/charts/kibana.json +++ b/src/plugins/charts/kibana.json @@ -3,6 +3,5 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["expressions"], - "requiredBundles": ["visDefaultEditor"] + "requiredPlugins": ["expressions"] } diff --git a/src/plugins/charts/public/static/components/index.ts b/src/plugins/charts/public/static/components/index.ts index c044d361bed18e..0d5d7bf3ba277d 100644 --- a/src/plugins/charts/public/static/components/index.ts +++ b/src/plugins/charts/public/static/components/index.ts @@ -17,17 +17,8 @@ * under the License. */ -export { BasicOptions } from './basic_options'; export { ColorMode, LabelRotation, defaultCountLabel } from './collections'; -export { ColorRanges, SetColorRangeValue } from './color_ranges'; -export { ColorSchemaOptions, SetColorSchemaOptionsValue } from './color_schema'; export { ColorSchemaParams, Labels, Style } from './types'; -export { NumberInputOption } from './number_input'; -export { RangeOption } from './range'; -export { RequiredNumberInputOption } from './required_number_input'; -export { SelectOption } from './select'; -export { SwitchOption } from './switch'; -export { TextInputOption } from './text_input'; export { LegendToggle } from './legend_toggle'; export { ColorPicker } from './color_picker'; export { CurrentTime } from './current_time'; diff --git a/src/plugins/maps_legacy/kibana.json b/src/plugins/maps_legacy/kibana.json index 1499b3de446b5b..9d4586ebce53bc 100644 --- a/src/plugins/maps_legacy/kibana.json +++ b/src/plugins/maps_legacy/kibana.json @@ -6,5 +6,5 @@ "ui": true, "server": true, "extraPublicDirs": ["common"], - "requiredBundles": ["kibanaReact", "charts"] + "requiredBundles": ["kibanaReact", "visDefaultEditor"] } diff --git a/src/plugins/maps_legacy/public/components/wms_internal_options.tsx b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx index d1def8153d1a84..86c15f10ae55d0 100644 --- a/src/plugins/maps_legacy/public/components/wms_internal_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_internal_options.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { TextInputOption } from '../../../charts/public'; +import { TextInputOption } from '../../../vis_default_editor/public'; import { WMSOptions } from '../common/types/external_basemap_types'; interface WmsInternalOptions { diff --git a/src/plugins/maps_legacy/public/components/wms_options.tsx b/src/plugins/maps_legacy/public/components/wms_options.tsx index 4892463bb9f856..79e08478f21553 100644 --- a/src/plugins/maps_legacy/public/components/wms_options.tsx +++ b/src/plugins/maps_legacy/public/components/wms_options.tsx @@ -24,7 +24,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { TmsLayer } from '../index'; import { Vis } from '../../../visualizations/public'; import { RegionMapVisParams } from '../common/types/region_map_types'; -import { SelectOption, SwitchOption } from '../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../vis_default_editor/public'; import { WmsInternalOptions } from './wms_internal_options'; import { WMSOptions, TileMapVisParams } from '../common/types/external_basemap_types'; diff --git a/src/plugins/region_map/public/components/region_map_options.tsx b/src/plugins/region_map/public/components/region_map_options.tsx index 4d564d7347a1e9..b2bb250d66ee21 100644 --- a/src/plugins/region_map/public/components/region_map_options.tsx +++ b/src/plugins/region_map/public/components/region_map_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { FileLayerField, VectorLayer, IServiceSettings } from '../../../maps_legacy/public'; -import { NumberInputOption, SelectOption, SwitchOption } from '../../../charts/public'; +import { SelectOption, SwitchOption, NumberInputOption } from '../../../vis_default_editor/public'; import { RegionMapVisParams, WmsOptions } from '../../../maps_legacy/public'; const mapLayerForOption = ({ layerId, name }: VectorLayer) => ({ diff --git a/src/plugins/tile_map/public/components/tile_map_options.tsx b/src/plugins/tile_map/public/components/tile_map_options.tsx index 1a7b11ccf6e208..a6c0bb8a50dda0 100644 --- a/src/plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/plugins/tile_map/public/components/tile_map_options.tsx @@ -21,8 +21,13 @@ import React, { useEffect } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { BasicOptions, RangeOption, SelectOption, SwitchOption } from '../../../charts/public'; +import { + VisOptionsProps, + BasicOptions, + SelectOption, + SwitchOption, + RangeOption, +} from '../../../vis_default_editor/public'; import { WmsOptions, TileMapVisParams, MapTypes } from '../../../maps_legacy/public'; export type TileMapOptionsProps = VisOptionsProps; diff --git a/src/plugins/charts/public/static/components/basic_options.tsx b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx similarity index 86% rename from src/plugins/charts/public/static/components/basic_options.tsx rename to src/plugins/vis_default_editor/public/components/options/basic_options.tsx index 9c5a22543df994..f67a9997cb5e2a 100644 --- a/src/plugins/charts/public/static/components/basic_options.tsx +++ b/src/plugins/vis_default_editor/public/components/options/basic_options.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; - +import { VisOptionsProps } from '../../vis_options_props'; import { SwitchOption } from './switch'; import { SelectOption } from './select'; @@ -39,7 +38,7 @@ function BasicOptions({ return ( <> ({ setValue={setValue} /> void; @@ -71,7 +71,7 @@ function ColorRanges({ return ( ( paramName: T, @@ -67,7 +66,7 @@ function ColorSchemaOptions({ }} > @@ -80,11 +79,11 @@ function ColorSchemaOptions({ disabled={disabled} helpText={ showHelpText && - i18n.translate('charts.controls.colorSchema.howToChangeColorsDescription', { + i18n.translate('visDefaultEditor.options.colorSchema.howToChangeColorsDescription', { defaultMessage: 'Individual colors can be changed in the legend.', }) } - label={i18n.translate('charts.controls.colorSchema.colorSchemaLabel', { + label={i18n.translate('visDefaultEditor.options.colorSchema.colorSchemaLabel', { defaultMessage: 'Color schema', })} labelAppend={isCustomColors && resetColorsButton} @@ -96,7 +95,7 @@ function ColorSchemaOptions({ ({ const [stateValue, setStateValue] = useState(value); const [isValidState, setIsValidState] = useState(true); - const error = i18n.translate('charts.controls.rangeErrorMessage', { + const error = i18n.translate('visDefaultEditor.options.rangeErrorMessage', { defaultMessage: 'Values must be on or between {min} and {max}', values: { min, max }, }); diff --git a/src/plugins/charts/public/static/components/required_number_input.tsx b/src/plugins/vis_default_editor/public/components/options/required_number_input.tsx similarity index 100% rename from src/plugins/charts/public/static/components/required_number_input.tsx rename to src/plugins/vis_default_editor/public/components/options/required_number_input.tsx diff --git a/src/plugins/charts/public/static/components/select.tsx b/src/plugins/vis_default_editor/public/components/options/select.tsx similarity index 100% rename from src/plugins/charts/public/static/components/select.tsx rename to src/plugins/vis_default_editor/public/components/options/select.tsx diff --git a/src/plugins/charts/public/static/components/switch.tsx b/src/plugins/vis_default_editor/public/components/options/switch.tsx similarity index 100% rename from src/plugins/charts/public/static/components/switch.tsx rename to src/plugins/vis_default_editor/public/components/options/switch.tsx diff --git a/src/plugins/charts/public/static/components/text_input.tsx b/src/plugins/vis_default_editor/public/components/options/text_input.tsx similarity index 100% rename from src/plugins/charts/public/static/components/text_input.tsx rename to src/plugins/vis_default_editor/public/components/options/text_input.tsx diff --git a/src/plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts index d7eb5eda7bdfe0..0d99f990a512dd 100644 --- a/src/plugins/vis_default_editor/public/index.ts +++ b/src/plugins/vis_default_editor/public/index.ts @@ -19,6 +19,7 @@ export { DefaultEditorController } from './default_editor_controller'; export { useValidation } from './components/controls/utils'; +export * from './components/options'; export { RangesParamEditor, RangeValues } from './components/controls/ranges'; export * from './editor_size'; export * from './vis_options_props'; diff --git a/src/plugins/vis_type_markdown/kibana.json b/src/plugins/vis_type_markdown/kibana.json index c0afcb0e99d139..6cfedf60687efe 100644 --- a/src/plugins/vis_type_markdown/kibana.json +++ b/src/plugins/vis_type_markdown/kibana.json @@ -4,5 +4,5 @@ "ui": true, "server": true, "requiredPlugins": ["expressions", "visualizations"], - "requiredBundles": ["kibanaReact", "charts", "visualizations", "expressions", "visDefaultEditor"] + "requiredBundles": ["kibanaReact", "visualizations", "expressions", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx index bf4570db5d4a0d..1b793ca573f827 100644 --- a/src/plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/plugins/vis_type_markdown/public/settings_options.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { RangeOption, SwitchOption } from '../../charts/public'; +import { VisOptionsProps, SwitchOption, RangeOption } from '../../vis_default_editor/public'; import { MarkdownVisParams } from './types'; function SettingsOptions({ stateParams, setValue }: VisOptionsProps) { diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx index d87a0da740d751..58c486dfa90ab8 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -29,16 +29,16 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { - ColorMode, ColorRanges, - ColorSchemaOptions, + SetColorRangeValue, + VisOptionsProps, SwitchOption, - RangeOption, SetColorSchemaOptionsValue, - SetColorRangeValue, -} from '../../../charts/public'; + ColorSchemaOptions, + RangeOption, +} from '../../../vis_default_editor/public'; +import { ColorMode } from '../../../charts/public'; import { MetricVisParam, VisParams } from '../types'; function MetricVisOptions({ diff --git a/src/plugins/vis_type_table/kibana.json b/src/plugins/vis_type_table/kibana.json index dce9bce0e88861..1fb8516851ebd9 100644 --- a/src/plugins/vis_type_table/kibana.json +++ b/src/plugins/vis_type_table/kibana.json @@ -13,7 +13,6 @@ "kibanaUtils", "kibanaReact", "share", - "charts", "visDefaultEditor" ], "optionalPlugins": ["usageCollection"] diff --git a/src/plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx index b81f0425011dae..3932db1262acf7 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -23,9 +23,13 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { search } from '../../../data/public'; -import { SwitchOption, SelectOption, NumberInputOption } from '../../../charts/public'; +import { + SwitchOption, + SelectOption, + NumberInputOption, + VisOptionsProps, +} from '../../../vis_default_editor/public'; import { TableVisParams } from '../types'; import { totalAggregations } from './utils'; diff --git a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index d33576e4e55294..5d5f499d650b47 100644 --- a/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -20,9 +20,8 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; +import { VisOptionsProps, SelectOption, SwitchOption } from '../../../vis_default_editor/public'; import { ValidatedDualRange } from '../../../kibana_react/public'; -import { SelectOption, SwitchOption } from '../../../charts/public'; import { TagCloudVisParams } from '../types'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx index 0bd5694f710213..e02f62fa6ed6b8 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/labels_panel.tsx @@ -21,8 +21,7 @@ import React from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - -import { SwitchOption, TextInputOption } from '../../../../../charts/public'; +import { SwitchOption, TextInputOption } from '../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; function LabelsPanel({ stateParams, setValue, setGaugeValue }: GaugeOptionsInternalProps) { diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx index c297fb08e4b68b..ec5201af2e7d0a 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/ranges_panel.tsx @@ -21,15 +21,13 @@ import React, { useCallback } from 'react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; - import { ColorRanges, - ColorSchemaOptions, - ColorSchemaParams, SetColorRangeValue, SwitchOption, - ColorSchemas, -} from '../../../../../charts/public'; + ColorSchemaOptions, +} from '../../../../../vis_default_editor/public'; +import { ColorSchemaParams, ColorSchemas } from '../../../../../charts/public'; import { GaugeOptionsInternalProps } from '../gauge'; import { Gauge } from '../../../gauge'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx index b299b2e86ca403..9cb807aac5759c 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/gauge/style_panel.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption } from '../../../../../charts/public'; +import { SelectOption } from '../../../../../vis_default_editor/public'; import { GaugeOptionsInternalProps } from '../gauge'; import { AggGroupNames } from '../../../../../data/public'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx index f5b853accb08e9..a409762b30f9fe 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/index.tsx @@ -23,18 +23,18 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { + VisOptionsProps, BasicOptions, - ColorRanges, - ColorSchemaOptions, - NumberInputOption, SelectOption, SwitchOption, - SetColorSchemaOptionsValue, + ColorRanges, SetColorRangeValue, -} from '../../../../../charts/public'; + SetColorSchemaOptionsValue, + ColorSchemaOptions, + NumberInputOption, +} from '../../../../../vis_default_editor/public'; import { HeatmapVisParams } from '../../../heatmap'; import { LabelsPanel } from './labels_panel'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx index 8ec06ea50ec123..506e5f74dc972a 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/heatmap/labels_panel.tsx @@ -23,8 +23,7 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; -import { SwitchOption } from '../../../../../charts/public'; +import { VisOptionsProps, SwitchOption } from '../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../../vis_type_xy/public'; import { HeatmapVisParams } from '../../../heatmap'; diff --git a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx index 1c3aa501b4d008..01516630287ecf 100644 --- a/src/plugins/vis_type_vislib/public/editor/components/pie.tsx +++ b/src/plugins/vis_type_vislib/public/editor/components/pie.tsx @@ -22,8 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; -import { BasicOptions, SwitchOption } from '../../../../charts/public'; +import { BasicOptions, SwitchOption, VisOptionsProps } from '../../../../vis_default_editor/public'; import { TruncateLabelsOption } from '../../../../vis_type_xy/public'; import { PieVisParams } from '../../pie'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx index a5511637475263..d4647ae41a6372 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/category_axis_panel.tsx @@ -24,8 +24,11 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { Position } from '@elastic/charts'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; -import { VisOptionsProps } from '../../../../../../vis_default_editor/public'; +import { + SelectOption, + SwitchOption, + VisOptionsProps, +} from '../../../../../../vis_default_editor/public'; import { LabelOptions, SetAxisLabel } from './label_options'; import { CategoryAxis } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx index c379fa30b49b85..070d5fe0181509 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/chart_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { SelectOption } from '../../../../../../charts/public'; +import { SelectOption } from '../../../../../../vis_default_editor/public'; import { SeriesParam, ValueAxis } from '../../../../types'; import { LineOptions } from './line_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx index 86a0c56e46942d..f64bdba542b99e 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/custom_extents_options.tsx @@ -21,7 +21,7 @@ import React, { useCallback, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption, SwitchOption } from '../../../../../../charts/public'; +import { NumberInputOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { YExtents } from './y_extents'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx index 8c5c440ad9de92..bc00e3768aed65 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/label_options.tsx @@ -23,7 +23,8 @@ import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SelectOption, SwitchOption, Labels } from '../../../../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; +import { Labels } from '../../../../../../charts/public'; import { TruncateLabelsOption } from '../../common'; import { getRotateOptions } from '../../../collections'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx index 7727f90f791077..c4a8fea510f826 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.test.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { NumberInputOption } from '../../../../../../charts/public'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; import { LineOptions, LineOptionsParams } from './line_options'; import { seriesParam, vis } from './mocks'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx index df2735396b38d4..39a2ad8de95fdb 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/line_options.tsx @@ -23,7 +23,11 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { NumberInputOption, SelectOption, SwitchOption } from '../../../../../../charts/public'; +import { + NumberInputOption, + SelectOption, + SwitchOption, +} from '../../../../../../vis_default_editor/public'; import { SeriesParam } from '../../../../types'; import { SetChart } from './chart_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx index 0b325198c3fe7c..62757d14a01961 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.test.tsx @@ -22,7 +22,7 @@ import { shallow } from 'enzyme'; import { Position } from '@elastic/charts'; -import { TextInputOption } from '../../../../../../charts/public'; +import { TextInputOption } from '../../../../../../vis_default_editor/public'; import { ValueAxis, ScaleType } from '../../../../types'; import { LabelOptions } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx index 4ab792142e83ad..d81ddcb95ce623 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/value_axis_options.tsx @@ -22,7 +22,11 @@ import { i18n } from '@kbn/i18n'; import { EuiSpacer, EuiAccordion, EuiHorizontalRule } from '@elastic/eui'; import { Vis } from '../../../../../../visualizations/public'; -import { SelectOption, SwitchOption, TextInputOption } from '../../../../../../charts/public'; +import { + SelectOption, + SwitchOption, + TextInputOption, +} from '../../../../../../vis_default_editor/public'; import { ValueAxis } from '../../../../types'; import { LabelOptions, SetAxisLabel } from './label_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx index c2af7f2ad921b7..27a28d96d06086 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.test.tsx @@ -20,10 +20,9 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; -import { NumberInputOption } from '../../../../../../charts/public'; - import { ScaleType } from '../../../../types'; import { YExtents, YExtentsProps } from './y_extents'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; describe('YExtents component', () => { let setMultipleValidity: jest.Mock; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx index 11d049d4864a7d..ba7049e9845738 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/metrics_axes/y_extents.tsx @@ -21,7 +21,7 @@ import React, { useEffect, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { NumberInputOption } from '../../../../../../charts/public'; +import { NumberInputOption } from '../../../../../../vis_default_editor/public'; import { Scale, ScaleType } from '../../../../types'; import { SetScale } from './value_axis_options'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx index 126c5521f0633a..f40972e86af6b0 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { ChartType } from '../../../../../common'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx index c6ad52f7112c96..9efc9b65b19ee0 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/grid_panel.tsx @@ -23,8 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { SelectOption, SwitchOption } from '../../../../../../charts/public'; - +import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { VisParams, ValueAxis } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx index 283fc28aed46ef..1d00f80e0b0d71 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/point_series.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { BasicOptions, SwitchOption } from '../../../../../../charts/public'; +import { BasicOptions, SwitchOption } from '../../../../../../vis_default_editor/public'; import { BUCKET_TYPES } from '../../../../../../data/public'; import { VisParams } from '../../../../types'; diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx index ec21a386a5679a..8eab0c478e67b7 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/threshold_panel.tsx @@ -26,8 +26,7 @@ import { SelectOption, SwitchOption, RequiredNumberInputOption, -} from '../../../../../../charts/public'; - +} from '../../../../../../vis_default_editor/public'; import { ValidationVisOptionsProps } from '../../common'; import { VisParams } from '../../../../types'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e5b82a5d3fcbc7..b61532ec88c3cf 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -247,14 +247,6 @@ "charts.colormaps.greysText": "グレー", "charts.colormaps.redsText": "赤", "charts.colormaps.yellowToRedText": "黄色から赤", - "charts.controls.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。", - "charts.controls.colorSchema.colorSchemaLabel": "配色", - "charts.controls.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。", - "charts.controls.colorSchema.resetColorsButtonLabel": "色をリセット", - "charts.controls.colorSchema.reverseColorSchemaLabel": "図表を反転", - "charts.controls.rangeErrorMessage": "値は{min}と{max}の間でなければなりません", - "charts.controls.vislibBasicOptions.legendPositionLabel": "凡例位置", - "charts.controls.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示", "charts.colorPicker.setColor.screenReaderDescription": "値 {legendDataLabel} の色を設定", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", @@ -3615,6 +3607,14 @@ "visDefaultEditor.editorConfig.dateHistogram.customInterval.helpText": "構成間隔の倍数でなければなりません: {interval}", "visDefaultEditor.editorConfig.histogram.interval.helpText": "構成間隔の倍数でなければなりません: {interval}", "visDefaultEditor.metrics.wrongLastBucketTypeErrorMessage": "「{type}」メトリック集約を使用する場合、最後のバケット集約は「Date Histogram」または「Histogram」でなければなりません。", + "visDefaultEditor.options.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。", + "visDefaultEditor.options.colorSchema.colorSchemaLabel": "配色", + "visDefaultEditor.options.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。", + "visDefaultEditor.options.colorSchema.resetColorsButtonLabel": "色をリセット", + "visDefaultEditor.options.colorSchema.reverseColorSchemaLabel": "図表を反転", + "visDefaultEditor.options.rangeErrorMessage": "値は{min}と{max}の間でなければなりません", + "visDefaultEditor.options.vislibBasicOptions.legendPositionLabel": "凡例位置", + "visDefaultEditor.options.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示", "visDefaultEditor.sidebar.autoApplyChangesOffLabel": "自動適用がオフです", "visDefaultEditor.sidebar.autoApplyChangesOnLabel": "自動適用がオンです", "visDefaultEditor.sidebar.autoApplyChangesTooltip": "変更されるごとにビジュアライゼーションを自動的に更新します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 879418870b527f..5377ae790c6014 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -247,14 +247,6 @@ "charts.colormaps.greysText": "灰色", "charts.colormaps.redsText": "红色", "charts.colormaps.yellowToRedText": "黄到红", - "charts.controls.colorRanges.errorText": "每个范围应大于前一范围。", - "charts.controls.colorSchema.colorSchemaLabel": "颜色方案", - "charts.controls.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。", - "charts.controls.colorSchema.resetColorsButtonLabel": "重置颜色", - "charts.controls.colorSchema.reverseColorSchemaLabel": "反向方案", - "charts.controls.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", - "charts.controls.vislibBasicOptions.legendPositionLabel": "图例位置", - "charts.controls.vislibBasicOptions.showTooltipLabel": "显示工具提示", "charts.colorPicker.setColor.screenReaderDescription": "为值 {legendDataLabel} 设置颜色", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "控制台", @@ -3616,6 +3608,14 @@ "visDefaultEditor.editorConfig.dateHistogram.customInterval.helpText": "必须是配置时间间隔的倍数:{interval}", "visDefaultEditor.editorConfig.histogram.interval.helpText": "必须是配置时间间隔的倍数:{interval}", "visDefaultEditor.metrics.wrongLastBucketTypeErrorMessage": "使用“{type}”指标聚合时,上一存储桶聚合必须是“Date Histogram”或“Histogram”。", + "visDefaultEditor.options.colorRanges.errorText": "每个范围应大于前一范围。", + "visDefaultEditor.options.colorSchema.colorSchemaLabel": "颜色方案", + "visDefaultEditor.options.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。", + "visDefaultEditor.options.colorSchema.resetColorsButtonLabel": "重置颜色", + "visDefaultEditor.options.colorSchema.reverseColorSchemaLabel": "反向方案", + "visDefaultEditor.options.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", + "visDefaultEditor.options.vislibBasicOptions.legendPositionLabel": "图例位置", + "visDefaultEditor.options.vislibBasicOptions.showTooltipLabel": "显示工具提示", "visDefaultEditor.sidebar.autoApplyChangesOffLabel": "自动应用关闭", "visDefaultEditor.sidebar.autoApplyChangesOnLabel": "自动应用开启", "visDefaultEditor.sidebar.autoApplyChangesTooltip": "每次更改时自动更新可视化。", From 39af596d69a26355c1ef20d188c26e4b7c8e686d Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Tue, 22 Dec 2020 10:48:47 +0000 Subject: [PATCH 035/100] [ML] Fix alignment of values in data frame analytics results view badges (#86621) --- .../expandable_section/expandable_section.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx index 45daa9f7a25b61..687800ed5fe829 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx @@ -97,10 +97,18 @@ export const ExpandableSection: FC = ({ > {label !== undefined && value !== undefined && ( <> - -

{label}

-
- {value} + + + +

{label}

+
+
+
+ + + {value} + + )} {label === undefined && ( From e2c4356108cab1f7e6a5af0cd5cb649d4c49311a Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 22 Dec 2020 15:05:06 +0300 Subject: [PATCH 036/100] [Visualizations] Remove vis_default_editor - visualize plugins cyclic dependencies (#85422) * [Visualizations] Remove vis_default_editor - visualize plugins cyclic dependencies # Conflicts: # src/plugins/visualize/kibana.json * fix CI * fix CI Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../run_find_plugins_with_circular_deps.ts | 1 - src/plugins/vis_default_editor/kibana.json | 1 + .../vis_default_editor/public/index.ts | 16 ++++---- .../vis_default_editor/public/plugin.ts | 40 +++++++++++++++++++ src/plugins/visualize/kibana.json | 5 +-- .../utils/use/use_saved_vis_instance.test.ts | 17 ++++++-- .../utils/use/use_saved_vis_instance.ts | 4 +- .../application/utils/use/use_vis_byvalue.ts | 5 ++- src/plugins/visualize/public/index.ts | 4 +- src/plugins/visualize/public/plugin.ts | 20 +++++++++- src/plugins/visualize/public/services.ts | 5 +++ 11 files changed, 95 insertions(+), 23 deletions(-) create mode 100644 src/plugins/vis_default_editor/public/plugin.ts diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts index 4da887c631d779..9fa2d28b8d5c5d 100644 --- a/src/dev/run_find_plugins_with_circular_deps.ts +++ b/src/dev/run_find_plugins_with_circular_deps.ts @@ -33,7 +33,6 @@ type CircularDepList = Set; const allowedList: CircularDepList = new Set([ 'src/plugins/charts -> src/plugins/discover', 'src/plugins/vis_default_editor -> src/plugins/visualizations', - 'src/plugins/vis_default_editor -> src/plugins/visualize', 'src/plugins/visualizations -> src/plugins/visualize', 'x-pack/plugins/actions -> x-pack/plugins/case', 'x-pack/plugins/case -> x-pack/plugins/security_solution', diff --git a/src/plugins/vis_default_editor/kibana.json b/src/plugins/vis_default_editor/kibana.json index 35ad0a3a8be9ac..9664b14821c0d9 100644 --- a/src/plugins/vis_default_editor/kibana.json +++ b/src/plugins/vis_default_editor/kibana.json @@ -2,5 +2,6 @@ "id": "visDefaultEditor", "version": "kibana", "ui": true, + "optionalPlugins": ["visualize"], "requiredBundles": ["kibanaUtils", "kibanaReact", "data"] } diff --git a/src/plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts index 0d99f990a512dd..fd1bdf8b2e65de 100644 --- a/src/plugins/vis_default_editor/public/index.ts +++ b/src/plugins/vis_default_editor/public/index.ts @@ -17,7 +17,11 @@ * under the License. */ -export { DefaultEditorController } from './default_editor_controller'; +import { PluginInitializerContext } from 'kibana/public'; +import { DefaultEditorController } from './default_editor_controller'; +import { VisDefaultEditorPlugin } from './plugin'; + +export { DefaultEditorController }; export { useValidation } from './components/controls/utils'; export * from './components/options'; export { RangesParamEditor, RangeValues } from './components/controls/ranges'; @@ -26,10 +30,6 @@ export * from './vis_options_props'; export * from './utils'; export { ISchemas, Schemas, Schema } from './schemas'; -/** dummy plugin, we just want visDefaultEditor to have its own bundle */ -export function plugin() { - return new (class VisDefaultEditor { - setup() {} - start() {} - })(); -} +export const plugin = (context: PluginInitializerContext) => { + return new VisDefaultEditorPlugin(); +}; diff --git a/src/plugins/vis_default_editor/public/plugin.ts b/src/plugins/vis_default_editor/public/plugin.ts new file mode 100644 index 00000000000000..a7a5c6146a6e84 --- /dev/null +++ b/src/plugins/vis_default_editor/public/plugin.ts @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreSetup, Plugin } from 'kibana/public'; + +import { VisualizePluginSetup } from '../../visualize/public'; +import { DefaultEditorController } from './default_editor_controller'; + +export interface VisDefaultEditorSetupDependencies { + visualize: VisualizePluginSetup; +} + +export class VisDefaultEditorPlugin + implements Plugin { + public setup(core: CoreSetup, { visualize }: VisDefaultEditorSetupDependencies) { + if (visualize) { + visualize.setDefaultEditor(DefaultEditorController); + } + } + + public start() {} + + stop() {} +} diff --git a/src/plugins/visualize/kibana.json b/src/plugins/visualize/kibana.json index 27229a11cd99f9..7f5c7d0dc08a20 100644 --- a/src/plugins/visualize/kibana.json +++ b/src/plugins/visualize/kibana.json @@ -22,8 +22,7 @@ "kibanaUtils", "kibanaReact", "home", - "discover", - "visDefaultEditor", - "presentationUtil" + "presentationUtil", + "discover" ] } diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts index 3f9676a9c93859..3995ebfd37253b 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.test.ts @@ -26,6 +26,7 @@ import { redirectWhenMissing } from '../../../../../kibana_utils/public'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { VisualizeServices } from '../../types'; import { VisualizeConstants } from '../../visualize_constants'; +import { setDefaultEditor } from '../../../services'; const mockDefaultEditorControllerDestroy = jest.fn(); const mockEmbeddableHandlerDestroy = jest.fn(); @@ -54,10 +55,14 @@ jest.mock('../breadcrumbs', () => ({ getEditBreadcrumbs: jest.fn((text) => text), getCreateBreadcrumbs: jest.fn((text) => text), })); -jest.mock('../../../../../vis_default_editor/public', () => ({ - DefaultEditorController: jest.fn(() => ({ destroy: mockDefaultEditorControllerDestroy })), -})); -jest.mock('../../../../../kibana_utils/public'); + +jest.mock('../../../../../kibana_utils/public', () => { + const actual = jest.requireActual('../../../../../kibana_utils/public'); + return { + ...actual, + redirectWhenMissing: jest.fn(), + }; +}); const mockGetVisualizationInstance = jest.requireMock('../get_visualization_instance') .getVisualizationInstance; @@ -69,6 +74,10 @@ describe('useSavedVisInstance', () => { const eventEmitter = new EventEmitter(); beforeEach(() => { + setDefaultEditor( + jest.fn().mockImplementation(() => ({ destroy: mockDefaultEditorControllerDestroy })) + ); + mockServices = ({ ...coreStartMock, toastNotifications, diff --git a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts index 44fbcce82f458b..9c156f20be375d 100644 --- a/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts +++ b/src/plugins/visualize/public/application/utils/use/use_saved_vis_instance.ts @@ -23,12 +23,12 @@ import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { redirectWhenMissing } from '../../../../../kibana_utils/public'; -import { DefaultEditorController } from '../../../../../vis_default_editor/public'; import { getVisualizationInstance } from '../get_visualization_instance'; import { getEditBreadcrumbs, getCreateBreadcrumbs } from '../breadcrumbs'; import { SavedVisInstance, IEditorController, VisualizeServices } from '../../types'; import { VisualizeConstants } from '../../visualize_constants'; +import { getDefaultEditor } from '../../../services'; /** * This effect is responsible for instantiating a saved vis or creating a new one @@ -104,7 +104,7 @@ export const useSavedVisInstance = ( // do not create editor in embeded mode if (visEditorRef.current) { if (isChromeVisible) { - const Editor = vis.type.editor || DefaultEditorController; + const Editor = vis.type.editor || getDefaultEditor(); visEditorController = new Editor( visEditorRef.current, vis, diff --git a/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts b/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts index e0286a63b9febb..ec97c221839409 100644 --- a/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts +++ b/src/plugins/visualize/public/application/utils/use/use_vis_byvalue.ts @@ -23,7 +23,7 @@ import { VisualizeInput } from 'src/plugins/visualizations/public'; import { ByValueVisInstance, IEditorController, VisualizeServices } from '../../types'; import { getVisualizationInstanceFromInput } from '../get_visualization_instance'; import { getBreadcrumbsPrefixedWithApp, getEditBreadcrumbs } from '../breadcrumbs'; -import { DefaultEditorController } from '../../../../../vis_default_editor/public'; +import { getDefaultEditor } from '../../../services'; export const useVisByValue = ( services: VisualizeServices, @@ -46,7 +46,8 @@ export const useVisByValue = ( } const byValueVisInstance = await getVisualizationInstanceFromInput(services, valueInput); const { embeddableHandler, vis } = byValueVisInstance; - const Editor = vis.type.editor || DefaultEditorController; + + const Editor = vis.type.editor || getDefaultEditor(); const visEditorController = new Editor( visEditorRef.current, vis, diff --git a/src/plugins/visualize/public/index.ts b/src/plugins/visualize/public/index.ts index 246806f300800a..c9ac85c5123cee 100644 --- a/src/plugins/visualize/public/index.ts +++ b/src/plugins/visualize/public/index.ts @@ -18,7 +18,7 @@ */ import { PluginInitializerContext } from 'kibana/public'; -import { VisualizePlugin } from './plugin'; +import { VisualizePlugin, VisualizePluginSetup } from './plugin'; export type { EditorRenderProps, @@ -27,6 +27,8 @@ export type { } from './application/types'; export { VisualizeConstants } from './application/visualize_constants'; +export { VisualizePluginSetup }; + export const plugin = (context: PluginInitializerContext) => { return new VisualizePlugin(context); }; diff --git a/src/plugins/visualize/public/plugin.ts b/src/plugins/visualize/public/plugin.ts index bbd7be0d348834..173f3fbbb63635 100644 --- a/src/plugins/visualize/public/plugin.ts +++ b/src/plugins/visualize/public/plugin.ts @@ -44,7 +44,7 @@ import { UrlForwardingSetup, UrlForwardingStart } from '../../url_forwarding/pub import { VisualizationsStart } from '../../visualizations/public'; import { VisualizeConstants } from './application/visualize_constants'; import { FeatureCatalogueCategory, HomePublicPluginSetup } from '../../home/public'; -import { VisualizeServices } from './application/types'; +import { VisEditorConstructor, VisualizeServices } from './application/types'; import { DEFAULT_APP_CATEGORIES } from '../../../core/public'; import { SavedObjectsStart } from '../../saved_objects/public'; import { EmbeddableStart } from '../../embeddable/public'; @@ -57,6 +57,7 @@ import { setIndexPatterns, setQueryService, setShareService, + setDefaultEditor, } from './services'; import { visualizeFieldAction } from './actions/visualize_field_action'; import { createVisualizeUrlGenerator } from './url_generator'; @@ -81,9 +82,18 @@ export interface VisualizePluginSetupDependencies { uiActions: UiActionsSetup; } +export interface VisualizePluginSetup { + setDefaultEditor: (editor: VisEditorConstructor) => void; +} + export class VisualizePlugin implements - Plugin { + Plugin< + VisualizePluginSetup, + void, + VisualizePluginSetupDependencies, + VisualizePluginStartDependencies + > { private appStateUpdater = new BehaviorSubject(() => ({})); private stopUrlTracking: (() => void) | undefined = undefined; private currentHistory: ScopedHistory | undefined = undefined; @@ -231,6 +241,12 @@ export class VisualizePlugin category: FeatureCatalogueCategory.DATA, }); } + + return { + setDefaultEditor: (editor) => { + setDefaultEditor(editor); + }, + } as VisualizePluginSetup; } public start(core: CoreStart, plugins: VisualizePluginStartDependencies) { diff --git a/src/plugins/visualize/public/services.ts b/src/plugins/visualize/public/services.ts index 8190872ec6508a..7994ad14543d55 100644 --- a/src/plugins/visualize/public/services.ts +++ b/src/plugins/visualize/public/services.ts @@ -21,6 +21,7 @@ import { ApplicationStart, IUiSettingsClient } from '../../../core/public'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; import { IndexPatternsContract, DataPublicPluginStart } from '../../../plugins/data/public'; import { SharePluginStart } from '../../../plugins/share/public'; +import { VisEditorConstructor } from './application/types'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -32,6 +33,10 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( + 'DefaultEditor' +); + export const [getQueryService, setQueryService] = createGetterSetter< DataPublicPluginStart['query'] >('Query'); From 3b5bf69070f374141aad0aaf202ee7d66123565f Mon Sep 17 00:00:00 2001 From: Silvia Mitter Date: Tue, 22 Dec 2020 13:12:23 +0100 Subject: [PATCH 037/100] update apm index pattern (#86739) --- src/plugins/apm_oss/server/tutorial/index_pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/apm_oss/server/tutorial/index_pattern.json b/src/plugins/apm_oss/server/tutorial/index_pattern.json index b9f3b43b67b844..6eb040f2758af8 100644 --- a/src/plugins/apm_oss/server/tutorial/index_pattern.json +++ b/src/plugins/apm_oss/server/tutorial/index_pattern.json @@ -1,7 +1,7 @@ { "attributes": { "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tracing.transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, From 8527b6cbe3c7c9fec6090536e96e05399bec29c5 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Tue, 22 Dec 2020 07:48:59 -0500 Subject: [PATCH 038/100] Fixed Result engine display (#86559) --- .../search_experience_content.test.tsx | 38 ++++++++++--------- .../search_experience_content.tsx | 12 ++++-- .../views/result_view.test.tsx | 6 ++- .../search_experience/views/result_view.tsx | 4 +- .../app_search/components/library/library.tsx | 13 ++++--- .../components/result/result.test.tsx | 22 ++++++----- .../app_search/components/result/result.tsx | 8 +++- .../components/result/result_header.test.tsx | 25 +++++++----- .../components/result/result_header.tsx | 7 ++-- .../app_search/components/result/types.ts | 1 - 10 files changed, 82 insertions(+), 54 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx index a46ec560a13e0a..8fc1ed5a0a4b6e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.test.tsx @@ -46,30 +46,32 @@ describe('SearchExperienceContent', () => { expect(wrapper.isEmptyRender()).toBe(false); }); - it('passes engineName and schema to the result view', () => { - const props = { - result: { - id: { - raw: '1', - }, - _meta: { - id: '1', - scopedId: '1', - score: 100, - engine: 'my-engine', - }, - foo: { - raw: 'bar', - }, + it('passes result, schema, and isMetaEngine to the result view', () => { + const result = { + id: { + raw: '1', }, - schemaForTypeHighlights: { - title: 'string' as SchemaTypes, + _meta: { + id: '1', + score: 100, + engine: 'my-engine', + }, + foo: { + raw: 'bar', }, }; const wrapper = shallow(); const resultView: any = wrapper.find(Results).prop('resultView'); - expect(resultView(props)).toEqual(); + expect(resultView({ result })).toEqual( + + ); }); it('renders pagination', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx index 55a8377261dd9b..b44f3115932a3f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience_content.tsx @@ -14,12 +14,12 @@ import { useValues } from 'kea'; import { ResultView } from './views'; import { Pagination } from './pagination'; -import { Props as ResultViewProps } from './views/result_view'; import { useSearchContextState } from './hooks'; import { DocumentCreationButton } from '../document_creation_button'; import { AppLogic } from '../../../app_logic'; import { EngineLogic } from '../../engine'; import { DOCS_PREFIX } from '../../../routes'; +import { Result } from '../../result/types'; export const SearchExperienceContent: React.FC = () => { const { resultSearchTerm, totalResults, wasSearched } = useSearchContextState(); @@ -43,8 +43,14 @@ export const SearchExperienceContent: React.FC = () => { { - return ; + resultView={({ result }: { result: Result }) => { + return ( + + ); }} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx index 91334f312623df..d3a61c12901d3a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.test.tsx @@ -22,7 +22,6 @@ describe('ResultView', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -33,11 +32,14 @@ describe('ResultView', () => { }; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find(Result).props()).toEqual({ result, shouldLinkToDetailPage: true, schemaForTypeHighlights: schema, + isMetaEngine: true, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx index 543c63b3349407..2a17dd6128536a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/result_view.tsx @@ -13,15 +13,17 @@ import { Result } from '../../../result/result'; export interface Props { result: ResultType; schemaForTypeHighlights?: Schema; + isMetaEngine: boolean; } -export const ResultView: React.FC = ({ result, schemaForTypeHighlights }) => { +export const ResultView: React.FC = ({ result, schemaForTypeHighlights, isMetaEngine }) => { return (
  • ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx index 1b222cfaacf7c1..24d2fea973e14e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/library/library.tsx @@ -20,13 +20,13 @@ import { Result } from '../result/result'; export const Library: React.FC = () => { const props = { + isMetaEngine: false, result: { id: { raw: '1', }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -98,6 +98,7 @@ export const Library: React.FC = () => { { { { { { }, _meta: { id: 'my-id-is-a-really-long-id-yes-it-is', - scopedId: '2', score: 100, engine: 'my-engine-is-a-really-long-engin-name-yes-it-is', }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx index 5b598a0b8565ec..c4de24e78eae5e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx @@ -18,6 +18,7 @@ import { Result } from './result'; describe('Result', () => { const props = { + isMetaEngine: false, result: { id: { raw: '1', @@ -33,7 +34,6 @@ describe('Result', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, @@ -60,14 +60,16 @@ describe('Result', () => { ]); }); - it('passes through showScore and resultMeta to ResultHeader', () => { - const wrapper = shallow(); - expect(wrapper.find(ResultHeader).prop('showScore')).toBe(true); - expect(wrapper.find(ResultHeader).prop('resultMeta')).toEqual({ - id: '1', - scopedId: '1', - score: 100, - engine: 'my-engine', + it('passes showScore, resultMeta, and isMetaEngine to ResultHeader', () => { + const wrapper = shallow(); + expect(wrapper.find(ResultHeader).props()).toEqual({ + isMetaEngine: true, + showScore: true, + resultMeta: { + id: '1', + score: 100, + engine: 'my-engine', + }, }); }); @@ -100,6 +102,7 @@ describe('Result', () => { describe('when there are more than 5 fields', () => { const propsWithMoreFields = { + isMetaEngine: false, result: { id: { raw: '1', @@ -124,7 +127,6 @@ describe('Result', () => { }, _meta: { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx index 11415f55123804..76b06754d6ce68 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx @@ -20,6 +20,7 @@ import { Schema } from '../../../shared/types'; interface Props { result: ResultType; + isMetaEngine: boolean; showScore?: boolean; shouldLinkToDetailPage?: boolean; schemaForTypeHighlights?: Schema; @@ -29,6 +30,7 @@ const RESULT_CUTOFF = 5; export const Result: React.FC = ({ result, + isMetaEngine, showScore = false, shouldLinkToDetailPage = false, schemaForTypeHighlights, @@ -68,7 +70,11 @@ export const Result: React.FC = ({ > {conditionallyLinkedArticle( <> - +
    {resultFields .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx index 95b77a0aed7bbd..4ccebb90eb6fec 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.test.tsx @@ -13,57 +13,64 @@ import { ResultHeader } from './result_header'; describe('ResultHeader', () => { const resultMeta = { id: '1', - scopedId: '1', score: 100, engine: 'my-engine', }; it('renders', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.isEmptyRender()).toBe(false); }); it('always renders an id', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultId"]').prop('value')).toEqual('1'); }); describe('score', () => { it('renders score if showScore is true ', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultScore"]').prop('value')).toEqual(100); }); it('does not render score if showScore is false', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper.find('[data-test-subj="ResultScore"]').exists()).toBe(false); }); }); describe('engine', () => { - it('renders engine name if the ids dont match, which means it is a meta engine', () => { + it('renders engine name if this is a meta engine', () => { const wrapper = shallow( ); expect(wrapper.find('[data-test-subj="ResultEngine"]').prop('value')).toBe('my-engine'); }); - it('does not render an engine name if the ids match, which means it is not a meta engine', () => { + it('does not render an engine if this is not a meta engine', () => { const wrapper = shallow( ); expect(wrapper.find('[data-test-subj="ResultEngine"]').exists()).toBe(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx index 9b83014d041ddd..14e0607e1249a0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result_header.tsx @@ -13,12 +13,11 @@ import './result_header.scss'; interface Props { showScore: boolean; + isMetaEngine: boolean; resultMeta: ResultMeta; } -export const ResultHeader: React.FC = ({ showScore, resultMeta }) => { - const showEngineLabel: boolean = resultMeta.id !== resultMeta.scopedId; - +export const ResultHeader: React.FC = ({ showScore, resultMeta, isMetaEngine }) => { return (
    {showScore && ( @@ -33,7 +32,7 @@ export const ResultHeader: React.FC = ({ showScore, resultMeta }) => { )}
    - {showEngineLabel && ( + {isMetaEngine && ( Date: Tue, 22 Dec 2020 15:43:01 +0100 Subject: [PATCH 039/100] Fixed the extraction of ServerApiError. (#86732) --- .../trusted_apps/store/middleware.test.ts | 6 ++++-- .../pages/trusted_apps/store/middleware.ts | 20 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index 44f43b90bdd0fc..735e63f8e084b3 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -179,7 +179,9 @@ describe('middleware', () => { const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); - service.getTrustedAppsList.mockRejectedValue(createServerApiError('Internal Server Error')); + service.getTrustedAppsList.mockRejectedValue({ + body: createServerApiError('Internal Server Error'), + }); store.dispatch(createUserChangedUrlAction('/trusted_apps', '?page_index=2&page_size=50')); @@ -315,7 +317,7 @@ describe('middleware', () => { const { store, spyMiddleware } = createStoreSetup(service); service.getTrustedAppsList.mockResolvedValue(getTrustedAppsListResponse); - service.deleteTrustedApp.mockRejectedValue(notFoundError); + service.deleteTrustedApp.mockRejectedValue({ body: notFoundError }); store.dispatch(createUserChangedUrlAction('/trusted_apps')); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index 48b2d7113f38e6..4508e25d3db33c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -95,7 +95,7 @@ const refreshListIfNeeded = async ( store.dispatch( createTrustedAppsListResourceStateChangedAction({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedListResourceState(store.getState()), }) ); @@ -103,13 +103,6 @@ const refreshListIfNeeded = async ( } }; -const createTrustedAppDeletionSubmissionResourceStateChanged = ( - newState: Immutable -): Immutable => ({ - type: 'trustedAppDeletionSubmissionResourceStateChanged', - payload: { newState }, -}); - const updateCreationDialogIfNeeded = ( store: ImmutableMiddlewareAPI ) => { @@ -167,7 +160,7 @@ const submitCreationIfNeeded = async ( store.dispatch( createTrustedAppCreationSubmissionResourceStateChanged({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedResourceState(submissionResourceState), }) ); @@ -175,6 +168,13 @@ const submitCreationIfNeeded = async ( } }; +const createTrustedAppDeletionSubmissionResourceStateChanged = ( + newState: Immutable +): Immutable => ({ + type: 'trustedAppDeletionSubmissionResourceStateChanged', + payload: { newState }, +}); + const submitDeletionIfNeeded = async ( store: ImmutableMiddlewareAPI, trustedAppsService: TrustedAppsService @@ -209,7 +209,7 @@ const submitDeletionIfNeeded = async ( store.dispatch( createTrustedAppDeletionSubmissionResourceStateChanged({ type: 'FailedResourceState', - error, + error: error.body, lastLoadedState: getLastLoadedResourceState(submissionResourceState), }) ); From 9bc2fccb2de8e96ce8347cd8fbd0b4514d7949c2 Mon Sep 17 00:00:00 2001 From: Peter Schretlen Date: Tue, 22 Dec 2020 09:54:54 -0500 Subject: [PATCH 040/100] use alerting concepts diagram with sequence, and update docs to explain the sequence (#86699) --- docs/user/alerting/alerting-getting-started.asciidoc | 11 ++++++----- docs/user/alerting/images/alert-concepts-summary.svg | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/user/alerting/alerting-getting-started.asciidoc b/docs/user/alerting/alerting-getting-started.asciidoc index 4eeecad0793486..cb2b9b19a0726e 100644 --- a/docs/user/alerting/alerting-getting-started.asciidoc +++ b/docs/user/alerting/alerting-getting-started.asciidoc @@ -123,14 +123,15 @@ image::images/alert-concepts-connectors.svg[Connectors provide a central place t [float] === Summary -An _alert_ consists of conditions, _actions_, and a schedule. When conditions are met, _alert instances_ are created that render _actions_ and invoke them. To make action setup and update easier, actions refer to _connectors_ that centralize the information used to connect with {kib} services and third-party integrations. +An _alert_ consists of conditions, _actions_, and a schedule. When conditions are met, _alert instances_ are created that render _actions_ and invoke them. To make action setup and update easier, actions refer to _connectors_ that centralize the information used to connect with {kib} services and third-party integrations. The following example ties these concepts together: image::images/alert-concepts-summary.svg[Alerts, actions, alert instances and connectors work together to convert detection into action] -* *Alert*: a specification of the conditions to be detected, the schedule for detection, and the response when detection occurs. -* *Action*: the response to a detected condition defined in the alert. Typically actions specify a service or third party integration along with alert details that will be sent to it. -* *Alert instance*: state tracked by {kib} for every occurrence of a detected condition. Actions as well as controls like muting and re-notification are controlled at the instance level. -* *Connector*: centralized configurations for services and third party integration that are referenced by actions. +. Anytime an *alert*'s conditions are met, an *alert instance* is created. This example checks for servers with average CPU > 0.9. Three servers meet the condition, so three instances are created. +. Instances create *actions* as long as they are not muted or throttled. When actions are created, the template that was setup in the alert is filled with actual values. In this example three actions are created, and the template string {{server}} is replaced with the server name for each instance. +. {kib} invokes the actions, sending them to a 3rd party *integration* like an email service. +. If the 3rd party integration has connection parameters or credentials, {kib} will fetch these from the *connector* referenced in the action. + [float] [[alerting-concepts-differences]] diff --git a/docs/user/alerting/images/alert-concepts-summary.svg b/docs/user/alerting/images/alert-concepts-summary.svg index 0d63601c0693d8..0aed3bf22375ff 100644 --- a/docs/user/alerting/images/alert-concepts-summary.svg +++ b/docs/user/alerting/images/alert-concepts-summary.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 23fd0445626951d28252a228ff6369389b7b967f Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 22 Dec 2020 10:33:27 -0500 Subject: [PATCH 041/100] [Lens] Introduce 4 new calculation functions: counter rate, cumulative sum, differences, and moving average (#84384) * [Lens] UI for reference-based functions * Fix tests * Add a few unit tests for reference editor * Respond to review comments * Update error handling * Update suggestion logic to work with errors and refs * Support ParamEditor in references to fix Last Value: refactoring as needed * Fix error states * Update logic for showing references in dimension editor, add tests * Fix tests Co-authored-by: Joe Reuter Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../workspace_panel/workspace_panel.tsx | 13 +- .../dimension_panel/dimension_editor.tsx | 83 ++- .../dimension_panel/dimension_panel.test.tsx | 113 +++- .../dimension_panel/field_select.tsx | 5 +- .../dimension_panel/operation_support.ts | 2 +- .../dimension_panel/reference_editor.test.tsx | 436 +++++++++++++++ .../dimension_panel/reference_editor.tsx | 306 +++++++++++ .../indexpattern.test.ts | 205 ++----- .../indexpattern_datasource/indexpattern.tsx | 126 ++--- .../indexpattern_suggestions.test.tsx | 507 ++++++++++++++++-- .../indexpattern_suggestions.ts | 96 ++-- .../operations/__mocks__/index.ts | 1 + .../definitions/calculations/counter_rate.tsx | 33 +- .../calculations/cumulative_sum.tsx | 43 +- .../definitions/calculations/derivative.tsx | 38 +- .../calculations/moving_average.tsx | 56 +- .../definitions/calculations/utils.test.ts | 73 +++ .../definitions/calculations/utils.ts | 82 ++- .../operations/definitions/cardinality.tsx | 9 +- .../operations/definitions/count.tsx | 7 +- .../definitions/date_histogram.test.tsx | 28 + .../operations/definitions/date_histogram.tsx | 5 +- .../operations/definitions/helpers.test.ts | 56 ++ .../operations/definitions/helpers.tsx | 9 + .../operations/definitions/index.ts | 11 +- .../definitions/last_value.test.tsx | 9 +- .../operations/definitions/last_value.tsx | 9 +- .../operations/definitions/metrics.tsx | 16 +- .../operations/definitions/ranges/ranges.tsx | 5 +- .../operations/definitions/terms/index.tsx | 38 +- .../definitions/terms/terms.test.tsx | 183 +++++-- .../operations/index.ts | 1 + .../operations/layer_helpers.test.ts | 235 ++++++-- .../operations/layer_helpers.ts | 293 +++++----- .../operations/operations.ts | 11 +- .../operations/time_scale_utils.test.ts | 67 ++- .../operations/time_scale_utils.ts | 16 +- .../public/indexpattern_datasource/utils.ts | 25 +- .../test/functional/apps/lens/smokescreen.ts | 75 +++ .../test/functional/page_objects/lens_page.ts | 39 ++ 40 files changed, 2614 insertions(+), 751 deletions(-) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 8820f26479cf92..99a5869a60872b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -162,7 +162,7 @@ export function WorkspacePanel({ const expression = useMemo( () => { - if (!configurationValidationError || configurationValidationError.length === 0) { + if (!configurationValidationError?.length) { try { return buildExpression({ visualization: activeVisualization, @@ -400,13 +400,17 @@ export const InnerVisualizationWrapper = ({ showExtraErrors = localState.configurationValidationError .slice(1) .map(({ longMessage }) => ( - + {longMessage} )); } else { showExtraErrors = ( - + { setLocalState((prevState: WorkspaceState) => ({ @@ -414,6 +418,7 @@ export const InnerVisualizationWrapper = ({ expandError: !prevState.expandError, })); }} + data-test-subj="configuration-failure-more-errors" > {i18n.translate('xpack.lens.editorFrame.configurationFailureMoreErrors', { defaultMessage: ` +{errors} {errors, plural, one {error} other {errors}}`, @@ -445,7 +450,7 @@ export const InnerVisualizationWrapper = ({ - + {localState.configurationValidationError[0].longMessage} {showExtraErrors} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index c655fc18ab5fae..cc22cbbf57883d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -16,6 +16,7 @@ import { EuiListGroupItemProps, EuiFormLabel, EuiToolTip, + EuiText, } from '@elastic/eui'; import { IndexPatternDimensionEditorProps } from './dimension_panel'; import { OperationSupportMatrix } from './operation_support'; @@ -37,6 +38,7 @@ import { BucketNestingEditor } from './bucket_nesting_editor'; import { IndexPattern, IndexPatternLayer } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { FormatSelector } from './format_selector'; +import { ReferenceEditor } from './reference_editor'; import { TimeScaling } from './time_scaling'; const operationPanels = getOperationDisplay(); @@ -156,7 +158,10 @@ export function DimensionEditor(props: DimensionEditorProps) { (selectedColumn && !hasField(selectedColumn) && definition.input === 'none'), disabledStatus: definition.getDisabledStatus && - definition.getDisabledStatus(state.indexPatterns[state.currentIndexPatternId]), + definition.getDisabledStatus( + state.indexPatterns[state.currentIndexPatternId], + state.layers[layerId] + ), }; }); @@ -180,7 +185,15 @@ export function DimensionEditor(props: DimensionEditorProps) { } let label: EuiListGroupItemProps['label'] = operationPanels[operationType].displayName; - if (disabledStatus) { + if (isActive && disabledStatus) { + label = ( + + + {operationPanels[operationType].displayName} + + + ); + } else if (disabledStatus) { label = ( {operationPanels[operationType].displayName} @@ -202,9 +215,12 @@ export function DimensionEditor(props: DimensionEditorProps) { compatibleWithCurrentField ? '' : ' incompatible' }`, onClick() { - if (operationDefinitionMap[operationType].input === 'none') { + if ( + operationDefinitionMap[operationType].input === 'none' || + operationDefinitionMap[operationType].input === 'fullReference' + ) { + // Clear invalid state because we are reseting to a valid column if (selectedColumn?.operationType === operationType) { - // Clear invalid state because we are reseting to a valid column if (incompleteInfo) { setStateWrapper(resetIncomplete(state.layers[layerId], columnId)); } @@ -291,6 +307,35 @@ export function DimensionEditor(props: DimensionEditorProps) {
    + {!incompleteInfo && + selectedColumn && + 'references' in selectedColumn && + selectedOperationDefinition?.input === 'fullReference' ? ( + <> + {selectedColumn.references.map((referenceId, index) => { + const validation = selectedOperationDefinition.requiredReferences[index]; + + return ( + { + setState(mergeLayer({ state, layerId, newLayer })); + }} + validation={validation} + currentIndexPattern={currentIndexPattern} + existingFields={state.existingFields} + selectionStyle={selectedOperationDefinition.selectionStyle} + dateRange={dateRange} + {...services} + /> + ); + })} + + + ) : null} + {!selectedColumn || selectedOperationDefinition?.input === 'field' || (incompleteOperation && operationDefinitionMap[incompleteOperation].input === 'field') ? ( @@ -325,7 +370,13 @@ export function DimensionEditor(props: DimensionEditorProps) { } incompleteOperation={incompleteOperation} onDeleteColumn={() => { - setStateWrapper(deleteColumn({ layer: state.layers[layerId], columnId })); + setStateWrapper( + deleteColumn({ + layer: state.layers[layerId], + columnId, + indexPattern: currentIndexPattern, + }) + ); }} onChoose={(choice) => { setStateWrapper( @@ -342,15 +393,6 @@ export function DimensionEditor(props: DimensionEditorProps) { ) : null} - {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ( - - )} - {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ParamEditor && ( <> )} + + {!currentFieldIsInvalid && !incompleteInfo && selectedColumn && ( + + )}
    @@ -432,11 +483,11 @@ export function DimensionEditor(props: DimensionEditorProps) { } function getErrorMessage( selectedColumn: IndexPatternColumn | undefined, - incompatibleSelectedOperationType: boolean, + incompleteOperation: boolean, input: 'none' | 'field' | 'fullReference' | undefined, fieldInvalid: boolean ) { - if (selectedColumn && incompatibleSelectedOperationType) { + if (selectedColumn && incompleteOperation) { if (input === 'field') { return i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { defaultMessage: 'To use this function, select a different field.', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 6bfeafd41c6b42..95a6c351e1fc23 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -854,6 +854,7 @@ describe('IndexPatternDimensionEditorPanel', () => { dataType: 'date', isBucketed: true, label: '', + customLabel: true, operationType: 'date_histogram', sourceField: 'ts', params: { @@ -872,6 +873,7 @@ describe('IndexPatternDimensionEditorPanel', () => { columnId: 'col2', }; } + it('should not show custom options if time scaling is not available', () => { wrapper = mount( { layers: { first: { ...state.layers.first, + columnOrder: ['col1', 'col2'], columns: { ...state.layers.first.columns, col2: expect.objectContaining({ - sourceField: 'bytes', operationType: 'avg', - // Other parts of this don't matter for this test + sourceField: 'bytes', }), }, - columnOrder: ['col1', 'col2'], + incompleteColumns: {}, }, }, }, @@ -1237,7 +1239,9 @@ describe('IndexPatternDimensionEditorPanel', () => { it('should indicate compatible fields when selecting the operation first', () => { wrapper = mount(); - wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + act(() => { + wrapper.find('button[data-test-subj="lns-indexPatternDimension-avg"]').simulate('click'); + }); const options = wrapper .find(EuiComboBox) @@ -1317,10 +1321,14 @@ describe('IndexPatternDimensionEditorPanel', () => { expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([ 'Average', 'Count', + 'Counter rate', + 'Cumulative sum', + 'Differences', 'Last value', 'Maximum', 'Median', 'Minimum', + 'Moving average', 'Sum', 'Unique count', ]); @@ -1536,4 +1544,101 @@ describe('IndexPatternDimensionEditorPanel', () => { }, }); }); + + it('should hide the top level field selector when switching from non-reference to reference', () => { + wrapper = mount(); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(0); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-derivative incompatible"]') + .simulate('click'); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(1); + }); + + it('should hide the reference editors when switching from reference to non-reference', () => { + const stateWithReferences: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Differences of (incomplete)', + dataType: 'number', + isBucketed: false, + operationType: 'derivative', + references: ['col2'], + params: {}, + }, + }); + + wrapper = mount( + + ); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(1); + + wrapper + .find('button[data-test-subj="lns-indexPatternDimension-avg incompatible"]') + .simulate('click'); + + expect(wrapper.find('ReferenceEditor')).toHaveLength(0); + }); + + it('should show a warning when the current dimension is no longer configurable', () => { + const stateWithInvalidCol: IndexPatternPrivateState = getStateWithColumns({ + col1: { + label: 'Invalid derivative', + dataType: 'number', + isBucketed: false, + operationType: 'derivative', + references: ['ref1'], + }, + }); + + wrapper = mount( + + ); + + expect( + wrapper + .find('[data-test-subj="lns-indexPatternDimension-derivative incompatible"]') + .find('EuiText[color="danger"]') + .first() + ).toBeTruthy(); + }); + + it('should remove options to select references when there are no time fields', () => { + const stateWithoutTime: IndexPatternPrivateState = { + ...getStateWithColumns({ + col1: { + label: 'Avg', + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + }), + indexPatterns: { + 1: { + id: '1', + title: 'my-fake-index-pattern', + hasRestrictions: false, + fields, + getFieldByName: getFieldByNameFactory([ + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + ]), + }, + }, + }; + + wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="lns-indexPatternDimension-derivative"]')).toHaveLength(0); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx index 406a32f62b2c7e..fbdf90e6cc4c79 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/field_select.tsx @@ -41,6 +41,7 @@ export interface FieldSelectProps extends EuiComboBoxProps<{}> { onDeleteColumn: () => void; existingFields: IndexPatternPrivateState['existingFields']; fieldIsInvalid: boolean; + markAllFieldsCompatible?: boolean; } export function FieldSelect({ @@ -53,6 +54,7 @@ export function FieldSelect({ onDeleteColumn, existingFields, fieldIsInvalid, + markAllFieldsCompatible, ...rest }: FieldSelectProps) { const { operationByField } = operationSupportMatrix; @@ -93,7 +95,7 @@ export function FieldSelect({ : operationByField[field]!.values().next().value, }, exists: containsData(field), - compatible: isCompatibleWithCurrentOperation(field), + compatible: markAllFieldsCompatible || isCompatibleWithCurrentOperation(field), }; }) .sort((a, b) => { @@ -163,6 +165,7 @@ export function FieldSelect({ currentIndexPattern, operationByField, existingFields, + markAllFieldsCompatible, ]); return ( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts index 817fdf637f0010..9d55a9d5f75229 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/operation_support.ts @@ -49,7 +49,7 @@ export const getOperationSupportMatrix = (props: Props): OperationSupportMatrix supportedFieldsByOperation[operation.operationType] = new Set(); } supportedFieldsByOperation[operation.operationType]?.add(operation.field); - } else if (operation.type === 'none') { + } else { supportedOperationsWithoutField.add(operation.operationType); } }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx new file mode 100644 index 00000000000000..0891dd27fcf172 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx @@ -0,0 +1,436 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ReactWrapper, ShallowWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { EuiComboBox } from '@elastic/eui'; +import { mountWithIntl as mount } from '@kbn/test/jest'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import { OperationMetadata } from '../../types'; +import { createMockedIndexPattern } from '../mocks'; +import { ReferenceEditor, ReferenceEditorProps } from './reference_editor'; +import { insertOrReplaceColumn } from '../operations'; +import { FieldSelect } from './field_select'; + +jest.mock('../operations'); + +describe('reference editor', () => { + let wrapper: ReactWrapper | ShallowWrapper; + let updateLayer: jest.Mock; + + function getDefaultArgs() { + return { + layer: { + indexPatternId: '1', + columns: {}, + columnOrder: [], + }, + columnId: 'ref', + updateLayer, + selectionStyle: 'full' as const, + currentIndexPattern: createMockedIndexPattern(), + existingFields: { + 'my-fake-index-pattern': { + timestamp: true, + bytes: true, + memory: true, + source: true, + }, + }, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + storage: {} as IStorageWrapper, + uiSettings: {} as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + http: {} as HttpSetup, + data: {} as DataPublicPluginStart, + }; + } + + beforeEach(() => { + updateLayer = jest.fn().mockImplementation((newLayer) => { + if (wrapper instanceof ReactWrapper) { + wrapper.setProps({ layer: newLayer }); + } + }); + + jest.clearAllMocks(); + }); + + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + } + }); + + it('should indicate that all functions and available fields are compatible in the empty state', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const functions = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]') + .prop('options'); + + expect(functions).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + + const fields = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); + + expect(fields![0].options).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + expect(fields![1].options).not.toContainEqual( + expect.objectContaining({ 'data-test-subj': expect.stringContaining('Incompatible') }) + ); + }); + + it('should indicate functions and fields that are incompatible with the current', () => { + wrapper = mount( + meta.isBucketed, + }} + /> + ); + + const functions = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]') + .prop('options'); + expect(functions.find(({ label }) => label === 'Date histogram')!['data-test-subj']).toContain( + 'incompatible' + ); + + const fields = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-dimension-field"]') + .prop('options'); + expect( + fields![0].options!.find(({ label }) => label === 'timestampLabel')!['data-test-subj'] + ).toContain('Incompatible'); + }); + + it('should not update when selecting the same operation', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Average')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + expect(insertOrReplaceColumn).not.toHaveBeenCalled(); + }); + + it('should keep the field when replacing an existing reference with a compatible function', () => { + wrapper = mount( + meta.dataType === 'number', + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Maximum')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + + expect(insertOrReplaceColumn).toHaveBeenCalledWith( + expect.objectContaining({ + op: 'max', + field: expect.objectContaining({ name: 'bytes' }), + }) + ); + }); + + it('should transition to another function with incompatible field', () => { + wrapper = mount( + true, + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + const option = comboBox.prop('options')!.find(({ label }) => label === 'Date histogram')!; + + act(() => { + comboBox.prop('onChange')!([option]); + }); + + expect(insertOrReplaceColumn).toHaveBeenCalledWith( + expect.objectContaining({ + op: 'date_histogram', + field: undefined, + }) + ); + }); + + it('should hide the function selector when using a field-only selection style', () => { + wrapper = mount( + true, + }} + /> + ); + + const comboBox = wrapper + .find(EuiComboBox) + .filter('[data-test-subj="indexPattern-reference-function"]'); + expect(comboBox).toHaveLength(0); + }); + + it('should pass the incomplete operation info to FieldSelect', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(true); + expect(fieldSelect.prop('selectedField')).toEqual('bytes'); + expect(fieldSelect.prop('selectedOperationType')).toEqual('avg'); + expect(fieldSelect.prop('incompleteOperation')).toEqual('max'); + expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(false); + }); + + it('should pass the incomplete field info to FieldSelect', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(false); + expect(fieldSelect.prop('selectedField')).toEqual('timestamp'); + expect(fieldSelect.prop('selectedOperationType')).toEqual('avg'); + expect(fieldSelect.prop('incompleteOperation')).toBeUndefined(); + }); + + it('should show the FieldSelect as invalid in the empty state for field-only forms', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(true); + expect(fieldSelect.prop('selectedField')).toBeUndefined(); + expect(fieldSelect.prop('selectedOperationType')).toBeUndefined(); + expect(fieldSelect.prop('incompleteOperation')).toBeUndefined(); + expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(true); + }); + + it('should show the ParamEditor for functions that offer one', () => { + wrapper = mount( + true, + }} + /> + ); + + expect(wrapper.find('[data-test-subj="lns-indexPattern-lastValue-sortField"]').exists()).toBe( + true + ); + }); + + it('should hide the ParamEditor for incomplete functions', () => { + wrapper = mount( + true, + }} + /> + ); + + expect(wrapper.find('[data-test-subj="lns-indexPattern-lastValue-sortField"]').exists()).toBe( + false + ); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx new file mode 100644 index 00000000000000..d73530ec8a9204 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx @@ -0,0 +1,306 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import './dimension_editor.scss'; +import _ from 'lodash'; +import React, { useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFormRow, EuiSpacer, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { DateRange } from '../../../common'; +import type { OperationSupportMatrix } from './operation_support'; +import type { OperationType } from '../indexpattern'; +import { + operationDefinitionMap, + getOperationDisplay, + insertOrReplaceColumn, + deleteColumn, + isOperationAllowedAsReference, + FieldBasedIndexPatternColumn, + RequiredReference, +} from '../operations'; +import { FieldSelect } from './field_select'; +import { hasField } from '../utils'; +import type { IndexPattern, IndexPatternLayer, IndexPatternPrivateState } from '../types'; +import { trackUiEvent } from '../../lens_ui_telemetry'; + +const operationPanels = getOperationDisplay(); + +export interface ReferenceEditorProps { + layer: IndexPatternLayer; + selectionStyle: 'full' | 'field'; + validation: RequiredReference; + columnId: string; + updateLayer: (newLayer: IndexPatternLayer) => void; + currentIndexPattern: IndexPattern; + existingFields: IndexPatternPrivateState['existingFields']; + dateRange: DateRange; + + // Services + uiSettings: IUiSettingsClient; + storage: IStorageWrapper; + savedObjectsClient: SavedObjectsClientContract; + http: HttpSetup; + data: DataPublicPluginStart; +} + +export function ReferenceEditor(props: ReferenceEditorProps) { + const { + layer, + columnId, + updateLayer, + currentIndexPattern, + existingFields, + validation, + selectionStyle, + dateRange, + ...services + } = props; + + const column = layer.columns[columnId]; + const selectedOperationDefinition = column && operationDefinitionMap[column.operationType]; + + const ParamEditor = selectedOperationDefinition?.paramEditor; + + const incompleteInfo = layer.incompleteColumns ? layer.incompleteColumns[columnId] : undefined; + const incompleteOperation = incompleteInfo?.operationType; + const incompleteField = incompleteInfo?.sourceField ?? null; + + // Basically the operation support matrix, but different validation + const operationSupportMatrix: OperationSupportMatrix & { + operationTypes: Set; + } = useMemo(() => { + const operationTypes: Set = new Set(); + const operationWithoutField: Set = new Set(); + const operationByField: Partial>> = {}; + const fieldByOperation: Partial>> = {}; + Object.values(operationDefinitionMap) + .sort((op1, op2) => { + return op1.displayName.localeCompare(op2.displayName); + }) + .forEach((op) => { + if (op.input === 'field') { + const allFields = currentIndexPattern.fields.filter((field) => + isOperationAllowedAsReference({ + operationType: op.type, + validation, + field, + indexPattern: currentIndexPattern, + }) + ); + if (allFields.length) { + operationTypes.add(op.type); + fieldByOperation[op.type] = new Set(allFields.map(({ name }) => name)); + allFields.forEach((field) => { + if (!operationByField[field.name]) { + operationByField[field.name] = new Set(); + } + operationByField[field.name]?.add(op.type); + }); + } + } else if ( + isOperationAllowedAsReference({ + operationType: op.type, + validation, + indexPattern: currentIndexPattern, + }) + ) { + operationTypes.add(op.type); + operationWithoutField.add(op.type); + } + }); + return { + operationTypes, + operationWithoutField, + operationByField, + fieldByOperation, + }; + }, [currentIndexPattern, validation]); + + const functionOptions: Array> = Array.from( + operationSupportMatrix.operationTypes + ).map((operationType) => { + const def = operationDefinitionMap[operationType]; + const label = operationPanels[operationType].displayName; + const isCompatible = + !column || + (column && + hasField(column) && + def.input === 'field' && + operationSupportMatrix.fieldByOperation[operationType]?.has(column.sourceField)) || + (column && !hasField(column) && def.input !== 'field'); + + return { + label, + value: operationType, + className: 'lnsIndexPatternDimensionEditor__operation', + 'data-test-subj': `lns-indexPatternDimension-${operationType}${ + isCompatible ? '' : ' incompatible' + }`, + }; + }); + + function onChooseFunction(operationType: OperationType) { + if (column?.operationType === operationType) { + return; + } + const possibleFieldNames = operationSupportMatrix.fieldByOperation[operationType]; + if (column && 'sourceField' in column && possibleFieldNames?.has(column.sourceField)) { + // Reuse the current field if possible + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + op: operationType, + indexPattern: currentIndexPattern, + field: currentIndexPattern.getFieldByName(column.sourceField), + }) + ); + } else { + // If reusing the field is impossible, we generally can't choose for the user. + // The one exception is if the field is the only possible field, like Count of Records. + const possibleField = + possibleFieldNames?.size === 1 + ? currentIndexPattern.getFieldByName(possibleFieldNames.values().next().value) + : undefined; + + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + op: operationType, + indexPattern: currentIndexPattern, + field: possibleField, + }) + ); + } + trackUiEvent(`indexpattern_dimension_operation_${operationType}`); + return; + } + + const selectedOption = incompleteInfo?.operationType + ? [functionOptions.find(({ value }) => value === incompleteInfo.operationType)!] + : column + ? [functionOptions.find(({ value }) => value === column.operationType)!] + : []; + + // If the operationType is incomplete, the user needs to select a field- so + // the function is marked as valid. + const showOperationInvalid = !column && !Boolean(incompleteInfo?.operationType); + // The field is invalid if the operation has been updated without a field, + // or if we are in a field-only mode but empty state + const showFieldInvalid = + Boolean(incompleteInfo?.operationType) || (selectionStyle === 'field' && !column); + + return ( +
    +
    + {selectionStyle !== 'field' ? ( + <> + + { + if (choices.length === 0) { + updateLayer( + deleteColumn({ layer, columnId, indexPattern: currentIndexPattern }) + ); + return; + } + + trackUiEvent('indexpattern_dimension_field_changed'); + + onChooseFunction(choices[0].value!); + }} + /> + + + + ) : null} + + {!column || selectedOperationDefinition.input === 'field' ? ( + + { + updateLayer(deleteColumn({ layer, columnId, indexPattern: currentIndexPattern })); + }} + onChoose={(choice) => { + updateLayer( + insertOrReplaceColumn({ + layer, + columnId, + indexPattern: currentIndexPattern, + op: choice.operationType, + field: currentIndexPattern.getFieldByName(choice.field), + }) + ); + }} + /> + + ) : null} + + {column && !incompleteInfo && ParamEditor && ( + <> + + + )} +
    +
    + ); +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 2e55abf4a429ad..7e67d863346c79 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -858,165 +858,49 @@ describe('IndexPattern Data Source', () => { it('should return null for non-existant columns', () => { expect(publicAPI.getOperationForColumnId('col2')).toBe(null); }); - }); - }); - describe('#getErrorMessages', () => { - it('should detect a missing reference in a layer', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(1); - expect(messages![0]).toEqual({ - shortMessage: 'Invalid reference.', - longMessage: '"Foo" has an invalid reference.', - }); - }); - - it('should detect and batch missing references in a layer', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - col2: { - dataType: 'number', - isBucketed: false, - label: 'Foo2', - operationType: 'count', // <= invalid - sourceField: 'memory', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(1); - expect(messages![0]).toEqual({ - shortMessage: 'Invalid references.', - longMessage: '"Foo", "Foo2" have invalid reference.', - }); - }); + it('should return null for referenced columns', () => { + publicAPI = indexPatternDatasource.getPublicAPI({ + state: { + ...enrichBaseState(baseState), + layers: { + first: { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Sum', + dataType: 'number', + isBucketed: false, - it('should detect and batch missing references in multiple layers', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1', 'col2'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'bytes', - }, - col2: { - dataType: 'number', - isBucketed: false, - label: 'Foo2', - operationType: 'count', // <= invalid - sourceField: 'memory', - }, - }, - }, - second: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'string', - isBucketed: false, - label: 'Foo', - operationType: 'count', // <= invalid - sourceField: 'source', - }, - }, - }, - }, - currentIndexPatternId: '1', - }; - const messages = indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState); - expect(messages).toHaveLength(2); - expect(messages).toEqual([ - { - shortMessage: 'Invalid references on Layer 1.', - longMessage: 'Layer 1 has invalid references in "Foo", "Foo2".', - }, - { - shortMessage: 'Invalid reference on Layer 2.', - longMessage: 'Layer 2 has an invalid reference in "Foo".', - }, - ]); - }); + operationType: 'sum', + sourceField: 'test', + params: {}, + } as IndexPatternColumn, + col2: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, - it('should return no errors if all references are satified', () => { - const state = { - indexPatternRefs: [], - existingFields: {}, - isFirstExistenceFetch: false, - indexPatterns: expectedIndexPatterns, - layers: { - first: { - indexPatternId: '1', - columnOrder: ['col1'], - columns: { - col1: { - dataType: 'number', - isBucketed: false, - label: 'Foo', - operationType: 'avg', - sourceField: 'bytes', + operationType: 'cumulative_sum', + references: ['col1'], + params: {}, + } as IndexPatternColumn, + }, }, }, }, - }, - currentIndexPatternId: '1', - }; - expect( - indexPatternDatasource.getErrorMessages(state as IndexPatternPrivateState) - ).toBeUndefined(); + layerId: 'first', + }); + expect(publicAPI.getOperationForColumnId('col1')).toEqual(null); + }); }); + }); - it('should return no errors with layers with no columns', () => { + describe('#getErrorMessages', () => { + it('should use the results of getErrorMessages directly when single layer', () => { + (getErrorMessages as jest.Mock).mockClear(); + (getErrorMessages as jest.Mock).mockReturnValueOnce(['error 1', 'error 2']); const state: IndexPatternPrivateState = { indexPatternRefs: [], existingFields: {}, @@ -1031,10 +915,14 @@ describe('IndexPattern Data Source', () => { }, currentIndexPatternId: '1', }; - expect(indexPatternDatasource.getErrorMessages(state)).toBeUndefined(); + expect(indexPatternDatasource.getErrorMessages(state)).toEqual([ + { longMessage: 'error 1', shortMessage: '' }, + { longMessage: 'error 2', shortMessage: '' }, + ]); + expect(getErrorMessages).toHaveBeenCalledTimes(1); }); - it('should bubble up invalid configuration from operations', () => { + it('should prepend each error with its layer number on multi-layer chart', () => { (getErrorMessages as jest.Mock).mockClear(); (getErrorMessages as jest.Mock).mockReturnValueOnce(['error 1', 'error 2']); const state: IndexPatternPrivateState = { @@ -1048,14 +936,19 @@ describe('IndexPattern Data Source', () => { columnOrder: [], columns: {}, }, + second: { + indexPatternId: '1', + columnOrder: [], + columns: {}, + }, }, currentIndexPatternId: '1', }; expect(indexPatternDatasource.getErrorMessages(state)).toEqual([ - { shortMessage: 'error 1', longMessage: '' }, - { shortMessage: 'error 2', longMessage: '' }, + { longMessage: 'Layer 1 error: error 1', shortMessage: '' }, + { longMessage: 'Layer 1 error: error 2', shortMessage: '' }, ]); - expect(getErrorMessages).toHaveBeenCalledTimes(1); + expect(getErrorMessages).toHaveBeenCalledTimes(2); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 2937b1cf057603..6c6bd2e1bb4391 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -39,12 +39,7 @@ import { getDatasourceSuggestionsForVisualizeField, } from './indexpattern_suggestions'; -import { - getInvalidColumnsForLayer, - getInvalidLayers, - isDraggedField, - normalizeOperationDataType, -} from './utils'; +import { isDraggedField, normalizeOperationDataType } from './utils'; import { LayerPanel } from './layerpanel'; import { IndexPatternColumn, getErrorMessages, IncompleteColumn } from './operations'; import { IndexPatternField, IndexPatternPrivateState, IndexPatternPersistedState } from './types'; @@ -55,7 +50,6 @@ import { mergeLayer } from './state_helpers'; import { Datasource, StateSetter } from '../index'; import { ChartsPluginSetup } from '../../../../../src/plugins/charts/public'; import { deleteColumn, isReferenced } from './operations'; -import { FieldBasedIndexPatternColumn } from './operations/definitions/column_types'; import { Dragging } from '../drag_drop/providers'; export { OperationType, IndexPatternColumn, deleteColumn } from './operations'; @@ -162,10 +156,11 @@ export function getIndexPatternDatasource({ }, removeColumn({ prevState, layerId, columnId }) { + const indexPattern = prevState.indexPatterns[prevState.layers[layerId]?.indexPatternId]; return mergeLayer({ state: prevState, layerId, - newLayer: deleteColumn({ layer: prevState.layers[layerId], columnId }), + newLayer: deleteColumn({ layer: prevState.layers[layerId], columnId, indexPattern }), }); }, @@ -351,7 +346,9 @@ export function getIndexPatternDatasource({ const layer = state.layers[layerId]; if (layer && layer.columns[columnId]) { - return columnToOperation(layer.columns[columnId], columnLabelMap[columnId]); + if (!isReferenced(layer, columnId)) { + return columnToOperation(layer.columns[columnId], columnLabelMap[columnId]); + } } return null; }, @@ -369,91 +366,46 @@ export function getIndexPatternDatasource({ if (!state) { return; } - const invalidLayers = getInvalidLayers(state); - const layerErrors = Object.values(state.layers).flatMap((layer) => + const layerErrors = Object.values(state.layers).map((layer) => (getErrorMessages(layer) ?? []).map((message) => ({ - shortMessage: message, - longMessage: '', + shortMessage: '', // Not displayed currently + longMessage: message, })) ); - if (invalidLayers.length === 0) { - return layerErrors.length ? layerErrors : undefined; + // Single layer case, no need to explain more + if (layerErrors.length <= 1) { + return layerErrors[0]?.length ? layerErrors[0] : undefined; } - const realIndex = Object.values(state.layers) - .map((layer, i) => { - const filteredIndex = invalidLayers.indexOf(layer); - if (filteredIndex > -1) { - return [filteredIndex, i + 1]; - } - }) - .filter(Boolean) as Array<[number, number]>; - const invalidColumnsForLayer: string[][] = getInvalidColumnsForLayer( - invalidLayers, - state.indexPatterns - ); - const originalLayersList = Object.keys(state.layers); - - if (layerErrors.length || realIndex.length) { - return [ - ...layerErrors, - ...realIndex.map(([filteredIndex, layerIndex]) => { - const columnLabelsWithBrokenReferences: string[] = invalidColumnsForLayer[ - filteredIndex - ].map((columnId) => { - const column = invalidLayers[filteredIndex].columns[ - columnId - ] as FieldBasedIndexPatternColumn; - return column.label; - }); - - if (originalLayersList.length === 1) { - return { - shortMessage: i18n.translate( - 'xpack.lens.indexPattern.dataReferenceFailureShortSingleLayer', - { - defaultMessage: - 'Invalid {columns, plural, one {reference} other {references}}.', - values: { - columns: columnLabelsWithBrokenReferences.length, - }, - } - ), - longMessage: i18n.translate( - 'xpack.lens.indexPattern.dataReferenceFailureLongSingleLayer', - { - defaultMessage: `"{columns}" {columnsLength, plural, one {has an} other {have}} invalid reference.`, - values: { - columns: columnLabelsWithBrokenReferences.join('", "'), - columnsLength: columnLabelsWithBrokenReferences.length, - }, - } - ), - }; - } - return { - shortMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureShort', { - defaultMessage: - 'Invalid {columnsLength, plural, one {reference} other {references}} on Layer {layer}.', - values: { - layer: layerIndex, - columnsLength: columnLabelsWithBrokenReferences.length, - }, - }), - longMessage: i18n.translate('xpack.lens.indexPattern.dataReferenceFailureLong', { - defaultMessage: `Layer {layer} has {columnsLength, plural, one {an invalid} other {invalid}} {columnsLength, plural, one {reference} other {references}} in "{columns}".`, - values: { - layer: layerIndex, - columns: columnLabelsWithBrokenReferences.join('", "'), - columnsLength: columnLabelsWithBrokenReferences.length, - }, - }), - }; - }), - ]; - } + // For multiple layers we will prepend each error with the layer number + const messages = layerErrors.flatMap((errors, index) => { + return errors.map((error) => { + const { shortMessage, longMessage } = error; + return { + shortMessage: shortMessage + ? i18n.translate('xpack.lens.indexPattern.layerErrorWrapper', { + defaultMessage: 'Layer {position} error: {wrappedMessage}', + values: { + position: index + 1, + wrappedMessage: shortMessage, + }, + }) + : '', + longMessage: longMessage + ? i18n.translate('xpack.lens.indexPattern.layerErrorWrapper', { + defaultMessage: 'Layer {position} error: {wrappedMessage}', + values: { + position: index + 1, + wrappedMessage: longMessage, + }, + }) + : '', + }; + }); + }); + return messages.length ? messages : undefined; }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 9fbad553d441a4..de768e92efb3d0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -6,11 +6,12 @@ import { DatasourceSuggestion } from '../types'; import { generateId } from '../id_generator'; -import { IndexPatternPrivateState } from './types'; +import type { IndexPatternPrivateState } from './types'; import { getDatasourceSuggestionsForField, getDatasourceSuggestionsFromCurrentState, getDatasourceSuggestionsForVisualizeField, + IndexPatternSuggestion, } from './indexpattern_suggestions'; import { documentField } from './document_field'; import { getFieldByNameFactory } from './pure_helpers'; @@ -153,6 +154,7 @@ function testInitialState(): IndexPatternPrivateState { columns: { col1: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -172,6 +174,19 @@ function testInitialState(): IndexPatternPrivateState { }; } +// Simplifies the debug output for failed test +function getSuggestionSubset( + suggestions: IndexPatternSuggestion[] +): Array> { + return suggestions.map((s) => { + const newSuggestion = { ...s } as Omit & { + state?: IndexPatternPrivateState; + }; + delete newSuggestion.state; + return newSuggestion; + }); +} + describe('IndexPattern Data Source suggestions', () => { beforeEach(async () => { let count = 0; @@ -698,6 +713,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, sourceField: 'source', label: 'values of source', + customLabel: true, operationType: 'terms', params: { orderBy: { type: 'column', columnId: 'colb' }, @@ -710,6 +726,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, sourceField: 'bytes', label: 'Avg of bytes', + customLabel: true, operationType: 'avg', }, }, @@ -733,7 +750,7 @@ describe('IndexPattern Data Source suggestions', () => { dataType: 'date', isBucketed: true, sourceField: 'timestamp', - label: 'date histogram of timestamp', + label: 'timestamp', operationType: 'date_histogram', params: { interval: 'w', @@ -744,6 +761,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, sourceField: 'bytes', label: 'Avg of bytes', + customLabel: true, operationType: 'avg', }, }, @@ -782,6 +800,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('puts a date histogram column after the last bucket column on date field', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'timestamp', @@ -790,17 +809,16 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ layers: { previousLayer: initialState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1', 'colb'], + columnOrder: ['cola', 'newid', 'colb'], columns: { ...initialState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'date_histogram', sourceField: 'timestamp', }), @@ -817,7 +835,7 @@ describe('IndexPattern Data Source suggestions', () => { columnId: 'cola', }), expect.objectContaining({ - columnId: 'id1', + columnId: 'newid', }), expect.objectContaining({ columnId: 'colb', @@ -845,6 +863,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('appends a terms column with default size on string field', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'dest', @@ -853,17 +872,16 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ layers: { previousLayer: initialState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1', 'colb'], + columnOrder: ['cola', 'newid', 'colb'], columns: { ...initialState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'terms', sourceField: 'dest', params: expect.objectContaining({ size: 3 }), @@ -877,6 +895,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('suggests both replacing and adding metric if only one other metric is set', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const suggestions = getDatasourceSuggestionsForField(initialState, '1', { name: 'memory', @@ -885,7 +904,6 @@ describe('IndexPattern Data Source suggestions', () => { aggregatable: true, searchable: true, }); - expect(suggestions).toContainEqual( expect.objectContaining({ state: expect.objectContaining({ @@ -910,11 +928,11 @@ describe('IndexPattern Data Source suggestions', () => { state: expect.objectContaining({ layers: expect.objectContaining({ currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'colb', 'id1'], + columnOrder: ['cola', 'colb', 'newid'], columns: { cola: initialState.layers.currentLayer.columns.cola, colb: initialState.layers.currentLayer.columns.colb, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'avg', sourceField: 'memory', }), @@ -927,6 +945,7 @@ describe('IndexPattern Data Source suggestions', () => { }); it('adds a metric column on a number field if no other metrics set', () => { + (generateId as jest.Mock).mockReturnValue('newid'); const initialState = stateWithNonEmptyTables(); const modifiedState: IndexPatternPrivateState = { ...initialState, @@ -955,10 +974,10 @@ describe('IndexPattern Data Source suggestions', () => { layers: { previousLayer: modifiedState.layers.previousLayer, currentLayer: expect.objectContaining({ - columnOrder: ['cola', 'id1'], + columnOrder: ['cola', 'newid'], columns: { ...modifiedState.layers.currentLayer.columns, - id1: expect.objectContaining({ + newid: expect.objectContaining({ operationType: 'avg', sourceField: 'memory', }), @@ -1008,6 +1027,137 @@ describe('IndexPattern Data Source suggestions', () => { const suggestions = getDatasourceSuggestionsForField(modifiedState, '1', documentField); expect(suggestions).not.toContain(expect.objectContaining({ changeType: 'extended' })); }); + + it('hides any referenced metrics when adding new metrics', () => { + (generateId as jest.Mock).mockReturnValue('newid'); + const initialState = stateWithNonEmptyTables(); + const modifiedState: IndexPatternPrivateState = { + ...initialState, + layers: { + currentLayer: { + indexPatternId: '1', + columnOrder: ['date', 'metric', 'ref'], + columns: { + date: { + label: '', + customLabel: true, + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + metric: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + ref: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + const suggestions = getSuggestionSubset( + getDatasourceSuggestionsForField(modifiedState, '1', documentField) + ); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + isMultiRow: true, + changeType: 'extended', + label: undefined, + layerId: 'currentLayer', + columns: [ + { + columnId: 'date', + operation: expect.objectContaining({ dataType: 'date', isBucketed: true }), + }, + { + columnId: 'newid', + operation: expect.objectContaining({ dataType: 'number', isBucketed: false }), + }, + { + columnId: 'ref', + operation: expect.objectContaining({ dataType: 'number', isBucketed: false }), + }, + ], + }), + keptLayerIds: ['currentLayer'], + }) + ); + }); + + it('makes a suggestion to extending from an invalid state with a new metric', () => { + (generateId as jest.Mock).mockReturnValue('newid'); + const initialState = stateWithNonEmptyTables(); + const modifiedState: IndexPatternPrivateState = { + ...initialState, + layers: { + currentLayer: { + indexPatternId: '1', + columnOrder: ['metric', 'ref'], + columns: { + metric: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'avg', + sourceField: 'bytes', + }, + ref: { + label: '', + customLabel: true, + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + const suggestions = getSuggestionSubset( + getDatasourceSuggestionsForField(modifiedState, '1', documentField) + ); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'extended', + columns: [ + { + columnId: 'newid', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Count of records', + scale: 'ratio', + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + ], + }), + }) + ); + }); }); describe('finding the layer that is using the current index pattern', () => { @@ -1121,6 +1271,7 @@ describe('IndexPattern Data Source suggestions', () => { }); }); }); + describe('#getDatasourceSuggestionsForVisualizeField', () => { describe('with no layer', () => { function stateWithoutLayer() { @@ -1218,6 +1369,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Op 2', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1305,6 +1457,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1316,7 +1469,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { isMultiRow: true, @@ -1359,6 +1512,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { cola: { label: 'My Terms', + customLabel: true, dataType: 'string', isBucketed: true, operationType: 'terms', @@ -1372,6 +1526,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1383,7 +1538,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { isMultiRow: true, @@ -1442,6 +1597,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: true, operationType: 'range', @@ -1487,6 +1643,7 @@ describe('IndexPattern Data Source suggestions', () => { }, colb: { label: 'My Custom Range', + customLabel: true, dataType: 'string', isBucketed: true, operationType: 'range', @@ -1503,7 +1660,7 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - expect(getDatasourceSuggestionsFromCurrentState(state)).toContainEqual( + expect(getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state))).toContainEqual( expect.objectContaining({ table: { changeType: 'extended', @@ -1555,6 +1712,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { id1: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, operationType: 'avg', @@ -1631,6 +1789,7 @@ describe('IndexPattern Data Source suggestions', () => { columns: { col1: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1644,6 +1803,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col2: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1657,6 +1817,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col3: { label: 'My Op', + customLabel: true, dataType: 'string', isBucketed: true, @@ -1670,6 +1831,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col4: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, @@ -1678,6 +1840,7 @@ describe('IndexPattern Data Source suggestions', () => { }, col5: { label: 'My Op', + customLabel: true, dataType: 'number', isBucketed: false, @@ -1691,31 +1854,26 @@ describe('IndexPattern Data Source suggestions', () => { }; const suggestions = getDatasourceSuggestionsFromCurrentState(state); - // 1 bucket col, 2 metric cols - isTableWithBucketColumns(suggestions[0], ['col1', 'col4', 'col5'], 1); + + // 3 bucket cols, 2 metric cols + isTableWithBucketColumns(suggestions[0], ['col1', 'col2', 'col3', 'col4', 'col5'], 3); // 1 bucket col, 1 metric col isTableWithBucketColumns(suggestions[1], ['col1', 'col4'], 1); // 2 bucket cols, 2 metric cols - isTableWithBucketColumns(suggestions[2], ['col1', 'col2', 'col4', 'col5'], 2); - - // 2 bucket cols, 1 metric col - isTableWithBucketColumns(suggestions[3], ['col1', 'col2', 'col4'], 2); - - // 3 bucket cols, 2 metric cols - isTableWithBucketColumns(suggestions[4], ['col1', 'col2', 'col3', 'col4', 'col5'], 3); + isTableWithBucketColumns(suggestions[2], ['col1', 'col2', 'col4'], 2); // 3 bucket cols, 1 metric col - isTableWithBucketColumns(suggestions[5], ['col1', 'col2', 'col3', 'col4'], 3); + isTableWithBucketColumns(suggestions[3], ['col1', 'col2', 'col3', 'col4'], 3); // first metric col - isTableWithMetricColumns(suggestions[6], ['col4']); + isTableWithMetricColumns(suggestions[4], ['col4']); // second metric col - isTableWithMetricColumns(suggestions[7], ['col5']); + isTableWithMetricColumns(suggestions[5], ['col5']); - expect(suggestions.length).toBe(8); + expect(suggestions.length).toBe(6); }); it('returns an only metric version of a given table', () => { @@ -1770,7 +1928,7 @@ describe('IndexPattern Data Source suggestions', () => { ...initialState.layers.first, columns: { id1: { - label: 'Date histogram', + label: 'field2', dataType: 'date', isBucketed: true, @@ -1794,8 +1952,19 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions[1].table.columns[0].operation.label).toBe('Average of field1'); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Average of field1' }), + }), + ], + }), + }) + ); }); it('returns an alternative metric for an only-metric table', () => { @@ -1848,9 +2017,18 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions[0].table.columns.length).toBe(1); - expect(suggestions[0].table.columns[0].operation.label).toBe('Sum of field1'); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + columns: [ + expect.objectContaining({ + operation: expect.objectContaining({ label: 'Sum of field1' }), + }), + ], + }), + }) + ); }); it('contains a reordering suggestion when there are exactly 2 buckets', () => { @@ -1909,7 +2087,7 @@ describe('IndexPattern Data Source suggestions', () => { ); }); - it('does not generate suggestions if invalid fields are referenced', () => { + it('will generate suggestions even if there are errors from missing fields', () => { const initialState = testInitialState(); const state: IndexPatternPrivateState = { indexPatternRefs: [], @@ -1937,8 +2115,259 @@ describe('IndexPattern Data Source suggestions', () => { }, }; - const suggestions = getDatasourceSuggestionsFromCurrentState(state); - expect(suggestions).toEqual([]); + const suggestions = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: { + changeType: 'unchanged', + columns: [ + { + columnId: 'col1', + operation: { + dataType: 'string', + isBucketed: true, + label: 'My Op', + scale: undefined, + }, + }, + { + columnId: 'col2', + operation: { + dataType: 'string', + isBucketed: true, + label: 'Top 5', + scale: undefined, + }, + }, + ], + isMultiRow: true, + label: undefined, + layerId: 'first', + }, + }) + ); + }); + + describe('references', () => { + it('will extend the table with a date when starting in an invalid state', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['metric', 'ref', 'ref2'], + columns: { + metric: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric2'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'extended', + layerId: 'first', + columns: [ + { + columnId: 'id1', + operation: { + dataType: 'date', + isBucketed: true, + label: 'timestampLabel', + scale: 'interval', + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Cumulative sum of Records', + scale: undefined, + }, + }, + { + columnId: 'ref2', + operation: { + dataType: 'number', + isBucketed: false, + label: 'Cumulative sum of (incomplete)', + scale: undefined, + }, + }, + ], + }), + keptLayerIds: ['first'], + }) + ); + }); + + it('will make an unchanged suggestion including incomplete references', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['date', 'ref', 'ref2'], + columns: { + date: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'unchanged', + layerId: 'first', + columns: [ + { + columnId: 'date', + operation: { + dataType: 'date', + isBucketed: true, + label: '', + scale: undefined, + }, + }, + { + columnId: 'ref', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + { + columnId: 'ref2', + operation: { + dataType: 'number', + isBucketed: false, + label: '', + scale: undefined, + }, + }, + ], + }), + keptLayerIds: ['first'], + }) + ); + }); + + it('will skip a reduced suggestion when handling multiple references', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + ...initialState, + layers: { + ...initialState.layers, + first: { + ...initialState.layers.first, + columnOrder: ['date', 'metric', 'metric2', 'ref', 'ref2'], + + columns: { + date: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + metric: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric'], + }, + metric2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + ref2: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['metric2'], + }, + }, + }, + }, + }; + + const result = getSuggestionSubset(getDatasourceSuggestionsFromCurrentState(state)); + + expect(result).not.toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reduced', + }), + }) + ); + }); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index ebac396210a5cb..969324c67e9094 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _, { partition } from 'lodash'; +import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import { generateId } from '../id_generator'; import { DatasourceSuggestion, TableChangeType } from '../types'; @@ -17,8 +17,10 @@ import { operationDefinitionMap, IndexPatternColumn, OperationType, + getExistingColumnGroups, + isReferenced, } from './operations'; -import { hasField, hasInvalidColumns } from './utils'; +import { hasField } from './utils'; import { IndexPattern, IndexPatternPrivateState, @@ -27,7 +29,7 @@ import { } from './types'; import { documentField } from './document_field'; -type IndexPatternSugestion = DatasourceSuggestion; +export type IndexPatternSuggestion = DatasourceSuggestion; function buildSuggestion({ state, @@ -71,10 +73,13 @@ function buildSuggestion({ }, table: { - columns: columnOrder.map((columnId) => ({ - columnId, - operation: columnToOperation(columnMap[columnId]), - })), + columns: columnOrder + // Hide any referenced columns from what visualizations know about + .filter((columnId) => !isReferenced(layers[layerId]!, columnId)) + .map((columnId) => ({ + columnId, + operation: columnToOperation(columnMap[columnId]), + })), isMultiRow, layerId, changeType, @@ -89,8 +94,7 @@ export function getDatasourceSuggestionsForField( state: IndexPatternPrivateState, indexPatternId: string, field: IndexPatternField -): IndexPatternSugestion[] { - if (hasInvalidColumns(state)) return []; +): IndexPatternSuggestion[] { const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); @@ -123,7 +127,7 @@ export function getDatasourceSuggestionsForVisualizeField( state: IndexPatternPrivateState, indexPatternId: string, fieldName: string -): IndexPatternSugestion[] { +): IndexPatternSuggestion[] { const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); // Identify the field by the indexPatternId and the fieldName @@ -158,7 +162,7 @@ function getExistingLayerSuggestionsForField( const fieldInUse = Object.values(layer.columns).some( (column) => hasField(column) && column.sourceField === field.name ); - const suggestions: IndexPatternSugestion[] = []; + const suggestions: IndexPatternSuggestion[] = []; if (usableAsBucketOperation && !fieldInUse) { if ( @@ -221,8 +225,9 @@ function getExistingLayerSuggestionsForField( ); } - const [, metrics] = separateBucketColumns(layer); - if (metrics.length === 1) { + const [, metrics, references] = getExistingColumnGroups(layer); + // TODO: Write test for the case where we have exactly one metric and one reference. We shouldn't switch the inner metric. + if (metrics.length === 1 && references.length === 0) { const layerWithReplacedMetric = replaceColumn({ layer, indexPattern, @@ -257,7 +262,7 @@ function getEmptyLayerSuggestionsForField( layerId: string, indexPatternId: string, field: IndexPatternField -): IndexPatternSugestion[] { +): IndexPatternSuggestion[] { const indexPattern = state.indexPatterns[indexPatternId]; let newLayer: IndexPatternLayer | undefined; const bucketOperation = getBucketOperation(field); @@ -331,7 +336,6 @@ function createNewLayerWithMetricAggregation( export function getDatasourceSuggestionsFromCurrentState( state: IndexPatternPrivateState ): Array> { - if (hasInvalidColumns(state)) return []; const layers = Object.entries(state.layers || {}); if (layers.length > 1) { // Return suggestions that reduce the data to each layer individually @@ -372,12 +376,13 @@ export function getDatasourceSuggestionsFromCurrentState( }), ]); } + return _.flatten( Object.entries(state.layers || {}) .filter(([_id, layer]) => layer.columnOrder.length && layer.indexPatternId) .map(([layerId, layer]) => { const indexPattern = state.indexPatterns[layer.indexPatternId]; - const [buckets, metrics] = separateBucketColumns(layer); + const [buckets, metrics, references] = getExistingColumnGroups(layer); const timeDimension = layer.columnOrder.find( (columnId) => layer.columns[columnId].isBucketed && layer.columns[columnId].dataType === 'date' @@ -390,29 +395,22 @@ export function getDatasourceSuggestionsFromCurrentState( buckets.some((columnId) => layer.columns[columnId].dataType === 'number'); const suggestions: Array> = []; - if (metrics.length === 0) { - // intermediary chart without metric, don't try to suggest reduced versions - suggestions.push( - buildSuggestion({ - state, - layerId, - changeType: 'unchanged', - }) - ); - } else if (buckets.length === 0) { + + // Always suggest an unchanged table, including during invalid states + suggestions.push( + buildSuggestion({ + state, + layerId, + changeType: 'unchanged', + }) + ); + + if (!references.length && metrics.length && buckets.length === 0) { if (timeField) { // suggest current metric over time if there is a default time field suggestions.push(createSuggestionWithDefaultDateHistogram(state, layerId, timeField)); } suggestions.push(...createAlternativeMetricSuggestions(indexPattern, layerId, state)); - // also suggest simple current state - suggestions.push( - buildSuggestion({ - state, - layerId, - changeType: 'unchanged', - }) - ); } else { suggestions.push(...createSimplifiedTableSuggestions(state, layerId)); @@ -570,7 +568,11 @@ function createSuggestionWithDefaultDateHistogram( function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layerId: string) { const layer = state.layers[layerId]; - const [availableBucketedColumns, availableMetricColumns] = separateBucketColumns(layer); + const [ + availableBucketedColumns, + availableMetricColumns, + availableReferenceColumns, + ] = getExistingColumnGroups(layer); return _.flatten( availableBucketedColumns.map((_col, index) => { @@ -581,21 +583,23 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer columnOrder: [...bucketedColumns, ...availableMetricColumns], }; - if (availableMetricColumns.length > 1) { - return [ - allMetricsSuggestion, - { ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }, - ]; + if (availableReferenceColumns.length) { + // Don't remove buckets when dealing with any refs. This can break refs. + return []; + } else if (availableMetricColumns.length > 1) { + return [{ ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }]; } else { return allMetricsSuggestion; } }) ) .concat( - availableMetricColumns.map((columnId) => { - // build suggestions with only metrics - return { ...layer, columnOrder: [columnId] }; - }) + availableReferenceColumns.length + ? [] + : availableMetricColumns.map((columnId) => { + // build suggestions with only metrics + return { ...layer, columnOrder: [columnId] }; + }) ) .map((updatedLayer) => { return buildSuggestion({ @@ -623,7 +627,3 @@ function getMetricSuggestionTitle(layer: IndexPatternLayer, onlyMetric: boolean) 'Title of a suggested chart containing only a single numerical metric calculated over all available data', }); } - -function separateBucketColumns(layer: IndexPatternLayer) { - return partition(layer.columnOrder, (columnId) => layer.columns[columnId].isBucketed); -} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts index ff900134df9a1a..6d7a0117a17704 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/__mocks__/index.ts @@ -42,6 +42,7 @@ export const { getErrorMessages, isReferenced, resetIncomplete, + isOperationAllowedAsReference, } = actualHelpers; export const { adjustTimeScaleLabelSuffix, DEFAULT_TIME_SCALE } = actualTimeScaleUtils; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx index 0cfba4cfc739f5..4fd045c17740d3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/counter_rate.tsx @@ -9,6 +9,7 @@ import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '. import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, + getErrorsForDateReference, checkForDateHistogram, dateBasedOperationToExpression, hasDateField, @@ -52,15 +53,18 @@ export const counterRateOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label, column.timeScale); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined, column.timeScale); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'lens_counter_rate'); @@ -69,7 +73,7 @@ export const counterRateOperation: OperationDefinition< const metric = layer.columns[referenceIds[0]]; const timeScale = previousColumn?.timeScale || DEFAULT_TIME_SCALE; return { - label: ofName(metric?.label, timeScale), + label: ofName(metric && 'sourceField' in metric ? metric.sourceField : undefined, timeScale), dataType: 'number', operationType: 'counter_rate', isBucketed: false, @@ -88,13 +92,22 @@ export const counterRateOperation: OperationDefinition< isTransferable: (column, newIndexPattern) => { return hasDateField(newIndexPattern); }, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.counterRate', { defaultMessage: 'Counter rate', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.counterRate', { + defaultMessage: 'Counter rate', + }) + )?.join(', '); + }, timeScalingMode: 'mandatory', }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx index 9244aaaf90ab76..7067b6470bec73 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/cumulative_sum.tsx @@ -7,12 +7,17 @@ import { i18n } from '@kbn/i18n'; import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '../column_types'; import { IndexPatternLayer } from '../../../types'; -import { checkForDateHistogram, dateBasedOperationToExpression } from './utils'; +import { + checkForDateHistogram, + getErrorsForDateReference, + dateBasedOperationToExpression, + hasDateField, +} from './utils'; import { OperationDefinition } from '..'; const ofName = (name?: string) => { return i18n.translate('xpack.lens.indexPattern.cumulativeSumOf', { - defaultMessage: 'Cumulative sum rate of {name}', + defaultMessage: 'Cumulative sum of {name}', values: { name: name ?? @@ -46,23 +51,26 @@ export const cumulativeSumOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'cumulative_sum'); }, buildColumn: ({ referenceIds, previousColumn, layer }) => { - const metric = layer.columns[referenceIds[0]]; + const ref = layer.columns[referenceIds[0]]; return { - label: ofName(metric?.label), + label: ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined), dataType: 'number', operationType: 'cumulative_sum', isBucketed: false, @@ -80,12 +88,21 @@ export const cumulativeSumOperation: OperationDefinition< isTransferable: () => { return true; }, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.cumulativeSum', { defaultMessage: 'Cumulative sum', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.cumulativeSum', { + defaultMessage: 'Cumulative sum', + }) + )?.join(', '); + }, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx index 41fe361c7ba9cf..358046ad5bfb9c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx @@ -10,6 +10,7 @@ import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, checkForDateHistogram, + getErrorsForDateReference, dateBasedOperationToExpression, hasDateField, } from './utils'; @@ -51,23 +52,29 @@ export const derivativeOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { - return ofName(columns[column.references[0]]?.label, column.timeScale); + const ref = columns[column.references[0]]; + return ofName(ref && 'sourceField' in ref ? ref.sourceField : undefined, column.timeScale); }, toExpression: (layer, columnId) => { return dateBasedOperationToExpression(layer, columnId, 'derivative'); }, buildColumn: ({ referenceIds, previousColumn, layer }) => { - const metric = layer.columns[referenceIds[0]]; + const ref = layer.columns[referenceIds[0]]; return { - label: ofName(metric?.label, previousColumn?.timeScale), + label: ofName( + ref && 'sourceField' in ref ? ref.sourceField : undefined, + previousColumn?.timeScale + ), dataType: 'number', operationType: 'derivative', isBucketed: false, @@ -87,13 +94,22 @@ export const derivativeOperation: OperationDefinition< return hasDateField(newIndexPattern); }, onOtherColumnChanged: adjustTimeScaleOnOtherColumnChange, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.derivative', { defaultMessage: 'Differences', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.derivative', { + defaultMessage: 'Differences', + }) + )?.join(', '); + }, timeScalingMode: 'optional', }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index 59d5924b9a3707..e1bc378635f1d6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -14,6 +14,7 @@ import { IndexPatternLayer } from '../../../types'; import { buildLabelFunction, checkForDateHistogram, + getErrorsForDateReference, dateBasedOperationToExpression, hasDateField, } from './utils'; @@ -50,7 +51,7 @@ export const movingAverageOperation: OperationDefinition< type: 'moving_average', priority: 1, displayName: i18n.translate('xpack.lens.indexPattern.movingAverage', { - defaultMessage: 'Moving Average', + defaultMessage: 'Moving average', }), input: 'fullReference', selectionStyle: 'full', @@ -60,12 +61,14 @@ export const movingAverageOperation: OperationDefinition< validateMetadata: (meta) => meta.dataType === 'number' && !meta.isBucketed, }, ], - getPossibleOperation: () => { - return { - dataType: 'number', - isBucketed: false, - scale: 'ratio', - }; + getPossibleOperation: (indexPattern) => { + if (hasDateField(indexPattern)) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } }, getDefaultLabel: (column, indexPattern, columns) => { return ofName(columns[column.references[0]]?.label, column.timeScale); @@ -99,17 +102,39 @@ export const movingAverageOperation: OperationDefinition< return hasDateField(newIndexPattern); }, onOtherColumnChanged: adjustTimeScaleOnOtherColumnChange, - getErrorMessage: (layer: IndexPatternLayer) => { - return checkForDateHistogram( + getErrorMessage: (layer: IndexPatternLayer, columnId: string) => { + return getErrorsForDateReference( layer, + columnId, i18n.translate('xpack.lens.indexPattern.movingAverage', { - defaultMessage: 'Moving Average', + defaultMessage: 'Moving average', }) ); }, + getDisabledStatus(indexPattern, layer) { + return checkForDateHistogram( + layer, + i18n.translate('xpack.lens.indexPattern.movingAverage', { + defaultMessage: 'Moving average', + }) + )?.join(', '); + }, timeScalingMode: 'optional', }; +function isValidNumber(input: string) { + if (input === '') return false; + try { + const val = parseFloat(input); + if (isNaN(val)) return false; + if (val < 1) return false; + if (val.toString().includes('.')) return false; + } catch (e) { + return false; + } + return true; +} + function MovingAverageParamEditor({ layer, updateLayer, @@ -120,10 +145,8 @@ function MovingAverageParamEditor({ useDebounceWithOptions( () => { - if (inputValue === '') { - return; - } - const inputNumber = Number(inputValue); + if (!isValidNumber(inputValue)) return; + const inputNumber = parseInt(inputValue, 10); updateLayer( updateColumnParam({ layer, @@ -137,6 +160,7 @@ function MovingAverageParamEditor({ 256, [inputValue] ); + return ( ) => setInputValue(e.target.value)} + min={1} + step={1} + isInvalid={!isValidNumber(inputValue)} /> ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts new file mode 100644 index 00000000000000..403f2b87ac86e9 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/utils.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { checkReferences } from './utils'; +import { operationDefinitionMap } from '..'; +import { createMockedReferenceOperation } from '../../mocks'; + +// Mock prevents issue with circular loading +jest.mock('..'); + +describe('utils', () => { + beforeEach(() => { + // @ts-expect-error test-only operation type + operationDefinitionMap.testReference = createMockedReferenceOperation(); + }); + + describe('checkReferences', () => { + it('should show an error if the reference is missing', () => { + expect( + checkReferences( + { + columns: { + ref: { + label: 'Label', + // @ts-expect-error test-only operation type + operationType: 'testReference', + isBucketed: false, + dataType: 'number', + references: ['missing'], + }, + }, + columnOrder: ['ref'], + indexPatternId: '', + }, + 'ref' + ) + ).toEqual(['"Label" is not fully configured']); + }); + + it('should show an error if the reference is not allowed per the requirements', () => { + expect( + checkReferences( + { + columns: { + ref: { + label: 'Label', + // @ts-expect-error test-only operation type + operationType: 'testReference', + isBucketed: false, + dataType: 'number', + references: ['invalid'], + }, + invalid: { + label: 'Date', + operationType: 'date_histogram', + isBucketed: true, + dataType: 'date', + sourceField: 'timestamp', + params: { interval: 'auto' }, + }, + }, + columnOrder: ['invalid', 'ref'], + indexPatternId: '', + }, + 'ref' + ) + ).toEqual(['Dimension "Label" is configured incorrectly']); + }); + }); +}); 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 bac45f683e4449..ca4b7c53b7ec74 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 @@ -5,11 +5,13 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionAST } from '@kbn/interpreter/common'; -import { TimeScaleUnit } from '../../../time_scale'; -import { IndexPattern, IndexPatternLayer } from '../../../types'; +import type { ExpressionFunctionAST } from '@kbn/interpreter/common'; +import type { TimeScaleUnit } from '../../../time_scale'; +import type { IndexPattern, IndexPatternLayer } from '../../../types'; import { adjustTimeScaleLabelSuffix } from '../../time_scale_utils'; -import { ReferenceBasedIndexPatternColumn } from '../column_types'; +import type { ReferenceBasedIndexPatternColumn } from '../column_types'; +import { operationDefinitionMap } from '..'; +import type { IndexPatternColumn, RequiredReference } from '..'; export const buildLabelFunction = (ofName: (name?: string) => string) => ( name?: string, @@ -41,6 +43,78 @@ export function checkForDateHistogram(layer: IndexPatternLayer, name: string) { ]; } +export function checkReferences(layer: IndexPatternLayer, columnId: string) { + const column = layer.columns[columnId] as ReferenceBasedIndexPatternColumn; + + const errors: string[] = []; + + column.references.forEach((referenceId, index) => { + if (!layer.columns[referenceId]) { + errors.push( + i18n.translate('xpack.lens.indexPattern.missingReferenceError', { + defaultMessage: '"{dimensionLabel}" is not fully configured', + values: { + dimensionLabel: column.label, + }, + }) + ); + } else { + const referenceColumn = layer.columns[referenceId]!; + const definition = operationDefinitionMap[column.operationType]; + if (definition.input !== 'fullReference') { + throw new Error('inconsistent state - column is not a reference operation'); + } + const requirements = definition.requiredReferences[index]; + const isValid = isColumnValidAsReference({ + validation: requirements, + column: referenceColumn, + }); + + if (!isValid) { + errors.push( + i18n.translate('xpack.lens.indexPattern.invalidReferenceConfiguration', { + defaultMessage: 'Dimension "{dimensionLabel}" is configured incorrectly', + values: { + dimensionLabel: column.label, + }, + }) + ); + } + } + }); + return errors.length ? errors : undefined; +} + +export function isColumnValidAsReference({ + column, + validation, +}: { + column: IndexPatternColumn; + validation: RequiredReference; +}): boolean { + if (!column) return false; + const operationType = column.operationType; + const operationDefinition = operationDefinitionMap[operationType]; + return ( + validation.input.includes(operationDefinition.input) && + (!validation.specificOperations || validation.specificOperations.includes(operationType)) && + validation.validateMetadata(column) + ); +} + +export function getErrorsForDateReference( + layer: IndexPatternLayer, + columnId: string, + name: string +) { + const dateErrors = checkForDateHistogram(layer, name) ?? []; + const referenceErrors = checkReferences(layer, columnId) ?? []; + if (dateErrors.length || referenceErrors.length) { + return [...dateErrors, ...referenceErrors]; + } + return; +} + export function hasDateField(indexPattern: IndexPattern) { return indexPattern.fields.some((field) => field.type === 'date'); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx index 95e905f6021be8..970f56020c7cdc 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/cardinality.tsx @@ -10,7 +10,7 @@ import { buildExpressionFunction } from '../../../../../../../src/plugins/expres import { OperationDefinition } from './index'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn } from './column_types'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); @@ -21,7 +21,9 @@ const IS_BUCKETED = false; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.cardinalityOf', { defaultMessage: 'Unique count of {name}', - values: { name }, + values: { + name, + }, }); } @@ -58,8 +60,7 @@ export const cardinalityOperation: OperationDefinition - ofName(indexPattern.getFieldByName(column.sourceField)!.displayName), + getDefaultLabel: (column, indexPattern) => ofName(getSafeName(column.sourceField, indexPattern)), buildColumn({ field, previousColumn }) { return { label: ofName(field.displayName), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx index 0d8ed44f528a82..06d330a4a7eb2f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/count.tsx @@ -69,7 +69,12 @@ export const countOperation: OperationDefinition + adjustTimeScaleOnOtherColumnChange( + layer, + thisColumnId, + changedColumnId + ), toEsAggsFn: (column, columnId) => { return buildExpressionFunction('aggCount', { id: columnId, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index eadcf8384b1dd9..8c2fa43b541d4f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -689,4 +689,32 @@ describe('date_histogram', () => { expect(instance.find('[data-test-subj="lensDateHistogramValue"]').exists()).toBeFalsy(); }); }); + + describe('getDefaultLabel', () => { + it('should not throw when the source field is not located', () => { + expect( + dateHistogramOperation.getDefaultLabel( + { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'missing', + params: { interval: 'auto' }, + }, + indexPattern1, + { + col1: { + label: '', + dataType: 'date', + isBucketed: true, + operationType: 'date_histogram', + sourceField: 'missing', + params: { interval: 'auto' }, + }, + } + ) + ).toEqual('Missing field'); + }); + }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index cdd1ccad96a991..a41cc88c4f2927 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -28,7 +28,7 @@ import { search, } from '../../../../../../../src/plugins/data/public'; import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; const { isValidInterval } = search.aggs; const autoInterval = 'auto'; @@ -67,8 +67,7 @@ export const dateHistogramOperation: OperationDefinition< }; } }, - getDefaultLabel: (column, indexPattern) => - indexPattern.getFieldByName(column.sourceField)!.displayName, + getDefaultLabel: (column, indexPattern) => getSafeName(column.sourceField, indexPattern), buildColumn({ field }) { let interval = autoInterval; let timeZone: string | undefined; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts new file mode 100644 index 00000000000000..04e04816d98efd --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.test.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createMockedIndexPattern } from '../../mocks'; +import { getInvalidFieldMessage } from './helpers'; + +describe('helpers', () => { + describe('getInvalidFieldMessage', () => { + it('return an error if a field was removed', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'count', // <= invalid + sourceField: 'bytes', + }, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field bytes was not found'); + }); + + it('returns an error if a field is the wrong type', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'avg', // <= invalid + sourceField: 'timestamp', + }, + createMockedIndexPattern() + ); + expect(messages).toHaveLength(1); + expect(messages![0]).toEqual('Field timestamp was not found'); + }); + + it('returns no message if all fields are matching', () => { + const messages = getInvalidFieldMessage( + { + dataType: 'number', + isBucketed: false, + label: 'Foo', + operationType: 'avg', + sourceField: 'bytes', + }, + createMockedIndexPattern() + ); + expect(messages).toBeUndefined(); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index 640a357d9a7a45..7b96bcf4f20698 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -62,3 +62,12 @@ export function getInvalidFieldMessage( ] : undefined; } + +export function getSafeName(name: string, indexPattern: IndexPattern): string { + const field = indexPattern.getFieldByName(name); + return field + ? field.displayName + : i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }); +} 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 6431dac7b381df..6231460347de26 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 @@ -152,8 +152,9 @@ interface BaseOperationDefinitionProps { * return an updated column. If not implemented, the `id` function is used instead. */ onOtherColumnChanged?: ( - currentColumn: C, - columns: Partial> + layer: IndexPatternLayer, + thisColumnId: string, + changedColumnId: string ) => C; /** * React component for operation specific settings shown in the popover editor @@ -176,7 +177,7 @@ interface BaseOperationDefinitionProps { * but disable it from usage, this function returns the string describing * the status. Otherwise it returns undefined */ - getDisabledStatus?: (indexPattern: IndexPattern) => string | undefined; + getDisabledStatus?: (indexPattern: IndexPattern, layer: IndexPatternLayer) => string | undefined; /** * Validate that the operation has the right preconditions in the state. For example: * @@ -314,9 +315,9 @@ interface FullReferenceOperationDefinition { ) => ReferenceBasedIndexPatternColumn & C; /** * Returns the meta data of the operation if applied. Undefined - * if the field is not applicable. + * if the operation can't be added with these fields. */ - getPossibleOperation: () => OperationMetadata; + getPossibleOperation: (indexPattern: IndexPattern) => OperationMetadata | undefined; /** * A chain of expression functions which will transform the table */ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index 817958aee54900..bde8cd0e424277 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -311,13 +311,13 @@ describe('last_value', () => { it('should return disabledStatus if indexPattern does contain date field', () => { const indexPattern = createMockedIndexPattern(); - expect(lastValueOperation.getDisabledStatus!(indexPattern)).toEqual(undefined); + expect(lastValueOperation.getDisabledStatus!(indexPattern, layer)).toEqual(undefined); const indexPatternWithoutTimeFieldName = { ...indexPattern, timeFieldName: undefined, }; - expect(lastValueOperation.getDisabledStatus!(indexPatternWithoutTimeFieldName)).toEqual( + expect(lastValueOperation.getDisabledStatus!(indexPatternWithoutTimeFieldName, layer)).toEqual( undefined ); @@ -326,7 +326,10 @@ describe('last_value', () => { fields: indexPattern.fields.filter((f) => f.type !== 'date'), }; - const disabledStatus = lastValueOperation.getDisabledStatus!(indexPatternWithoutTimefields); + const disabledStatus = lastValueOperation.getDisabledStatus!( + indexPatternWithoutTimefields, + layer + ); expect(disabledStatus).toEqual( 'This function requires the presence of a date field in your index' ); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 7b5aee860654aa..256ef7f75676d2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -13,12 +13,14 @@ import { FieldBasedIndexPatternColumn } from './column_types'; import { IndexPatternField, IndexPattern } from '../../types'; import { updateColumnParam } from '../layer_helpers'; import { DataType } from '../../../types'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.lastValueOf', { defaultMessage: 'Last value of {name}', - values: { name }, + values: { + name, + }, }); } @@ -87,8 +89,7 @@ export const lastValueOperation: OperationDefinition - indexPattern.getFieldByName(column.sourceField)!.displayName, + getDefaultLabel: (column, indexPattern) => ofName(getSafeName(column.sourceField, indexPattern)), input: 'field', onFieldChange: (oldColumn, field) => { const newParams = { ...oldColumn.params }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index a886bfdaad325c..eb25b5d932b1f8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; import { OperationDefinition } from './index'; -import { getInvalidFieldMessage } from './helpers'; +import { getInvalidFieldMessage, getSafeName } from './helpers'; import { FormattedIndexPatternColumn, FieldBasedIndexPatternColumn, @@ -45,11 +45,11 @@ function buildMetricOperation>({ optionalTimeScaling?: boolean; }) { const labelLookup = (name: string, column?: BaseIndexPatternColumn) => { - const rawLabel = ofName(name); + const label = ofName(name); if (!optionalTimeScaling) { - return rawLabel; + return label; } - return adjustTimeScaleLabelSuffix(rawLabel, undefined, column?.timeScale); + return adjustTimeScaleLabelSuffix(label, undefined, column?.timeScale); }; return { @@ -81,10 +81,12 @@ function buildMetricOperation>({ (!newField.aggregationRestrictions || newField.aggregationRestrictions![type]) ); }, - onOtherColumnChanged: (column, otherColumns) => - optionalTimeScaling ? adjustTimeScaleOnOtherColumnChange(column, otherColumns) : column, + onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => + optionalTimeScaling + ? adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) + : layer.columns[thisColumnId], getDefaultLabel: (column, indexPattern, columns) => - labelLookup(indexPattern.getFieldByName(column.sourceField)!.displayName, column), + labelLookup(getSafeName(column.sourceField, indexPattern), column), buildColumn: ({ field, previousColumn }) => ({ label: labelLookup(field.displayName, previousColumn), dataType: 'number', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 2ba8f5febce5bd..b687e6fe3da504 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -98,7 +98,10 @@ export const rangeOperation: OperationDefinition - indexPattern.getFieldByName(column.sourceField)!.displayName, + indexPattern.getFieldByName(column.sourceField)?.displayName ?? + i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }), buildColumn({ field }) { return { label: field.displayName, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 888df40873a353..8462d374c6e6b8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -18,23 +18,36 @@ import { } from '@elastic/eui'; import { AggFunctionsMapping } from '../../../../../../../../src/plugins/data/public'; import { buildExpressionFunction } from '../../../../../../../../src/plugins/expressions/public'; -import { IndexPatternColumn } from '../../../indexpattern'; import { updateColumnParam, isReferenced } from '../../layer_helpers'; import { DataType } from '../../../../types'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { ValuesRangeInput } from './values_range_input'; import { getInvalidFieldMessage } from '../helpers'; +import type { IndexPatternLayer } from '../../../types'; -function ofName(name: string) { +function ofName(name?: string) { return i18n.translate('xpack.lens.indexPattern.termsOf', { defaultMessage: 'Top values of {name}', - values: { name }, + values: { + name: + name ?? + i18n.translate('xpack.lens.indexPattern.missingFieldLabel', { + defaultMessage: 'Missing field', + }), + }, }); } -function isSortableByColumn(column: IndexPatternColumn) { - return !column.isBucketed && column.operationType !== 'last_value'; +function isSortableByColumn(layer: IndexPatternLayer, columnId: string) { + const column = layer.columns[columnId]; + return ( + column && + !column.isBucketed && + column.operationType !== 'last_value' && + !('references' in column) && + !isReferenced(layer, columnId) + ); } const DEFAULT_SIZE = 3; @@ -89,10 +102,7 @@ export const termsOperation: OperationDefinition - column && !isReferenced(layer, columnId) && isSortableByColumn(column) - ) + .filter(([columnId]) => isSortableByColumn(layer, columnId)) .map(([id]) => id)[0]; const previousBucketsLength = Object.values(layer.columns).filter( @@ -138,7 +148,7 @@ export const termsOperation: OperationDefinition - ofName(indexPattern.getFieldByName(column.sourceField)!.displayName), + ofName(indexPattern.getFieldByName(column.sourceField)?.displayName), onFieldChange: (oldColumn, field) => { const newParams = { ...oldColumn.params }; if ('format' in newParams && field.type !== 'number') { @@ -152,11 +162,13 @@ export const termsOperation: OperationDefinition { + onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => { + const columns = layer.columns; + const currentColumn = columns[thisColumnId] as TermsIndexPatternColumn; if (currentColumn.params.orderBy.type === 'column') { // check whether the column is still there and still a metric const columnSortedBy = columns[currentColumn.params.orderBy.columnId]; - if (!columnSortedBy || !isSortableByColumn(columnSortedBy)) { + if (!columnSortedBy || !isSortableByColumn(layer, changedColumnId)) { return { ...currentColumn, params: { @@ -194,7 +206,7 @@ export const termsOperation: OperationDefinition isSortableByColumn(column)) + .filter(([sortId]) => isSortableByColumn(layer, sortId)) .map(([sortId, column]) => { return { value: toValue({ type: 'column', columnId: sortId }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index eb78bb3ffebff8..0209c0b9a448b7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -402,15 +402,25 @@ describe('terms', () => { }, sourceField: 'category', }; - const updatedColumn = termsOperation.onOtherColumnChanged!(initialColumn, { - col1: { - label: 'Count', - dataType: 'number', - isBucketed: false, - sourceField: 'Records', - operationType: 'count', + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + indexPatternId: '', + columnOrder: [], + columns: { + col2: initialColumn, + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, }, - }); + 'col2', + 'col1' + ); + expect(updatedColumn).toBe(initialColumn); }); @@ -429,18 +439,74 @@ describe('terms', () => { }, sourceField: 'category', }; - const updatedColumn = termsOperation.onOtherColumnChanged!(initialColumn, { - col1: { - label: 'Last Value', - dataType: 'number', - isBucketed: false, - sourceField: 'bytes', - operationType: 'last_value', - params: { - sortField: 'time', + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + columns: { + col2: initialColumn, + col1: { + label: 'Last Value', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'last_value', + params: { + sortField: 'time', + }, + }, }, + columnOrder: [], + indexPatternId: '', }, - }); + 'col2', + 'col1' + ); + expect(updatedColumn.params).toEqual( + expect.objectContaining({ + orderBy: { type: 'alphabetical' }, + }) + ); + }); + + it('should switch to alphabetical ordering if metric is reference-based', () => { + const initialColumn: TermsIndexPatternColumn = { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }; + const updatedColumn = termsOperation.onOtherColumnChanged!( + { + columns: { + col2: initialColumn, + col1: { + label: 'Cumulative sum', + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + references: ['referenced'], + }, + referenced: { + label: '', + dataType: 'number', + isBucketed: false, + operationType: 'count', + sourceField: 'Records', + }, + }, + columnOrder: [], + indexPatternId: '', + }, + 'col2', + 'col1' + ); expect(updatedColumn.params).toEqual( expect.objectContaining({ orderBy: { type: 'alphabetical' }, @@ -451,20 +517,27 @@ describe('terms', () => { it('should switch to alphabetical ordering if there are no columns to order by', () => { const termsColumn = termsOperation.onOtherColumnChanged!( { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, + columns: { + col2: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, - // Private - operationType: 'terms', - params: { - orderBy: { type: 'column', columnId: 'col1' }, - size: 3, - orderDirection: 'asc', + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, }, - sourceField: 'category', + columnOrder: [], + indexPatternId: '', }, - {} + 'col2', + 'col1' ); expect(termsColumn.params).toEqual( expect.objectContaining({ @@ -476,33 +549,39 @@ describe('terms', () => { it('should switch to alphabetical ordering if the order column is not a metric anymore', () => { const termsColumn = termsOperation.onOtherColumnChanged!( { - label: 'Top value of category', - dataType: 'string', - isBucketed: true, + columns: { + col2: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, - // Private - operationType: 'terms', - params: { - orderBy: { type: 'column', columnId: 'col1' }, - size: 3, - orderDirection: 'asc', - }, - sourceField: 'category', - }, - { - col1: { - label: 'Value of timestamp', - dataType: 'date', - isBucketed: true, + // Private + operationType: 'terms', + params: { + orderBy: { type: 'column', columnId: 'col1' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col1: { + label: 'Value of timestamp', + dataType: 'date', + isBucketed: true, - // Private - operationType: 'date_histogram', - params: { - interval: 'w', + // Private + operationType: 'date_histogram', + params: { + interval: 'w', + }, + sourceField: 'timestamp', }, - sourceField: 'timestamp', }, - } + columnOrder: [], + indexPatternId: '', + }, + 'col2', + 'col1' ); expect(termsColumn.params).toEqual( expect.objectContaining({ 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 7123becf71b4dd..079913347470ad 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/index.ts @@ -12,6 +12,7 @@ export { IndexPatternColumn, FieldBasedIndexPatternColumn, IncompleteColumn, + RequiredReference, } from './definitions'; export { createMockedReferenceOperation } from './mocks'; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index bb09474798fd4d..9496f95f74dec2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -190,6 +190,44 @@ describe('state_helpers', () => { ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2'] })); }); + it('should insert a metric after buckets, but before references', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1'], + columns: { + col1: { + label: 'Date histogram of timestamp', + dataType: 'date', + isBucketed: true, + + // Private + operationType: 'date_histogram', + sourceField: 'timestamp', + params: { + interval: 'h', + }, + }, + col3: { + label: 'Reference', + dataType: 'number', + isBucketed: false, + + operationType: 'cumulative_sum', + references: ['col2'], + }, + }, + }; + expect( + insertNewColumn({ + layer, + indexPattern, + columnId: 'col2', + op: 'count', + field: documentField, + }) + ).toEqual(expect.objectContaining({ columnOrder: ['col1', 'col2', 'col3'] })); + }); + it('should insert new buckets at the end of previous buckets', () => { const layer: IndexPatternLayer = { indexPatternId: '1', @@ -782,18 +820,83 @@ describe('state_helpers', () => { field: indexPattern.fields[2], // bytes field }); - expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith(termsColumn, { - col1: termsColumn, - col2: expect.objectContaining({ - label: 'Average of bytes', - dataType: 'number', - isBucketed: false, + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: termsColumn, + col2: expect.objectContaining({ + label: 'Average of bytes', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'avg', + }), + }, + incompleteColumns: {}, + }, + 'col1', + 'col2' + ); + }); - // Private - operationType: 'avg', - sourceField: 'bytes', - }), + it('should execute adjustments for other columns when creating a reference', () => { + const termsColumn: TermsIndexPatternColumn = { + label: 'Top values of source', + dataType: 'string', + isBucketed: true, + + // Private + operationType: 'terms', + sourceField: 'source', + params: { + orderBy: { type: 'column', columnId: 'willBeReference' }, + orderDirection: 'desc', + size: 5, + }, + }; + + replaceColumn({ + layer: { + indexPatternId: '1', + columnOrder: ['col1', 'willBeReference'], + columns: { + col1: termsColumn, + willBeReference: { + label: 'Count', + dataType: 'number', + isBucketed: false, + sourceField: 'Records', + operationType: 'count', + }, + }, + }, + indexPattern, + columnId: 'willBeReference', + op: 'cumulative_sum', }); + + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { + indexPatternId: '1', + columnOrder: ['col1', 'willBeReference'], + columns: { + col1: { + ...termsColumn, + params: { orderBy: { type: 'alphabetical' }, orderDirection: 'asc', size: 5 }, + }, + willBeReference: expect.objectContaining({ + dataType: 'number', + isBucketed: false, + operationType: 'cumulative_sum', + }), + }, + incompleteColumns: {}, + }, + 'col1', + 'willBeReference' + ); }); it('should not wrap the previous operation when switching to reference', () => { @@ -963,7 +1066,7 @@ describe('state_helpers', () => { isTransferable: jest.fn(), toExpression: jest.fn().mockReturnValue([]), getPossibleOperation: jest.fn().mockReturnValue({ dataType: 'number', isBucketed: false }), - getDefaultLabel: () => 'Test reference', + getDefaultLabel: jest.fn().mockReturnValue('Test reference'), }; const layer: IndexPatternLayer = { @@ -1081,6 +1184,7 @@ describe('state_helpers', () => { }, }, columnId: 'col1', + indexPattern, }) ).toEqual({ indexPatternId: '1', @@ -1126,6 +1230,7 @@ describe('state_helpers', () => { }, }, columnId: 'col2', + indexPattern, }) ).toEqual({ indexPatternId: '1', @@ -1176,11 +1281,14 @@ describe('state_helpers', () => { }, }, columnId: 'col2', + indexPattern, }); - expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith(termsColumn, { - col1: termsColumn, - }); + expect(operationDefinitionMap.terms.onOtherColumnChanged).toHaveBeenCalledWith( + { indexPatternId: '1', columnOrder: ['col1', 'col2'], columns: { col1: termsColumn } }, + 'col1', + 'col2' + ); }); it('should delete the column and all of its references', () => { @@ -1207,11 +1315,57 @@ describe('state_helpers', () => { }, }, }; - expect(deleteColumn({ layer, columnId: 'col2' })).toEqual( + expect(deleteColumn({ layer, columnId: 'col2', indexPattern })).toEqual( expect.objectContaining({ columnOrder: [], columns: {} }) ); }); + it('should update the labels when deleting columns', () => { + const layer: IndexPatternLayer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Count', + dataType: 'number', + isBucketed: false, + + operationType: 'count', + sourceField: 'Records', + }, + col2: { + label: 'Changed label', + dataType: 'number', + isBucketed: false, + + // @ts-expect-error not a valid type + operationType: 'testReference', + references: ['col1'], + }, + }, + }; + deleteColumn({ layer, columnId: 'col1', indexPattern }); + expect(operationDefinitionMap.testReference.getDefaultLabel).toHaveBeenCalledWith( + { + label: 'Changed label', + dataType: 'number', + isBucketed: false, + operationType: 'testReference', + references: ['col1'], + }, + indexPattern, + { + col2: { + label: 'Default label', + dataType: 'number', + isBucketed: false, + operationType: 'testReference', + references: ['col1'], + }, + } + ); + }); + it('should recursively delete references', () => { const layer: IndexPatternLayer = { indexPatternId: '1', @@ -1245,7 +1399,7 @@ describe('state_helpers', () => { }, }, }; - expect(deleteColumn({ layer, columnId: 'col3' })).toEqual( + expect(deleteColumn({ layer, columnId: 'col3', indexPattern })).toEqual( expect.objectContaining({ columnOrder: [], columns: {} }) ); }); @@ -1680,63 +1834,34 @@ describe('state_helpers', () => { }); describe('getErrorMessages', () => { - it('should collect errors from the operation definitions', () => { + it('should collect errors from metric-type operation definitions', () => { const mock = jest.fn().mockReturnValue(['error 1']); - operationDefinitionMap.testReference.getErrorMessage = mock; + operationDefinitionMap.avg.getErrorMessage = mock; const errors = getErrorMessages({ indexPatternId: '1', columnOrder: [], columns: { - col1: - // @ts-expect-error not statically analyzed - { operationType: 'testReference', references: [] }, + // @ts-expect-error invalid column + col1: { operationType: 'avg' }, }, }); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); - it('should identify missing references', () => { + it('should collect errors from reference-type operation definitions', () => { + const mock = jest.fn().mockReturnValue(['error 1']); + operationDefinitionMap.testReference.getErrorMessage = mock; const errors = getErrorMessages({ indexPatternId: '1', columnOrder: [], columns: { col1: - // @ts-expect-error not statically analyzed yet - { operationType: 'testReference', references: ['ref1', 'ref2'] }, - }, - }); - expect(errors).toHaveLength(2); - }); - - it('should identify references that are no longer valid', () => { - // There is only one operation with `none` as the input type - // @ts-expect-error this function is not valid - operationDefinitionMap.testReference.requiredReferences = [ - { - input: ['none'], - validateMetadata: () => true, - }, - ]; - - const errors = getErrorMessages({ - indexPatternId: '1', - columnOrder: [], - columns: { - // @ts-expect-error incomplete operation - ref1: { - dataType: 'string', - isBucketed: true, - operationType: 'terms', - }, - col1: { - label: '', - references: ['ref1'], - // @ts-expect-error tests only - operationType: 'testReference', - }, + // @ts-expect-error not statically analyzed + { operationType: 'testReference', references: [] }, }, }); + expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index 1619ad907fffca..2d8078b9a61545 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -5,7 +5,6 @@ */ import _, { partition } from 'lodash'; -import { i18n } from '@kbn/i18n'; import { operationDefinitionMap, operationDefinitions, @@ -61,9 +60,15 @@ export function insertNewColumn({ const possibleOperation = operationDefinition.getPossibleOperation(); const isBucketed = Boolean(possibleOperation.isBucketed); if (isBucketed) { - return addBucket(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId); + return updateDefaultLabels( + addBucket(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + indexPattern + ); } else { - return addMetric(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId); + return updateDefaultLabels( + addMetric(layer, operationDefinition.buildColumn({ ...baseOptions, layer }), columnId), + indexPattern + ); } } @@ -77,7 +82,7 @@ export function insertNewColumn({ // access to the operationSupportMatrix, we should validate the metadata against // the possible fields const validOperations = Object.values(operationDefinitionMap).filter(({ type }) => - isOperationAllowedAsReference({ validation, operationType: type }) + isOperationAllowedAsReference({ validation, operationType: type, indexPattern }) ); if (!validOperations.length) { @@ -122,29 +127,23 @@ export function insertNewColumn({ return newId; }); - const possibleOperation = operationDefinition.getPossibleOperation(); - const isBucketed = Boolean(possibleOperation.isBucketed); - if (isBucketed) { - return addBucket( - tempLayer, - operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - }), - columnId + const possibleOperation = operationDefinition.getPossibleOperation(indexPattern); + if (!possibleOperation) { + throw new Error( + `Can't create operation ${op} because it's incompatible with the index pattern` ); - } else { - return addMetric( + } + const isBucketed = Boolean(possibleOperation.isBucketed); + + const addOperationFn = isBucketed ? addBucket : addMetric; + return updateDefaultLabels( + addOperationFn( tempLayer, - operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - }), + operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, referenceIds }), columnId - ); - } + ), + indexPattern + ); } const invalidFieldName = (layer.incompleteColumns ?? {})[columnId]?.sourceField; @@ -159,16 +158,22 @@ export function insertNewColumn({ } const isBucketed = Boolean(possibleOperation.isBucketed); if (isBucketed) { - return addBucket( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), - columnId + return updateDefaultLabels( + addBucket( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ), + indexPattern ); } else { - return addMetric( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), - columnId + return updateDefaultLabels( + addMetric( + layer, + operationDefinition.buildColumn({ ...baseOptions, layer, field: invalidField }), + columnId + ), + indexPattern ); } } else if (!field) { @@ -193,19 +198,15 @@ export function insertNewColumn({ }; } const isBucketed = Boolean(possibleOperation.isBucketed); - if (isBucketed) { - return addBucket( + const addOperationFn = isBucketed ? addBucket : addMetric; + return updateDefaultLabels( + addOperationFn( layer, operationDefinition.buildColumn({ ...baseOptions, layer, field }), columnId - ); - } else { - return addMetric( - layer, - operationDefinition.buildColumn({ ...baseOptions, layer, field }), - columnId - ); - } + ), + indexPattern + ); } export function replaceColumn({ @@ -241,39 +242,50 @@ export function replaceColumn({ if (previousDefinition.input === 'fullReference') { (previousColumn as ReferenceBasedIndexPatternColumn).references.forEach((id: string) => { - tempLayer = deleteColumn({ layer: tempLayer, columnId: id }); + tempLayer = deleteColumn({ layer: tempLayer, columnId: id, indexPattern }); }); } + tempLayer = resetIncomplete(tempLayer, columnId); + if (operationDefinition.input === 'fullReference') { const referenceIds = operationDefinition.requiredReferences.map(() => generateId()); - const newColumns = { - ...tempLayer.columns, - [columnId]: operationDefinition.buildColumn({ - ...baseOptions, - layer: tempLayer, - referenceIds, - previousColumn, - }), - }; - return { + const newLayer = { ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: newColumns, + columns: { + ...tempLayer.columns, + [columnId]: operationDefinition.buildColumn({ + ...baseOptions, + layer: tempLayer, + referenceIds, + previousColumn, + }), + }, }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } if (operationDefinition.input === 'none') { let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer }); newColumn = adjustLabel(newColumn, previousColumn); - const newColumns = { ...tempLayer.columns, [columnId]: newColumn }; - return { - ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } if (!field) { @@ -289,12 +301,15 @@ export function replaceColumn({ let newColumn = operationDefinition.buildColumn({ ...baseOptions, layer: tempLayer, field }); newColumn = adjustLabel(newColumn, previousColumn); - const newColumns = { ...tempLayer.columns, [columnId]: newColumn }; - return { - ...tempLayer, - columnOrder: getColumnOrder({ ...tempLayer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + const newLayer = { ...tempLayer, columns: { ...tempLayer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...tempLayer, + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } else if ( operationDefinition.input === 'field' && field && @@ -304,12 +319,20 @@ export function replaceColumn({ // Same operation, new field const newColumn = operationDefinition.onFieldChange(previousColumn, field); - const newColumns = { ...layer.columns, [columnId]: adjustLabel(newColumn, previousColumn) }; - return { - ...layer, - columnOrder: getColumnOrder({ ...layer, columns: newColumns }), - columns: adjustColumnReferencesForChangedColumn(newColumns, columnId), - }; + if (previousColumn.customLabel) { + newColumn.customLabel = true; + newColumn.label = previousColumn.label; + } + + const newLayer = { ...layer, columns: { ...layer.columns, [columnId]: newColumn } }; + return updateDefaultLabels( + { + ...resetIncomplete(layer, columnId), + columnOrder: getColumnOrder(newLayer), + columns: adjustColumnReferencesForChangedColumn(newLayer, columnId), + }, + indexPattern + ); } else { throw new Error('nothing changed'); } @@ -370,7 +393,6 @@ function addMetric( ...layer.columns, [addedColumnId]: column, }, - columnOrder: [...layer.columnOrder, addedColumnId], }; return { ...tempLayer, columnOrder: getColumnOrder(tempLayer) }; } @@ -409,17 +431,18 @@ export function updateColumnParam({ }; } -function adjustColumnReferencesForChangedColumn( - columns: Record, - columnId: string -) { - const newColumns = { ...columns }; +function adjustColumnReferencesForChangedColumn(layer: IndexPatternLayer, changedColumnId: string) { + const newColumns = { ...layer.columns }; Object.keys(newColumns).forEach((currentColumnId) => { - if (currentColumnId !== columnId) { + if (currentColumnId !== changedColumnId) { const currentColumn = newColumns[currentColumnId]; const operationDefinition = operationDefinitionMap[currentColumn.operationType]; newColumns[currentColumnId] = operationDefinition.onOtherColumnChanged - ? operationDefinition.onOtherColumnChanged(currentColumn, newColumns) + ? operationDefinition.onOtherColumnChanged( + { ...layer, columns: newColumns }, + currentColumnId, + changedColumnId + ) : currentColumn; } }); @@ -429,9 +452,11 @@ function adjustColumnReferencesForChangedColumn( export function deleteColumn({ layer, columnId, + indexPattern, }: { layer: IndexPatternLayer; columnId: string; + indexPattern: IndexPattern; }): IndexPatternLayer { const column = layer.columns[columnId]; if (!column) { @@ -451,17 +476,27 @@ export function deleteColumn({ let newLayer = { ...layer, - columns: adjustColumnReferencesForChangedColumn(hypotheticalColumns, columnId), + columns: adjustColumnReferencesForChangedColumn( + { ...layer, columns: hypotheticalColumns }, + columnId + ), }; extraDeletions.forEach((id) => { - newLayer = deleteColumn({ layer: newLayer, columnId: id }); + newLayer = deleteColumn({ layer: newLayer, columnId: id, indexPattern }); }); const newIncomplete = { ...(newLayer.incompleteColumns || {}) }; delete newIncomplete[columnId]; - return { ...newLayer, columnOrder: getColumnOrder(newLayer), incompleteColumns: newIncomplete }; + return updateDefaultLabels( + { + ...newLayer, + columnOrder: getColumnOrder(newLayer), + incompleteColumns: newIncomplete, + }, + indexPattern + ); } // Derives column order from column object, respects existing columnOrder @@ -482,7 +517,7 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] { const [direct, referenceBased] = _.partition( entries, - ([id, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference' + ([, col]) => operationDefinitionMap[col.operationType].input !== 'fullReference' ); // If a reference has another reference as input, put it last in sort order referenceBased.sort(([idA, a], [idB, b]) => { @@ -503,7 +538,7 @@ export function getColumnOrder(layer: IndexPatternLayer): string[] { } // Splits existing columnOrder into the three categories -function getExistingColumnGroups(layer: IndexPatternLayer): [string[], string[], string[]] { +export function getExistingColumnGroups(layer: IndexPatternLayer): [string[], string[], string[]] { const [direct, referenced] = partition( layer.columnOrder, (columnId) => layer.columns[columnId] && !('references' in layer.columns[columnId]) @@ -553,44 +588,9 @@ export function getErrorMessages(layer: IndexPatternLayer): string[] | undefined Object.entries(layer.columns).forEach(([columnId, column]) => { const def = operationDefinitionMap[column.operationType]; - if (def.input === 'fullReference' && def.getErrorMessage) { + if (def.getErrorMessage) { errors.push(...(def.getErrorMessage(layer, columnId) ?? [])); } - - if ('references' in column) { - column.references.forEach((referenceId, index) => { - if (!layer.columns[referenceId]) { - errors.push( - i18n.translate('xpack.lens.indexPattern.missingReferenceError', { - defaultMessage: 'Dimension {dimensionLabel} is incomplete', - values: { - dimensionLabel: column.label, - }, - }) - ); - } else { - const referenceColumn = layer.columns[referenceId]!; - const requirements = - // @ts-expect-error not statically analyzed - operationDefinitionMap[column.operationType].requiredReferences[index]; - const isValid = isColumnValidAsReference({ - validation: requirements, - column: referenceColumn, - }); - - if (!isValid) { - errors.push( - i18n.translate('xpack.lens.indexPattern.invalidReferenceConfiguration', { - defaultMessage: 'Dimension {dimensionLabel} does not have a valid configuration', - values: { - dimensionLabel: column.label, - }, - }) - ); - } - } - }); - } }); return errors.length ? errors : undefined; @@ -603,30 +603,15 @@ export function isReferenced(layer: IndexPatternLayer, columnId: string): boolea return allReferences.includes(columnId); } -function isColumnValidAsReference({ - column, - validation, -}: { - column: IndexPatternColumn; - validation: RequiredReference; -}): boolean { - if (!column) return false; - const operationType = column.operationType; - const operationDefinition = operationDefinitionMap[operationType]; - return ( - validation.input.includes(operationDefinition.input) && - (!validation.specificOperations || validation.specificOperations.includes(operationType)) && - validation.validateMetadata(column) - ); -} - -function isOperationAllowedAsReference({ +export function isOperationAllowedAsReference({ operationType, validation, field, + indexPattern, }: { operationType: OperationType; validation: RequiredReference; + indexPattern: IndexPattern; field?: IndexPatternField; }): boolean { const operationDefinition = operationDefinitionMap[operationType]; @@ -635,9 +620,12 @@ function isOperationAllowedAsReference({ if (field && operationDefinition.input === 'field') { const metadata = operationDefinition.getPossibleOperationForField(field); hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); - } else if (operationDefinition.input !== 'field') { + } else if (operationDefinition.input === 'none') { const metadata = operationDefinition.getPossibleOperation(); hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); + } else if (operationDefinition.input === 'fullReference') { + const metadata = operationDefinition.getPossibleOperation(indexPattern); + hasValidMetadata = Boolean(metadata) && validation.validateMetadata(metadata!); } else { // TODO: How can we validate the metadata without a specific field? } @@ -648,6 +636,29 @@ function isOperationAllowedAsReference({ ); } +// Labels need to be updated when columns are added because reference-based column labels +// are sometimes copied into the parents +function updateDefaultLabels( + layer: IndexPatternLayer, + indexPattern: IndexPattern +): IndexPatternLayer { + const copiedColumns = { ...layer.columns }; + layer.columnOrder.forEach((id) => { + const col = copiedColumns[id]; + if (!col.customLabel) { + copiedColumns[id] = { + ...col, + label: operationDefinitionMap[col.operationType].getDefaultLabel( + col, + indexPattern, + copiedColumns + ), + }; + } + }); + return { ...layer, columns: copiedColumns }; +} + export function resetIncomplete(layer: IndexPatternLayer, columnId: string): IndexPatternLayer { const incompleteColumns = { ...(layer.incompleteColumns ?? {}) }; delete incompleteColumns[columnId]; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts index 58685fa494a046..c111983ea2cd6c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.ts @@ -167,10 +167,13 @@ export function getAvailableOperationsByMetadata(indexPattern: IndexPattern) { operationDefinition.getPossibleOperation() ); } else if (operationDefinition.input === 'fullReference') { - addToMap( - { type: 'fullReference', operationType: operationDefinition.type }, - operationDefinition.getPossibleOperation() - ); + const validOperation = operationDefinition.getPossibleOperation(indexPattern); + if (validOperation) { + addToMap( + { type: 'fullReference', operationType: operationDefinition.type }, + validOperation + ); + } } }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts index 841011c5884336..09132b142986f9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.test.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeScaleUnit } from '../time_scale'; -import { IndexPatternColumn } from './definitions'; +import type { IndexPatternLayer } from '../types'; +import type { TimeScaleUnit } from '../time_scale'; +import type { IndexPatternColumn } from './definitions'; import { adjustTimeScaleLabelSuffix, adjustTimeScaleOnOtherColumnChange } from './time_scale_utils'; export const DEFAULT_TIME_SCALE = 's' as TimeScaleUnit; @@ -48,45 +49,71 @@ describe('time scale utils', () => { isBucketed: false, timeScale: 's', }; + const baseLayer: IndexPatternLayer = { + columns: { col1: baseColumn }, + columnOrder: [], + indexPatternId: '', + }; it('should keep column if there is no time scale', () => { const column = { ...baseColumn, timeScale: undefined }; - expect(adjustTimeScaleOnOtherColumnChange(column, { col1: column })).toBe(column); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: column } }, + 'col1', + 'col2' + ) + ).toBe(column); }); it('should keep time scale if there is a date histogram', () => { expect( - adjustTimeScaleOnOtherColumnChange(baseColumn, { - col1: baseColumn, - col2: { - operationType: 'date_histogram', - dataType: 'date', - isBucketed: true, - label: '', + adjustTimeScaleOnOtherColumnChange( + { + ...baseLayer, + columns: { + col1: baseColumn, + col2: { + operationType: 'date_histogram', + dataType: 'date', + isBucketed: true, + label: '', + sourceField: 'date', + params: { interval: 'auto' }, + }, + }, }, - }) + 'col1', + 'col2' + ) ).toBe(baseColumn); }); it('should remove time scale if there is no date histogram', () => { - expect(adjustTimeScaleOnOtherColumnChange(baseColumn, { col1: baseColumn })).toHaveProperty( + expect(adjustTimeScaleOnOtherColumnChange(baseLayer, 'col1', 'col2')).toHaveProperty( 'timeScale', undefined ); }); it('should remove suffix from label', () => { - expect(adjustTimeScaleOnOtherColumnChange(baseColumn, { col1: baseColumn })).toHaveProperty( - 'label', - 'Count of records' - ); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: baseColumn } }, + 'col1', + 'col2' + ) + ).toHaveProperty('label', 'Count of records'); }); it('should keep custom label', () => { const column = { ...baseColumn, label: 'abc', customLabel: true }; - expect(adjustTimeScaleOnOtherColumnChange(column, { col1: column })).toHaveProperty( - 'label', - 'abc' - ); + expect( + adjustTimeScaleOnOtherColumnChange( + { ...baseLayer, columns: { col1: column } }, + 'col1', + 'col2' + ) + ).toHaveProperty('label', 'abc'); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts index 5d525e573a6177..340cad97e7db09 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/time_scale_utils.ts @@ -5,8 +5,9 @@ */ import { unitSuffixesLong } from '../suffix_formatter'; -import { TimeScaleUnit } from '../time_scale'; -import { BaseIndexPatternColumn } from './definitions/column_types'; +import type { TimeScaleUnit } from '../time_scale'; +import type { IndexPatternLayer } from '../types'; +import type { IndexPatternColumn } from './definitions'; export const DEFAULT_TIME_SCALE = 's' as TimeScaleUnit; @@ -30,10 +31,13 @@ export function adjustTimeScaleLabelSuffix( return `${cleanedLabel} ${unitSuffixesLong[newTimeScale]}`; } -export function adjustTimeScaleOnOtherColumnChange( - column: T, - columns: Partial> -) { +export function adjustTimeScaleOnOtherColumnChange( + layer: IndexPatternLayer, + thisColumnId: string, + changedColumnId: string +): T { + const columns = layer.columns; + const column = columns[thisColumnId] as T; if (!column.timeScale) { return column; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts index 702930d02a90ef..57cc4abeb723a2 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts @@ -5,7 +5,7 @@ */ import { DataType } from '../types'; -import { IndexPatternPrivateState, IndexPattern, IndexPatternLayer } from './types'; +import { IndexPattern, IndexPatternLayer } from './types'; import { DraggedField } from './indexpattern'; import { BaseIndexPatternColumn, @@ -44,29 +44,6 @@ export function isDraggedField(fieldCandidate: unknown): fieldCandidate is Dragg ); } -export function hasInvalidColumns(state: IndexPatternPrivateState) { - return getInvalidLayers(state).length > 0; -} - -export function getInvalidLayers(state: IndexPatternPrivateState) { - return Object.values(state.layers).filter((layer) => { - return layer.columnOrder.some((columnId) => - isColumnInvalid(layer, columnId, state.indexPatterns[layer.indexPatternId]) - ); - }); -} - -export function getInvalidColumnsForLayer( - layers: IndexPatternLayer[], - indexPatternMap: Record -) { - return layers.map((layer) => { - return layer.columnOrder.filter((columnId) => - isColumnInvalid(layer, columnId, indexPatternMap[layer.indexPatternId]) - ); - }); -} - export function isColumnInvalid( layer: IndexPatternLayer, columnId: string, diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 92ea9508cf8371..c212a371401d90 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -326,6 +326,81 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.lens.getDatatableCellText(0, 1)).to.eql('6,011.351'); }); + it('should create a valid XY chart with references', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'moving_average', + keepOpen: true, + }); + await PageObjects.lens.configureReference({ + operation: 'sum', + field: 'bytes', + }); + await PageObjects.lens.closeDimensionEditor(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'cumulative_sum', + keepOpen: true, + }); + await PageObjects.lens.configureReference({ + field: 'Records', + }); + await PageObjects.lens.closeDimensionEditor(); + + // Two Y axes that are both valid + expect(await find.allByCssSelector('.echLegendItem')).to.have.length(2); + }); + + /** + * The edge cases are: + * + * 1. Showing errors when creating a partial configuration + * 2. Being able to drag in a new field while in partial config + * 3. Being able to switch charts while in partial config + */ + it('should handle edge cases in reference-based operations', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', + operation: 'date_histogram', + field: '@timestamp', + }); + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'cumulative_sum', + }); + expect(await PageObjects.lens.getErrorCount()).to.eql(1); + + await PageObjects.lens.removeDimension('lnsXY_xDimensionPanel'); + expect(await PageObjects.lens.getErrorCount()).to.eql(2); + + await PageObjects.lens.dragFieldToDimensionTrigger( + '@timestamp', + 'lnsXY_xDimensionPanel > lns-empty-dimension' + ); + expect(await PageObjects.lens.getErrorCount()).to.eql(1); + + expect(await PageObjects.lens.hasChartSwitchWarning('lnsDatatable')).to.eql(false); + await PageObjects.lens.switchToVisualization('lnsDatatable'); + + expect(await PageObjects.lens.getDimensionTriggerText('lnsDatatable_metrics')).to.eql( + 'Cumulative sum of (incomplete)' + ); + }); + it('should allow to change index pattern', async () => { await PageObjects.lens.switchFirstLayerIndexPattern('log*'); expect(await PageObjects.lens.getFirstLayerIndexPattern()).to.equal('log*'); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 2159f939a56f7d..7e1fb4ab10a4a0 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -122,6 +122,32 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } }, + /** + * Changes the specified dimension to the specified operation and (optinally) field. + * + * @param opts.dimension - the selector of the dimension being changed + * @param opts.operation - the desired operation ID for the dimension + * @param opts.field - the desired field for the dimension + * @param layerIndex - the index of the layer + */ + async configureReference(opts: { + operation?: string; + field?: string; + isPreviousIncompatible?: boolean; + }) { + if (opts.operation) { + const target = await testSubjects.find('indexPattern-subFunction-selection-row'); + await comboBox.openOptionsList(target); + await comboBox.setElement(target, opts.operation); + } + + if (opts.field) { + const target = await testSubjects.find('indexPattern-reference-field-selection-row'); + await comboBox.openOptionsList(target); + await comboBox.setElement(target, opts.field); + } + }, + /** * Drags field to workspace * @@ -327,6 +353,19 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont }); }, + /** Counts the visible warnings in the config panel */ + async getErrorCount() { + const moreButton = await testSubjects.exists('configuration-failure-more-errors'); + if (moreButton) { + await retry.try(async () => { + await testSubjects.click('configuration-failure-more-errors'); + await testSubjects.missingOrFail('configuration-failure-more-errors'); + }); + } + const errors = await testSubjects.findAll('configuration-failure-error'); + return errors?.length ?? 0; + }, + /** * Checks a specific subvisualization in the chart switcher for a "data loss" indicator * From 9a2858105e7f91c836412e2deb35f0f6bb7c6191 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 22 Dec 2020 09:44:47 -0600 Subject: [PATCH 042/100] [build] Migrate grunt ensureAllTestsInCIGroup to script (#85873) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .ci/jobs.yml | 2 +- scripts/ensure_all_tests_in_ci_group.js | 21 ++++++++ .../dev/run_ensure_all_tests_in_ci_group.js | 49 +++++++++---------- test/scripts/jenkins_build_kibana.sh | 2 +- 4 files changed, 45 insertions(+), 29 deletions(-) create mode 100644 scripts/ensure_all_tests_in_ci_group.js rename tasks/function_test_groups.js => src/dev/run_ensure_all_tests_in_ci_group.js (65%) diff --git a/.ci/jobs.yml b/.ci/jobs.yml index d4ec8a3d5a6997..f62ec9510d2d4e 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -1,4 +1,4 @@ -# This file is needed by functionalTests:ensureAllTestsInCiGroup for the list of ciGroups. That must be changed before this file can be removed +# This file is needed by node scripts/ensure_all_tests_in_ci_group for the list of ciGroups. That must be changed before this file can be removed JOB: - kibana-intake diff --git a/scripts/ensure_all_tests_in_ci_group.js b/scripts/ensure_all_tests_in_ci_group.js new file mode 100644 index 00000000000000..d189aac8f62e86 --- /dev/null +++ b/scripts/ensure_all_tests_in_ci_group.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_ensure_all_tests_in_ci_group'); diff --git a/tasks/function_test_groups.js b/src/dev/run_ensure_all_tests_in_ci_group.js similarity index 65% rename from tasks/function_test_groups.js rename to src/dev/run_ensure_all_tests_in_ci_group.js index 0b456dcb0da13b..b5d36c405cbbbe 100644 --- a/tasks/function_test_groups.js +++ b/src/dev/run_ensure_all_tests_in_ci_group.js @@ -21,32 +21,28 @@ import { readFileSync } from 'fs'; import { resolve } from 'path'; import execa from 'execa'; -import grunt from 'grunt'; import { safeLoad } from 'js-yaml'; -const JOBS_YAML = readFileSync(resolve(__dirname, '../.ci/jobs.yml'), 'utf8'); +import { run } from '@kbn/dev-utils'; + +const JOBS_YAML = readFileSync(resolve(__dirname, '../../.ci/jobs.yml'), 'utf8'); const TEST_TAGS = safeLoad(JOBS_YAML) .JOB.filter((id) => id.startsWith('kibana-ciGroup')) .map((id) => id.replace(/^kibana-/, '')); -grunt.registerTask( - 'functionalTests:ensureAllTestsInCiGroup', - 'Check that all of the functional tests are in a CI group', - async function () { - const done = this.async(); - - try { - const result = await execa(process.execPath, [ - 'scripts/functional_test_runner', - ...TEST_TAGS.map((tag) => `--include-tag=${tag}`), - '--config', - 'test/functional/config.js', - '--test-stats', - ]); - const stats = JSON.parse(result.stderr); - - if (stats.excludedTests.length > 0) { - grunt.fail.fatal(` +run(async ({ log }) => { + try { + const result = await execa(process.execPath, [ + 'scripts/functional_test_runner', + ...TEST_TAGS.map((tag) => `--include-tag=${tag}`), + '--config', + 'test/functional/config.js', + '--test-stats', + ]); + const stats = JSON.parse(result.stderr); + + if (stats.excludedTests.length > 0) { + log.error(` ${stats.excludedTests.length} tests are excluded by the ciGroup tags, make sure that all test suites have a "ciGroup{X}" tag and that "tasks/functional_test_groups.js" knows about the tag that you are using. @@ -55,12 +51,11 @@ grunt.registerTask( - ${stats.excludedTests.join('\n - ')} `); - return; - } - - done(); - } catch (error) { - grunt.fail.fatal(error.stack); + process.exitCode = 1; + return; } + } catch (error) { + log.error(error.stack); + process.exitCode = 1; } -); +}); diff --git a/test/scripts/jenkins_build_kibana.sh b/test/scripts/jenkins_build_kibana.sh index f449986713f97d..6184708ea3fc62 100755 --- a/test/scripts/jenkins_build_kibana.sh +++ b/test/scripts/jenkins_build_kibana.sh @@ -10,7 +10,7 @@ fi export KBN_NP_PLUGINS_BUILT=true echo " -> Ensuring all functional tests are in a ciGroup" -yarn run grunt functionalTests:ensureAllTestsInCiGroup; +node scripts/ensure_all_tests_in_ci_group; # Do not build kibana for code coverage run if [[ -z "$CODE_COVERAGE" ]] ; then From 3aeb344b7c79c07ba1d4f900f0e8032ba78e967a Mon Sep 17 00:00:00 2001 From: Nick Partridge Date: Tue, 22 Dec 2020 10:06:37 -0600 Subject: [PATCH 043/100] Enable new chart library setting (#86538) --- src/plugins/vis_type_vislib/public/plugin.ts | 2 +- src/plugins/vis_type_xy/public/plugin.ts | 2 +- src/plugins/vis_type_xy/server/plugin.ts | 2 +- test/functional/config.js | 1 + x-pack/test/functional/config.js | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/vis_type_vislib/public/plugin.ts b/src/plugins/vis_type_vislib/public/plugin.ts index 36a184d3da5072..0f849c18332309 100644 --- a/src/plugins/vis_type_vislib/public/plugin.ts +++ b/src/plugins/vis_type_vislib/public/plugin.ts @@ -61,7 +61,7 @@ export class VisTypeVislibPlugin core: VisTypeVislibCoreSetup, { expressions, visualizations, charts }: VisTypeVislibPluginSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) { + if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { // Register only non-replaced vis types convertedTypeDefinitions.forEach(visualizations.createBaseVisualization); visualizations.createBaseVisualization(pieVisTypeDefinition); diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts index 7425c5f7248aca..07084de67fd219 100644 --- a/src/plugins/vis_type_xy/public/plugin.ts +++ b/src/plugins/vis_type_xy/public/plugin.ts @@ -71,7 +71,7 @@ export class VisTypeXyPlugin core: VisTypeXyCoreSetup, { expressions, visualizations, charts }: VisTypeXyPluginSetupDependencies ) { - if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, true)) { + if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { setUISettings(core.uiSettings); setThemeService(charts.theme); setColorsService(charts.legacyColors); diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts index b5999535064aaa..fafc4052a88fa4 100644 --- a/src/plugins/vis_type_xy/server/plugin.ts +++ b/src/plugins/vis_type_xy/server/plugin.ts @@ -31,7 +31,7 @@ export const uiSettingsConfig: Record> = { name: i18n.translate('visTypeXy.advancedSettings.visualization.legacyChartsLibrary.name', { defaultMessage: 'Legacy charts library', }), - value: true, + value: false, description: i18n.translate( 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description', { diff --git a/test/functional/config.js b/test/functional/config.js index 5bef9896d17cc8..ea6e75b174b4c2 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -59,6 +59,7 @@ export default async function ({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyChartsLibrary': true, }, }, diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 814f943a68b057..1815942a06a9af 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -95,6 +95,7 @@ export default async function ({ readConfigFile }) { defaults: { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', + 'visualization:visualize:legacyChartsLibrary': true, }, }, // the apps section defines the urls that From ce546adb046a390f234f429e517b61f469457b48 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 22 Dec 2020 08:14:07 -0800 Subject: [PATCH 044/100] [ML] Use doc links service for more ML pages (#86405) --- .../feature_importance/decision_path_popover.tsx | 7 ++----- .../advanced_step/advanced_step_form.tsx | 14 ++++---------- .../classification_exploration/evaluate_panel.tsx | 4 ++-- .../regression_exploration/evaluate_panel.tsx | 4 ++-- .../feature_importance_summary.tsx | 4 ++-- .../components/calendars/description.tsx | 3 +-- .../components/custom_urls/description.tsx | 3 +-- .../components/summary_count_field/description.tsx | 3 +-- 8 files changed, 15 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_popover.tsx b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_popover.tsx index 40fcd1a6d316e6..46982c7553c308 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_popover.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_popover.tsx @@ -54,7 +54,7 @@ export const DecisionPathPopover: FC = ({ const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const docLink = docLinks.links.ml.featureImportance; if (featureImportance.length < 2) { return ; @@ -106,10 +106,7 @@ export const DecisionPathPopover: FC = ({ values={{ predictionFieldName, linkedFeatureImportanceValues: ( - + ); -function getZeroClassesMessage(elasaticUrl: string, version: string) { +function getZeroClassesMessage(elasticUrl: string) { return ( + {i18n.translate('xpack.ml.dataframe.analytics.create.aucRocLabel', { defaultMessage: 'AUC ROC', })} @@ -136,7 +132,7 @@ export const AdvancedStepForm: FC = ({ const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const classAucRocDocLink = docLinks.links.ml.classificationAucRoc; const { setEstimatedModelMemoryLimit, setFormState } = actions; const { form, isJobCreated, estimatedModelMemoryLimit } = state; @@ -422,9 +418,7 @@ export const AdvancedStepForm: FC = ({ helpText={getTopClassesHelpText(selectedNumTopClasses)} isInvalid={selectedNumTopClasses === 0 || selectedNumTopClassesIsInvalid} error={[ - ...(selectedNumTopClasses === 0 - ? [getZeroClassesMessage(ELASTIC_WEBSITE_URL, DOC_LINK_VERSION)] - : []), + ...(selectedNumTopClasses === 0 ? [getZeroClassesMessage(classAucRocDocLink)] : []), ...(selectedNumTopClassesIsInvalid ? [numClassesTypeMessage] : []), ]} > 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 469ded8dfe4aef..8009cda455e519 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 @@ -272,7 +272,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, se return {columnId === ACTUAL_CLASS_ID ? cellValue : accuracy}; }; - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const docLink = docLinks.links.ml.classificationEvaluation; const showTrailingColumns = columnsData.length > MAX_COLUMNS; const extraColumns = columnsData.length - MAX_COLUMNS; @@ -300,7 +300,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, se iconType="help" iconSide="left" color="primary" - href={`${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-classification`} + href={docLink} > {i18n.translate( 'xpack.ml.dataframe.analytics.classificationExploration.classificationDocsLink', diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx index 123c84d59db2d8..91d467a98ae35c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/evaluate_panel.tsx @@ -61,7 +61,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const docLink = docLinks.links.ml.regressionEvaluation; const [trainingEval, setTrainingEval] = useState(defaultEval); const [generalizationEval, setGeneralizationEval] = useState(defaultEval); const [isLoadingTraining, setIsLoadingTraining] = useState(false); @@ -236,7 +236,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) iconType="help" iconSide="left" color="primary" - href={`${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-dfanalytics-evaluate.html#ml-dfanalytics-regression-evaluation`} + href={docLink} > {i18n.translate( 'xpack.ml.dataframe.analytics.regressionExploration.regressionDocsLink', diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx index 96b2cc7da23092..53802098424e57 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx @@ -191,7 +191,7 @@ export const FeatureImportanceSummaryPanel: FC Number(d.toPrecision(3)).toString(), []); // do not expand by default if no feature importance data @@ -256,7 +256,7 @@ export const FeatureImportanceSummaryPanel: FC { const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - const docsUrl = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-calendars.html`; + const docsUrl = docLinks.links.ml.calendars; const title = i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.additionalSection.calendarsSelection.title', { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/description.tsx index 40974418b09b17..c1b595d6b25797 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/description.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/components/custom_urls/description.tsx @@ -14,8 +14,7 @@ export const Description: FC = memo(({ children }) => { const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - const docsUrl = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-url.html`; + const docsUrl = docLinks.links.ml.customUrls; const title = i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.additionalSection.customUrls.title', { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/summary_count_field/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/summary_count_field/description.tsx index a09b6540e101fd..f50308f060c894 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/summary_count_field/description.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/summary_count_field/description.tsx @@ -22,8 +22,7 @@ export const Description: FC = memo(({ children, validation }) => { const { services: { docLinks }, } = useMlKibana(); - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - const docsUrl = `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/ml-configuring-aggregation.html`; + const docsUrl = docLinks.links.ml.aggregations; return ( {title}} From 9c74a1090ec5320ffeb4cda0cecf6f25fbc6ef0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Tue, 22 Dec 2020 17:33:36 +0100 Subject: [PATCH 045/100] [Security Solution] Fix artifacts in Events table (#86767) --- .../data_driven_columns/__snapshots__/index.test.tsx.snap | 7 ------- .../timeline/body/data_driven_columns/index.tsx | 8 ++++---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap index d112a665d77c00..8f514ca49e8480 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/__snapshots__/index.test.tsx.snap @@ -44,7 +44,6 @@ exports[`Columns it renders the expected columns 1`] = ` truncate={true} /> - 0 - 0 - 0 - 0 - 0 - 0 - 0 `; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx index c497d4f459f000..0242a2a0ff0914 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx @@ -117,17 +117,17 @@ export const DataDrivenColumns = React.memo( })} - {hasRowRenderers && ( + {hasRowRenderers ? (

    {i18n.EVENT_HAS_AN_EVENT_RENDERER(ariaRowindex)}

    - )} + ) : null} - {notesCount && ( + {notesCount ? (

    {i18n.EVENT_HAS_NOTES({ row: ariaRowindex, notesCount })}

    - )} + ) : null} ))} From 57a72a78f7ce7054d148ccf5e95f6852b376e5f1 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 22 Dec 2020 17:35:27 +0100 Subject: [PATCH 046/100] [Lens] Configurable color syncing (#86180) --- ...ugins-embeddable-public.embeddableinput.md | 1 + ...c.expressionrenderhandler._constructor_.md | 4 +- ...ressions-public.expressionrenderhandler.md | 2 +- ...ressions-public.iexpressionloaderparams.md | 1 + ...blic.iexpressionloaderparams.synccolors.md | 11 + ...reterrenderhandlers.issynccolorsenabled.md | 11 + ...sions-public.iinterpreterrenderhandlers.md | 1 + ...reterrenderhandlers.issynccolorsenabled.md | 11 + ...sions-server.iinterpreterrenderhandlers.md | 1 + docs/user/dashboard/edit-dashboards.asciidoc | 15 + .../services/mapped_colors/mapped_colors.ts | 6 +- .../services/palettes/palettes.test.tsx | 410 +++++++++++--- .../public/services/palettes/palettes.tsx | 39 +- .../charts/public/services/palettes/types.ts | 5 + .../application/dashboard_app_functions.ts | 1 + .../application/dashboard_state.test.ts | 1 + .../application/dashboard_state_manager.ts | 9 + .../embeddable/dashboard_container.tsx | 4 + .../dashboard_container_factory.tsx | 1 + .../application/top_nav/dashboard_top_nav.tsx | 4 + .../public/application/top_nav/options.tsx | 21 + .../top_nav/show_options_popover.tsx | 6 + src/plugins/dashboard/public/types.ts | 1 + src/plugins/embeddable/common/types.ts | 5 + src/plugins/embeddable/public/public.api.md | 1 + .../common/expression_renderers/types.ts | 1 + src/plugins/expressions/public/loader.ts | 1 + src/plugins/expressions/public/public.api.md | 6 +- .../public/react_expression_renderer.tsx | 7 +- src/plugins/expressions/public/render.ts | 5 + src/plugins/expressions/public/types/index.ts | 1 + src/plugins/expressions/server/server.api.md | 2 + .../renderers/__stories__/render.tsx | 1 + .../canvas/public/lib/create_handlers.ts | 3 + .../embeddable/embeddable.tsx | 1 + .../embeddable/expression_wrapper.tsx | 3 + .../public/pie_visualization/expression.tsx | 1 + .../render_function.test.tsx | 2 + .../pie_visualization/render_function.tsx | 4 +- .../xy_visualization/expression.test.tsx | 518 +++--------------- .../public/xy_visualization/expression.tsx | 6 +- 41 files changed, 574 insertions(+), 560 deletions(-) create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md create mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md create mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md index 0f14215ff13090..07ede291e33d2b 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.embeddableinput.md @@ -17,5 +17,6 @@ export declare type EmbeddableInput = { disabledActions?: string[]; disableTriggers?: boolean; searchSessionId?: string; + syncColors?: boolean; }; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md index 1565202e84674f..9dfad91c336791 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `ExpressionRenderHandler` class Signature: ```typescript -constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActions, }?: ExpressionRenderHandlerParams); +constructor(element: HTMLElement, { onRenderError, renderMode, syncColors, hasCompatibleActions, }?: ExpressionRenderHandlerParams); ``` ## Parameters @@ -17,5 +17,5 @@ constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActi | Parameter | Type | Description | | --- | --- | --- | | element | HTMLElement | | -| { onRenderError, renderMode, hasCompatibleActions, } | ExpressionRenderHandlerParams | | +| { onRenderError, renderMode, syncColors, hasCompatibleActions, } | ExpressionRenderHandlerParams | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md index d65c06bdaed83a..1a7050f3ffd4eb 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderhandler.md @@ -14,7 +14,7 @@ export declare class ExpressionRenderHandler | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(element, { onRenderError, renderMode, hasCompatibleActions, })](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) | | Constructs a new instance of the ExpressionRenderHandler class | +| [(constructor)(element, { onRenderError, renderMode, syncColors, hasCompatibleActions, })](./kibana-plugin-plugins-expressions-public.expressionrenderhandler._constructor_.md) | | Constructs a new instance of the ExpressionRenderHandler class | ## Properties diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md index 22a73fff039e6b..4ef1225ae0d7e3 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md @@ -25,6 +25,7 @@ export interface IExpressionLoaderParams | [renderMode](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.rendermode.md) | RenderMode | | | [searchContext](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchcontext.md) | SerializableState | | | [searchSessionId](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.searchsessionid.md) | string | | +| [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) | boolean | | | [uiState](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.uistate.md) | unknown | | | [variables](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.variables.md) | Record<string, any> | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md new file mode 100644 index 00000000000000..619f54ad88ef24 --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) > [syncColors](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.synccolors.md) + +## IExpressionLoaderParams.syncColors property + +Signature: + +```typescript +syncColors?: boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md new file mode 100644 index 00000000000000..6cdc796bf464be --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) > [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md) + +## IInterpreterRenderHandlers.isSyncColorsEnabled property + +Signature: + +```typescript +isSyncColorsEnabled: () => boolean; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md index c22c8bc6b62456..0b39a9b4b3ea2d 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md @@ -18,6 +18,7 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [getRenderMode](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.getrendermode.md) | () => RenderMode | | | [hasCompatibleActions](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean> | | +| [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.issynccolorsenabled.md) | () => boolean | | | [onDestroy](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.reload.md) | () => void | | | [uiState](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.uistate.md) | unknown | This uiState interface is actually PersistedState from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md new file mode 100644 index 00000000000000..71a7e020e65a5a --- /dev/null +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) > [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md) + +## IInterpreterRenderHandlers.isSyncColorsEnabled property + +Signature: + +```typescript +isSyncColorsEnabled: () => boolean; +``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md index 547608f40e6aa2..831c9023c7e483 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md @@ -18,6 +18,7 @@ export interface IInterpreterRenderHandlers | [event](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.event.md) | (event: any) => void | | | [getRenderMode](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.getrendermode.md) | () => RenderMode | | | [hasCompatibleActions](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.hascompatibleactions.md) | (event: any) => Promise<boolean> | | +| [isSyncColorsEnabled](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.issynccolorsenabled.md) | () => boolean | | | [onDestroy](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.ondestroy.md) | (fn: () => void) => void | | | [reload](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.reload.md) | () => void | | | [uiState](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.uistate.md) | unknown | This uiState interface is actually PersistedState from the visualizations plugin, but expressions cannot know about vis or it creates a mess of circular dependencies. Downstream consumers of the uiState handler will need to cast for now. | diff --git a/docs/user/dashboard/edit-dashboards.asciidoc b/docs/user/dashboard/edit-dashboards.asciidoc index 7b712b355b315a..d7f7dc2d65c852 100644 --- a/docs/user/dashboard/edit-dashboards.asciidoc +++ b/docs/user/dashboard/edit-dashboards.asciidoc @@ -81,6 +81,21 @@ Put the dashboard in *Edit* mode, then use the following options: * To delete, open the panel menu, then select *Delete from dashboard*. When you delete a panel from the dashboard, the visualization or saved search from the panel is still available in Kibana. +[float] +[[sync-colors]] +=== Synchronize colors + +By default, dashboard panels that share a non-gradient based color palette will synchronize their color assignment to improve readability. +Color assignment is based on the series name, and the total number of colors is based on the number of unique series names. + +The color synchronizing logic can make the dashboard less readable when there are too many unique series names. It is possible to disable the synchronization behavior: + +. Put the dashboard in *Edit* mode. + +. Click the "Options" button in the top navigation bar. + +. Disable "Sync color palettes across panels". + [float] [[clone-panels]] === Clone panels diff --git a/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts b/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts index 2934d4208d22ce..7848cdd3f3140d 100644 --- a/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts +++ b/src/plugins/charts/public/services/mapped_colors/mapped_colors.ts @@ -37,15 +37,15 @@ export class MappedColors { private _mapping: any; constructor( - private uiSettings: CoreSetup['uiSettings'], + private uiSettings?: CoreSetup['uiSettings'], private colorPaletteFn: (num: number) => string[] = createColorPalette ) { this._oldMap = {}; this._mapping = {}; } - private getConfigColorMapping() { - return _.mapValues(this.uiSettings.get(COLOR_MAPPING_SETTING), standardizeColor); + private getConfigColorMapping(): Record { + return _.mapValues(this.uiSettings?.get(COLOR_MAPPING_SETTING) || {}, standardizeColor); } public get oldMap(): any { diff --git a/src/plugins/charts/public/services/palettes/palettes.test.tsx b/src/plugins/charts/public/services/palettes/palettes.test.tsx index 5d9337f1ee683f..7356f13fddf9f6 100644 --- a/src/plugins/charts/public/services/palettes/palettes.test.tsx +++ b/src/plugins/charts/public/services/palettes/palettes.test.tsx @@ -18,9 +18,11 @@ */ import { coreMock } from '../../../../../core/public/mocks'; +import { createColorPalette as createLegacyColorPalette } from '../../../../../../src/plugins/charts/public'; import { PaletteDefinition } from './types'; import { buildPalettes } from './palettes'; import { colorsServiceMock } from '../legacy_colors/mock'; +import { euiPaletteColorBlind, euiPaletteColorBlindBehindText } from '@elastic/eui'; describe('palettes', () => { const palettes: Record = buildPalettes( @@ -28,79 +30,257 @@ describe('palettes', () => { colorsServiceMock ); describe('default palette', () => { - it('should return different colors based on behind text flag', () => { - const palette = palettes.default; + describe('syncColors: false', () => { + it('should return different colors based on behind text flag', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - ]); - const color2 = palette.getColor( - [ + const color1 = palette.getColor([ { name: 'abc', rankAtDepth: 0, totalSeriesAtDepth: 5, }, - ], - { - behindText: true, - } - ); - expect(color1).not.toEqual(color2); - }); + ]); + const color2 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + behindText: true, + } + ); + expect(color1).not.toEqual(color2); + }); - it('should return different colors based on rank at current series', () => { - const palette = palettes.default; + it('should return different colors based on rank at current series', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - ]); - const color2 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 1, - totalSeriesAtDepth: 5, - }, - ]); - expect(color1).not.toEqual(color2); + const color1 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ]); + const color2 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 1, + totalSeriesAtDepth: 5, + }, + ]); + expect(color1).not.toEqual(color2); + }); + + it('should return the same color for different positions on outer series layers', () => { + const palette = palettes.default; + + const color1 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ]); + const color2 = palette.getColor([ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ]); + expect(color1).toEqual(color2); + }); }); - it('should return the same color for different positions on outer series layers', () => { - const palette = palettes.default; + describe('syncColors: true', () => { + it('should return different colors based on behind text flag', () => { + const palette = palettes.default; - const color1 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - { - name: 'def', - rankAtDepth: 0, - totalSeriesAtDepth: 2, - }, - ]); - const color2 = palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 5, - }, - { - name: 'ghj', - rankAtDepth: 1, - totalSeriesAtDepth: 1, - }, - ]); - expect(color1).toEqual(color2); + const color1 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + behindText: true, + syncColors: true, + } + ); + expect(color1).not.toEqual(color2); + }); + + it('should return different colors for different keys', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + expect(color1).not.toEqual(color2); + }); + + it('should return the same color for the same key, irregardless of rank', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'hij', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'hij', + rankAtDepth: 5, + totalSeriesAtDepth: 5, + }, + ], + { + syncColors: true, + } + ); + expect(color1).toEqual(color2); + }); + + it('should return the same color for different positions on outer series layers', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 3, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ], + { + syncColors: true, + } + ); + expect(color1).toEqual(color2); + }); + + it('should return the same index of the behind text palette for same key', () => { + const palette = palettes.default; + + const color1 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 0, + totalSeriesAtDepth: 5, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 2, + }, + ], + { + syncColors: true, + } + ); + const color2 = palette.getColor( + [ + { + name: 'klm', + rankAtDepth: 3, + totalSeriesAtDepth: 5, + }, + { + name: 'ghj', + rankAtDepth: 1, + totalSeriesAtDepth: 1, + }, + ], + { + syncColors: true, + behindText: true, + } + ); + const color1Index = euiPaletteColorBlind({ rotations: 2 }).indexOf(color1!); + const color2Index = euiPaletteColorBlindBehindText({ rotations: 2 }).indexOf(color2!); + expect(color1Index).toEqual(color2Index); + }); }); }); @@ -136,35 +316,87 @@ describe('palettes', () => { (colorsServiceMock.mappedColors.get as jest.Mock).mockClear(); }); - it('should query legacy color service', () => { - palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - ]); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + describe('syncColors: false', () => { + it('should not query legacy color service', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: false, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).not.toHaveBeenCalled(); + expect(colorsServiceMock.mappedColors.get).not.toHaveBeenCalled(); + }); + + it('should return a color from the legacy palette based on position of first series', () => { + const result = palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 2, + totalSeriesAtDepth: 10, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: false, + } + ); + expect(result).toEqual(createLegacyColorPalette(20)[2]); + }); }); - it('should always use root series', () => { - palette.getColor([ - { - name: 'abc', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - { - name: 'def', - rankAtDepth: 0, - totalSeriesAtDepth: 10, - }, - ]); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledTimes(1); - expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledTimes(1); - expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + describe('syncColors: true', () => { + it('should query legacy color service', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: true, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + }); + + it('should always use root series', () => { + palette.getColor( + [ + { + name: 'abc', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + { + name: 'def', + rankAtDepth: 0, + totalSeriesAtDepth: 10, + }, + ], + { + syncColors: true, + } + ); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledTimes(1); + expect(colorsServiceMock.mappedColors.mapKeys).toHaveBeenCalledWith(['abc']); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledTimes(1); + expect(colorsServiceMock.mappedColors.get).toHaveBeenCalledWith('abc'); + }); }); }); diff --git a/src/plugins/charts/public/services/palettes/palettes.tsx b/src/plugins/charts/public/services/palettes/palettes.tsx index c1fd7c3cc739fd..ffb237904b36ce 100644 --- a/src/plugins/charts/public/services/palettes/palettes.tsx +++ b/src/plugins/charts/public/services/palettes/palettes.tsx @@ -28,26 +28,45 @@ import { euiPaletteNegative, euiPalettePositive, euiPaletteWarm, - euiPaletteColorBlindBehindText, euiPaletteForStatus, euiPaletteForTemperature, euiPaletteComplimentary, + euiPaletteColorBlindBehindText, } from '@elastic/eui'; -import { ChartsPluginSetup } from '../../../../../../src/plugins/charts/public'; +import { flatten, zip } from 'lodash'; +import { + ChartsPluginSetup, + createColorPalette as createLegacyColorPalette, +} from '../../../../../../src/plugins/charts/public'; import { lightenColor } from './lighten_color'; import { ChartColorConfiguration, PaletteDefinition, SeriesLayer } from './types'; import { LegacyColorsService } from '../legacy_colors'; +import { MappedColors } from '../mapped_colors'; function buildRoundRobinCategoricalWithMappedColors(): Omit { const colors = euiPaletteColorBlind({ rotations: 2 }); const behindTextColors = euiPaletteColorBlindBehindText({ rotations: 2 }); + const behindTextColorMap: Record = Object.fromEntries( + zip(colors, behindTextColors) + ); + const mappedColors = new MappedColors(undefined, (num: number) => { + return flatten(new Array(Math.ceil(num / 10)).fill(colors)).map((color) => color.toLowerCase()); + }); function getColor( series: SeriesLayer[], chartConfiguration: ChartColorConfiguration = { behindText: false } ) { - const outputColor = chartConfiguration.behindText - ? behindTextColors[series[0].rankAtDepth % behindTextColors.length] - : colors[series[0].rankAtDepth % colors.length]; + let outputColor: string; + if (chartConfiguration.syncColors) { + const colorKey = series[0].name; + mappedColors.mapKeys([colorKey]); + const mappedColor = mappedColors.get(colorKey); + outputColor = chartConfiguration.behindText ? behindTextColorMap[mappedColor] : mappedColor; + } else { + outputColor = chartConfiguration.behindText + ? behindTextColors[series[0].rankAtDepth % behindTextColors.length] + : colors[series[0].rankAtDepth % colors.length]; + } if (!chartConfiguration.maxDepth || chartConfiguration.maxDepth === 1) { return outputColor; @@ -115,9 +134,15 @@ function buildGradient( function buildSyncedKibanaPalette( colors: ChartsPluginSetup['legacyColors'] ): Omit { + const staticColors = createLegacyColorPalette(20); function getColor(series: SeriesLayer[], chartConfiguration: ChartColorConfiguration = {}) { - colors.mappedColors.mapKeys([series[0].name]); - const outputColor = colors.mappedColors.get(series[0].name); + let outputColor: string; + if (chartConfiguration.syncColors) { + colors.mappedColors.mapKeys([series[0].name]); + outputColor = colors.mappedColors.get(series[0].name); + } else { + outputColor = staticColors[series[0].rankAtDepth % staticColors.length]; + } if (!chartConfiguration.maxDepth || chartConfiguration.maxDepth === 1) { return outputColor; diff --git a/src/plugins/charts/public/services/palettes/types.ts b/src/plugins/charts/public/services/palettes/types.ts index f92bcb4bd08246..15989578518f5b 100644 --- a/src/plugins/charts/public/services/palettes/types.ts +++ b/src/plugins/charts/public/services/palettes/types.ts @@ -55,6 +55,11 @@ export interface ChartColorConfiguration { * adjust colors for better a11y. Might be ignored depending on the palette. */ behindText?: boolean; + /** + * Flag whether a color assignment to a given key should be remembered and re-used the next time the key shows up. + * This setting might be ignored based on the palette. + */ + syncColors?: boolean; } /** diff --git a/src/plugins/dashboard/public/application/dashboard_app_functions.ts b/src/plugins/dashboard/public/application/dashboard_app_functions.ts index 0381fdb2e55b55..af7a485296ea04 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_functions.ts +++ b/src/plugins/dashboard/public/application/dashboard_app_functions.ts @@ -151,6 +151,7 @@ export const getDashboardContainerInput = ({ description: dashboardStateManager.getDescription(), id: dashboardStateManager.savedDashboard.id || '', useMargins: dashboardStateManager.getUseMargins(), + syncColors: dashboardStateManager.getSyncColors(), viewMode: dashboardStateManager.getViewMode(), filters: query.filterManager.getFilters(), query: dashboardStateManager.getQuery(), diff --git a/src/plugins/dashboard/public/application/dashboard_state.test.ts b/src/plugins/dashboard/public/application/dashboard_state.test.ts index b07ea762f35e0c..f31ed30f8eb80c 100644 --- a/src/plugins/dashboard/public/application/dashboard_state.test.ts +++ b/src/plugins/dashboard/public/application/dashboard_state.test.ts @@ -68,6 +68,7 @@ describe('DashboardState', function () { query: {} as DashboardContainerInput['query'], timeRange: {} as DashboardContainerInput['timeRange'], useMargins: true, + syncColors: false, title: 'ultra awesome test dashboard', isFullScreenMode: false, panels: {} as DashboardContainerInput['panels'], diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts index daa0bbdfc9f8a9..dfcbfcafd3db1c 100644 --- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts @@ -404,6 +404,15 @@ export class DashboardStateManager { this.stateContainer.transitions.setOption('useMargins', useMargins); } + public getSyncColors() { + // Existing dashboards that don't define this should default to true. + return this.appState.options.syncColors === undefined ? true : this.appState.options.syncColors; + } + + public setSyncColors(syncColors: boolean) { + this.stateContainer.transitions.setOption('syncColors', syncColors); + } + public getHidePanelTitles() { return this.appState.options.hidePanelTitles; } diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index 01b4e81fc484c9..a3b67ede9f3f9b 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -59,6 +59,7 @@ export interface DashboardContainerInput extends ContainerInput { timeRange: TimeRange; description?: string; useMargins: boolean; + syncColors?: boolean; viewMode: ViewMode; filters: Filter[]; title: string; @@ -93,6 +94,7 @@ export interface InheritedChildInput extends IndexSignature { hidePanelTitles?: boolean; id: string; searchSessionId?: string; + syncColors?: boolean; } export type DashboardReactContextValue = KibanaReactContextValue; @@ -269,6 +271,7 @@ export class DashboardContainer extends Container { dashboardStateManager.setUseMargins(isChecked); }, + syncColors: dashboardStateManager.getSyncColors(), + onSyncColorsChange: (isChecked: boolean) => { + dashboardStateManager.setSyncColors(isChecked); + }, hidePanelTitles: dashboardStateManager.getHidePanelTitles(), onHidePanelTitlesChange: (isChecked: boolean) => { dashboardStateManager.setHidePanelTitles(isChecked); diff --git a/src/plugins/dashboard/public/application/top_nav/options.tsx b/src/plugins/dashboard/public/application/top_nav/options.tsx index 3398696ff40dbf..86409cdeba74fa 100644 --- a/src/plugins/dashboard/public/application/top_nav/options.tsx +++ b/src/plugins/dashboard/public/application/top_nav/options.tsx @@ -27,17 +27,21 @@ interface Props { onUseMarginsChange: (useMargins: boolean) => void; hidePanelTitles: boolean; onHidePanelTitlesChange: (hideTitles: boolean) => void; + syncColors: boolean; + onSyncColorsChange: (syncColors: boolean) => void; } interface State { useMargins: boolean; hidePanelTitles: boolean; + syncColors: boolean; } export class OptionsMenu extends Component { state = { useMargins: this.props.useMargins, hidePanelTitles: this.props.hidePanelTitles, + syncColors: this.props.syncColors, }; constructor(props: Props) { @@ -56,6 +60,12 @@ export class OptionsMenu extends Component { this.setState({ hidePanelTitles: isChecked }); }; + handleSyncColorsChange = (evt: any) => { + const isChecked = evt.target.checked; + this.props.onSyncColorsChange(isChecked); + this.setState({ syncColors: isChecked }); + }; + render() { return ( @@ -80,6 +90,17 @@ export class OptionsMenu extends Component { data-test-subj="dashboardPanelTitlesCheckbox" /> + + + + ); } diff --git a/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx b/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx index 7c23e4808fbea3..6c519ccad327fd 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_options_popover.tsx @@ -39,10 +39,14 @@ export function showOptionsPopover({ onUseMarginsChange, hidePanelTitles, onHidePanelTitlesChange, + syncColors, + onSyncColorsChange, }: { anchorElement: HTMLElement; useMargins: boolean; onUseMarginsChange: (useMargins: boolean) => void; + syncColors: boolean; + onSyncColorsChange: (syncColors: boolean) => void; hidePanelTitles: boolean; onHidePanelTitlesChange: (hideTitles: boolean) => void; }) { @@ -62,6 +66,8 @@ export function showOptionsPopover({ onUseMarginsChange={onUseMarginsChange} hidePanelTitles={hidePanelTitles} onHidePanelTitlesChange={onHidePanelTitlesChange} + syncColors={syncColors} + onSyncColorsChange={onSyncColorsChange} /> diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts index 7e859a81d9d4d5..882c5b42862637 100644 --- a/src/plugins/dashboard/public/types.ts +++ b/src/plugins/dashboard/public/types.ts @@ -78,6 +78,7 @@ export interface DashboardAppState { options: { hidePanelTitles: boolean; useMargins: boolean; + syncColors?: boolean; }; query: Query | string; filters: Filter[]; diff --git a/src/plugins/embeddable/common/types.ts b/src/plugins/embeddable/common/types.ts index d893724f616d2b..8366d81a657540 100644 --- a/src/plugins/embeddable/common/types.ts +++ b/src/plugins/embeddable/common/types.ts @@ -55,6 +55,11 @@ export type EmbeddableInput = { * Search session id to group searches */ searchSessionId?: string; + + /** + * Flag whether colors should be synced with other panels + */ + syncColors?: boolean; }; export interface PanelState { diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index a401795c498b3f..ab039d15a2ce6e 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -410,6 +410,7 @@ export type EmbeddableInput = { disabledActions?: string[]; disableTriggers?: boolean; searchSessionId?: string; + syncColors?: boolean; }; // Warning: (ae-missing-release-tag) "EmbeddableInstanceConfiguration" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index fca1694747ce2d..3f3cfb9ed2dd9c 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -82,6 +82,7 @@ export interface IInterpreterRenderHandlers { event: (event: any) => void; hasCompatibleActions?: (event: any) => Promise; getRenderMode: () => RenderMode; + isSyncColorsEnabled: () => boolean; /** * This uiState interface is actually `PersistedState` from the visualizations plugin, * but expressions cannot know about vis or it creates a mess of circular dependencies. diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index e9e0fa18af6c37..1cf499ce2635ab 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -64,6 +64,7 @@ export class ExpressionLoader { this.renderHandler = new ExpressionRenderHandler(element, { onRenderError: params && params.onRenderError, renderMode: params?.renderMode, + syncColors: params?.syncColors, hasCompatibleActions: params?.hasCompatibleActions, }); this.render$ = this.renderHandler.render$; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 404df2db019a1e..5c018adc0131b1 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -531,7 +531,7 @@ export interface ExpressionRenderError extends Error { // @public (undocumented) export class ExpressionRenderHandler { // Warning: (ae-forgotten-export) The symbol "ExpressionRenderHandlerParams" needs to be exported by the entry point index.d.ts - constructor(element: HTMLElement, { onRenderError, renderMode, hasCompatibleActions, }?: ExpressionRenderHandlerParams); + constructor(element: HTMLElement, { onRenderError, renderMode, syncColors, hasCompatibleActions, }?: ExpressionRenderHandlerParams); // (undocumented) destroy: () => void; // (undocumented) @@ -903,6 +903,8 @@ export interface IExpressionLoaderParams { // (undocumented) searchSessionId?: string; // (undocumented) + syncColors?: boolean; + // (undocumented) uiState?: unknown; // (undocumented) variables?: Record; @@ -920,6 +922,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) hasCompatibleActions?: (event: any) => Promise; // (undocumented) + isSyncColorsEnabled: () => boolean; + // (undocumented) onDestroy: (fn: () => void) => void; // (undocumented) reload: () => void; diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index eac2371ec66d0c..d19f4821078458 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -170,7 +170,12 @@ export const ReactExpressionRenderer = ({ errorRenderHandlerRef.current = null; }; - }, [hasCustomRenderErrorHandler, onEvent]); + }, [ + hasCustomRenderErrorHandler, + onEvent, + expressionLoaderOptions.renderMode, + expressionLoaderOptions.syncColors, + ]); useEffect(() => { const subscription = reload$?.subscribe(() => { diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 717776a2861b47..e3091b908deca3 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -31,6 +31,7 @@ export type IExpressionRendererExtraHandlers = Record; export interface ExpressionRenderHandlerParams { onRenderError?: RenderErrorHandlerFnType; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise; } @@ -63,6 +64,7 @@ export class ExpressionRenderHandler { { onRenderError, renderMode, + syncColors, hasCompatibleActions = async () => false, }: ExpressionRenderHandlerParams = {} ) { @@ -101,6 +103,9 @@ export class ExpressionRenderHandler { getRenderMode: () => { return renderMode || 'display'; }, + isSyncColorsEnabled: () => { + return syncColors || false; + }, hasCompatibleActions, }; } diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index f37107abbb716c..d709d8ca96bbd2 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -57,6 +57,7 @@ export interface IExpressionLoaderParams { onRenderError?: RenderErrorHandlerFnType; searchSessionId?: string; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions']; } diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index 8b8678371dd838..71199560ee0c76 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -737,6 +737,8 @@ export interface IInterpreterRenderHandlers { // (undocumented) hasCompatibleActions?: (event: any) => Promise; // (undocumented) + isSyncColorsEnabled: () => boolean; + // (undocumented) onDestroy: (fn: () => void) => void; // (undocumented) reload: () => void; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx index 54702f26548393..34f4bb39fbfa70 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/render.tsx @@ -12,6 +12,7 @@ export const defaultHandlers: RendererHandlers = { getElementId: () => 'element-id', getFilter: () => 'filter', getRenderMode: () => 'display', + isSyncColorsEnabled: () => false, onComplete: (fn) => undefined, onEmbeddableDestroyed: action('onEmbeddableDestroyed'), onEmbeddableInputChange: action('onEmbeddableInputChange'), diff --git a/x-pack/plugins/canvas/public/lib/create_handlers.ts b/x-pack/plugins/canvas/public/lib/create_handlers.ts index 9bc4bd5e78fd08..4c9dbd92d3f21f 100644 --- a/x-pack/plugins/canvas/public/lib/create_handlers.ts +++ b/x-pack/plugins/canvas/public/lib/create_handlers.ts @@ -26,6 +26,9 @@ export const createHandlers = (): RendererHandlers => ({ getRenderMode() { return 'display'; }, + isSyncColorsEnabled() { + return false; + }, onComplete(fn: () => void) { this.done = fn; }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index b00760e9664f35..ea7ce99e92cefc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -260,6 +260,7 @@ export class Embeddable handleEvent={this.handleEvent} onData$={this.updateActiveData} renderMode={input.renderMode} + syncColors={input.syncColors} hasCompatibleActions={this.hasCompatibleActions} />, domNode diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx index e2607886a42196..c91ca74b54a4fc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/expression_wrapper.tsx @@ -29,6 +29,7 @@ export interface ExpressionWrapperProps { inspectorAdapters?: Partial | undefined ) => void; renderMode?: RenderMode; + syncColors?: boolean; hasCompatibleActions?: ReactExpressionRendererProps['hasCompatibleActions']; } @@ -41,6 +42,7 @@ export function ExpressionWrapper({ searchSessionId, onData$, renderMode, + syncColors, hasCompatibleActions, }: ExpressionWrapperProps) { return ( @@ -70,6 +72,7 @@ export function ExpressionWrapper({ searchSessionId={searchSessionId} onData$={onData$} renderMode={renderMode} + syncColors={syncColors} renderError={(errorMessage, error) => (
    diff --git a/x-pack/plugins/lens/public/pie_visualization/expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx index 5f18ef7c7f6373..63261d08ff1a48 100644 --- a/x-pack/plugins/lens/public/pie_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/expression.tsx @@ -140,6 +140,7 @@ export const getPieRenderer = (dependencies: { paletteService={dependencies.paletteService} onClickValue={onClickValue} renderMode={handlers.getRenderMode()} + syncColors={handlers.isSyncColorsEnabled()} /> , domNode, diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index 458b1a75c4c171..c6eed36f81ab08 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -71,6 +71,7 @@ describe('PieVisualization component', () => { chartsThemeService, paletteService: chartPluginMock.createPaletteRegistry(), renderMode: 'display' as const, + syncColors: false, }; } @@ -172,6 +173,7 @@ describe('PieVisualization component', () => { { maxDepth: 2, totalSeries: 5, + syncColors: false, behindText: true, }, undefined diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 56ecf57f2dff78..70a98e4cf8589f 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -47,12 +47,13 @@ export function PieComponent( paletteService: PaletteRegistry; onClickValue: (data: LensFilterEvent['data']) => void; renderMode: RenderMode; + syncColors: boolean; } ) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; - const { chartsThemeService, paletteService, onClickValue } = props; + const { chartsThemeService, paletteService, syncColors, onClickValue } = props; const { shape, groups, @@ -145,6 +146,7 @@ export function PieComponent( behindText: categoryDisplay !== 'hide', maxDepth: bucketColumns.length, totalSeries: totalSeriesCount, + syncColors, }, palette.params ); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index 0e2b47410c3f9f..97efd39c02fab8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -18,7 +18,13 @@ import { Fit, } from '@elastic/charts'; import { PaletteOutput } from 'src/plugins/charts/public'; -import { calculateMinInterval, xyChart, XYChart, XYChartProps } from './expression'; +import { + calculateMinInterval, + xyChart, + XYChart, + XYChartProps, + XYChartRenderProps, +} from './expression'; import { LensMultiTable } from '../types'; import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; import React from 'react'; @@ -382,6 +388,7 @@ describe('xy_expression', () => { describe('XYChart component', () => { let getFormatSpy: jest.Mock; let convertSpy: jest.Mock; + let defaultProps: Omit; const dataWithoutFormats: LensMultiTable = { type: 'lens_multitable', @@ -421,26 +428,25 @@ describe('xy_expression', () => { }; const getRenderedComponent = (data: LensMultiTable, args: XYArgs) => { - return shallow( - - ); + return shallow(); }; beforeEach(() => { convertSpy = jest.fn((x) => x); getFormatSpy = jest.fn(); getFormatSpy.mockReturnValue({ convert: convertSpy }); + + defaultProps = { + formatFactory: getFormatSpy, + timeZone: 'UTC', + renderMode: 'display', + chartsThemeService, + paletteService, + minInterval: 50, + onClickValue, + onSelectRange, + syncColors: false, + }; }); test('it renders line', () => { @@ -448,16 +454,9 @@ describe('xy_expression', () => { const component = shallow( ); expect(component).toMatchSnapshot(); @@ -493,6 +492,7 @@ describe('xy_expression', () => { const component = shallow( { ...args, layers: [{ ...args.layers[0], seriesType: 'line', xScaleType: 'time' }], }} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} minInterval={undefined} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` @@ -534,6 +527,7 @@ describe('xy_expression', () => { const component = shallow( { }, }} args={multiLayerArgs} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} - minInterval={50} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); @@ -569,6 +555,7 @@ describe('xy_expression', () => { const component = shallow( { ...args, layers: [{ ...args.layers[0], seriesType: 'line', xScaleType: 'linear' }], }} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} - minInterval={50} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); expect(component.find(Settings).prop('xDomain')).toBeUndefined(); @@ -597,16 +576,9 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -619,16 +591,9 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -641,16 +606,9 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -666,20 +624,7 @@ describe('xy_expression', () => { // send empty data to the chart data.tables.first.rows = []; - const component = shallow( - - ); + const component = shallow(); expect(component.find(BarSeries)).toHaveLength(0); expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); @@ -690,19 +635,12 @@ describe('xy_expression', () => { const wrapper = mountWithIntl( ); @@ -776,19 +714,12 @@ describe('xy_expression', () => { const wrapper = mountWithIntl( ); @@ -806,18 +737,7 @@ describe('xy_expression', () => { const { args, data } = sampleArgs(); const wrapper = mountWithIntl( - + ); expect(wrapper.find(Settings).first().prop('onBrushEnd')).toBeUndefined(); @@ -837,6 +757,7 @@ describe('xy_expression', () => { const wrapper = mountWithIntl( { }, ], }} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} - minInterval={50} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); @@ -892,18 +805,7 @@ describe('xy_expression', () => { const { args, data } = sampleArgs(); const wrapper = mountWithIntl( - + ); expect(wrapper.find(Settings).first().prop('onElementClick')).toBeUndefined(); @@ -913,16 +815,9 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -935,16 +830,9 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -957,19 +845,12 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component).toMatchSnapshot(); @@ -984,6 +865,7 @@ describe('xy_expression', () => { const component = shallow( { }, ], }} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} - minInterval={50} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); @@ -1014,18 +888,7 @@ describe('xy_expression', () => { test('it passes time zone to the series', () => { const { data, args } = sampleArgs(); const component = shallow( - + ); expect(component.find(LineSeries).at(0).prop('timeZone')).toEqual('CEST'); expect(component.find(LineSeries).at(1).prop('timeZone')).toEqual('CEST'); @@ -1041,18 +904,7 @@ describe('xy_expression', () => { }; delete firstLayer.splitAccessor; const component = shallow( - + ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); }); @@ -1062,18 +914,7 @@ describe('xy_expression', () => { const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; delete firstLayer.splitAccessor; const component = shallow( - + ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); @@ -1087,16 +928,9 @@ describe('xy_expression', () => { delete secondLayer.splitAccessor; const component = shallow( ); expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); @@ -1107,6 +941,7 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( { }, ], }} - formatFactory={getFormatSpy} - timeZone="UTC" - renderMode="display" - chartsThemeService={chartsThemeService} - paletteService={paletteService} - minInterval={50} - onClickValue={onClickValue} - onSelectRange={onSelectRange} /> ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); @@ -1136,19 +963,12 @@ describe('xy_expression', () => { const { data, args } = sampleArgs(); const component = shallow( ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); @@ -1541,16 +1361,9 @@ describe('xy_expression', () => { const component = shallow( ); expect(component.find(LineSeries).at(0).prop('xScaleType')).toEqual(ScaleType.Ordinal); @@ -1562,16 +1375,9 @@ describe('xy_expression', () => { const component = shallow( ); expect(component.find(LineSeries).at(0).prop('yScaleType')).toEqual(ScaleType.Sqrt); @@ -1581,20 +1387,7 @@ describe('xy_expression', () => { test('it gets the formatter for the x axis', () => { const { data, args } = sampleArgs(); - shallow( - - ); + shallow(); expect(getFormatSpy).toHaveBeenCalledWith({ id: 'string' }); }); @@ -1604,16 +1397,9 @@ describe('xy_expression', () => { shallow( ); expect(getFormatSpy).toHaveBeenCalledWith({ @@ -1625,20 +1411,7 @@ describe('xy_expression', () => { test('it should pass the formatter function to the axis', () => { const { data, args } = sampleArgs(); - const instance = shallow( - - ); + const instance = shallow(); const tickFormatter = instance.find(Axis).first().prop('tickFormat'); @@ -1661,20 +1434,7 @@ describe('xy_expression', () => { type: 'lens_xy_tickLabelsConfig', }; - const instance = shallow( - - ); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -1695,20 +1455,7 @@ describe('xy_expression', () => { type: 'lens_xy_tickLabelsConfig', }; - const instance = shallow( - - ); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -1729,20 +1476,7 @@ describe('xy_expression', () => { type: 'lens_xy_tickLabelsConfig', }; - const instance = shallow( - - ); + const instance = shallow(); const axisStyle = instance.find(Axis).first().prop('style'); @@ -1763,20 +1497,7 @@ describe('xy_expression', () => { type: 'lens_xy_tickLabelsConfig', }; - const instance = shallow( - - ); + const instance = shallow(); const axisStyle = instance.find(Axis).at(1).prop('style'); @@ -1864,20 +1585,7 @@ describe('xy_expression', () => { ], }; - const component = shallow( - - ); + const component = shallow(); const series = component.find(LineSeries); @@ -1939,20 +1647,7 @@ describe('xy_expression', () => { ], }; - const component = shallow( - - ); + const component = shallow(); const series = component.find(LineSeries); @@ -2012,20 +1707,7 @@ describe('xy_expression', () => { ], }; - const component = shallow( - - ); + const component = shallow(); expect(component.find(Settings).prop('showLegend')).toEqual(true); }); @@ -2035,20 +1717,13 @@ describe('xy_expression', () => { const component = shallow( ); @@ -2060,19 +1735,12 @@ describe('xy_expression', () => { const component = shallow( ); @@ -2084,19 +1752,12 @@ describe('xy_expression', () => { const component = shallow( ); @@ -2123,16 +1784,9 @@ describe('xy_expression', () => { const component = shallow( ); @@ -2150,18 +1804,7 @@ describe('xy_expression', () => { args.layers[0].accessors = ['a']; const component = shallow( - + ); expect(component.find(LineSeries).prop('fit')).toEqual({ type: Fit.None }); @@ -2173,18 +1816,7 @@ describe('xy_expression', () => { args.xTitle = 'My custom x-axis title'; const component = shallow( - + ); expect(component.find(Axis).at(0).prop('title')).toEqual('My custom x-axis title'); @@ -2201,18 +1833,7 @@ describe('xy_expression', () => { }; const component = shallow( - + ); const axisStyle = component.find(Axis).first().prop('style'); @@ -2235,18 +1856,7 @@ describe('xy_expression', () => { }; const component = shallow( - + ); expect(component.find(Axis).at(0).prop('gridLine')).toMatchObject({ diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 790416a6c920d7..399ba705f2f5ea 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -76,7 +76,7 @@ export interface XYRender { value: XYChartProps; } -type XYChartRenderProps = XYChartProps & { +export type XYChartRenderProps = XYChartProps & { chartsThemeService: ChartsPluginSetup['theme']; paletteService: PaletteRegistry; formatFactory: FormatFactory; @@ -85,6 +85,7 @@ type XYChartRenderProps = XYChartProps & { onClickValue: (data: LensFilterEvent['data']) => void; onSelectRange: (data: LensBrushEvent['data']) => void; renderMode: RenderMode; + syncColors: boolean; }; export const xyChart: ExpressionFunctionDefinition< @@ -240,6 +241,7 @@ export const getXyChartRenderer = (dependencies: { onClickValue={onClickValue} onSelectRange={onSelectRange} renderMode={handlers.getRenderMode()} + syncColors={handlers.isSyncColorsEnabled()} /> , domNode, @@ -309,6 +311,7 @@ export function XYChart({ onClickValue, onSelectRange, renderMode, + syncColors, }: XYChartRenderProps) { const { legend, layers, fittingFunction, gridlinesVisibilitySettings, valueLabels } = args; const chartTheme = chartsThemeService.useChartsTheme(); @@ -681,6 +684,7 @@ export function XYChart({ maxDepth: 1, behindText: false, totalSeries: colorAssignment.totalSeriesCount, + syncColors, }, palette.params ); From 57d3349638f0f5fc1fdc550053076deade64ebdb Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 22 Dec 2020 19:39:30 +0300 Subject: [PATCH 047/100] Remove legacy src/plugins/charts -> src/plugins/discover cyclic dependencies (#86758) Part of #84750 --- src/dev/run_find_plugins_with_circular_deps.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts index 9fa2d28b8d5c5d..5afb8df8502df1 100644 --- a/src/dev/run_find_plugins_with_circular_deps.ts +++ b/src/dev/run_find_plugins_with_circular_deps.ts @@ -31,7 +31,6 @@ interface Options { type CircularDepList = Set; const allowedList: CircularDepList = new Set([ - 'src/plugins/charts -> src/plugins/discover', 'src/plugins/vis_default_editor -> src/plugins/visualizations', 'src/plugins/visualizations -> src/plugins/visualize', 'x-pack/plugins/actions -> x-pack/plugins/case', From 1d856c39b5aaaee23cae0f0cbeea1c475af9273f Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 22 Dec 2020 11:41:35 -0500 Subject: [PATCH 048/100] Fix Add From Library Flyout Staying Open (#86698) * Fixed add from library flyout staying open after save or cancel --- ...ns-embeddable-public.openaddpanelflyout.md | 4 +- .../application/top_nav/dashboard_top_nav.tsx | 41 ++++++++++++++----- .../add_panel/open_add_panel_flyout.tsx | 7 ++-- src/plugins/embeddable/public/public.api.md | 3 +- 4 files changed, 39 insertions(+), 16 deletions(-) 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 ce97f79b4beb97..add46463753590 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 @@ -14,7 +14,7 @@ export declare function openAddPanelFlyout(options: { overlays: OverlayStart; notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; -}): Promise; +}): OverlayRef; ``` ## Parameters @@ -25,5 +25,5 @@ export declare function openAddPanelFlyout(options: { Returns: -`Promise` +`OverlayRef` 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 a9ed46160ef77c..87ccbf29b99f7c 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 @@ -57,10 +57,12 @@ import { showOptionsPopover } from './show_options_popover'; import { TopNavIds } from './top_nav_ids'; import { ShowShareModal } from './show_share_modal'; import { PanelToolbar } from './panel_toolbar'; +import { OverlayRef } from '../../../../../core/public'; import { DashboardContainer } from '..'; export interface DashboardTopNavState { chromeIsVisible: boolean; + addPanelOverlay?: OverlayRef; savedQuery?: SavedQuery; } @@ -111,14 +113,17 @@ export function DashboardTopNav({ const addFromLibrary = useCallback(() => { if (!isErrorEmbeddable(dashboardContainer)) { - openAddPanelFlyout({ - embeddable: dashboardContainer, - getAllFactories: embeddable.getEmbeddableFactories, - getFactory: embeddable.getEmbeddableFactory, - notifications: core.notifications, - overlays: core.overlays, - SavedObjectFinder: getSavedObjectFinder(core.savedObjects, uiSettings), - }); + setState((s) => ({ + ...s, + addPanelOverlay: openAddPanelFlyout({ + embeddable: dashboardContainer, + getAllFactories: embeddable.getEmbeddableFactories, + getFactory: embeddable.getEmbeddableFactory, + notifications: core.notifications, + overlays: core.overlays, + SavedObjectFinder: getSavedObjectFinder(core.savedObjects, uiSettings), + }), + })); } }, [ embeddable.getEmbeddableFactories, @@ -139,8 +144,16 @@ export function DashboardTopNav({ await factory.create({} as EmbeddableInput, dashboardContainer); }, [dashboardContainer, embeddable]); + const clearAddPanel = useCallback(() => { + if (state.addPanelOverlay) { + state.addPanelOverlay.close(); + setState((s) => ({ ...s, addPanelOverlay: undefined })); + } + }, [state.addPanelOverlay]); + const onChangeViewMode = useCallback( (newMode: ViewMode) => { + clearAddPanel(); const isPageRefresh = newMode === dashboardStateManager.getViewMode(); const isLeavingEditMode = !isPageRefresh && newMode === ViewMode.VIEW; const willLoseChanges = isLeavingEditMode && dashboardStateManager.getIsDirty(timefilter); @@ -178,7 +191,7 @@ export function DashboardTopNav({ } }); }, - [redirectTo, timefilter, core.overlays, savedDashboard.id, dashboardStateManager] + [redirectTo, timefilter, core.overlays, savedDashboard.id, dashboardStateManager, clearAddPanel] ); /** @@ -301,8 +314,16 @@ export function DashboardTopNav({ showCopyOnSave={lastDashboardId ? true : false} /> ); + clearAddPanel(); showSaveModal(dashboardSaveModal, core.i18n.Context); - }, [save, core.i18n.Context, savedObjectsTagging, dashboardStateManager, lastDashboardId]); + }, [ + save, + clearAddPanel, + lastDashboardId, + core.i18n.Context, + savedObjectsTagging, + dashboardStateManager, + ]); const runClone = useCallback(() => { const currentTitle = dashboardStateManager.getTitle(); 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 867092b78ef7a8..3363f556b418e4 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 @@ -17,20 +17,20 @@ * under the License. */ import React from 'react'; -import { NotificationsStart, OverlayStart } from 'src/core/public'; +import { NotificationsStart, OverlayRef, OverlayStart } from 'src/core/public'; import { EmbeddableStart } from '../../../../../plugin'; import { toMountPoint } from '../../../../../../../kibana_react/public'; import { IContainer } from '../../../../containers'; import { AddPanelFlyout } from './add_panel_flyout'; -export async function openAddPanelFlyout(options: { +export function openAddPanelFlyout(options: { embeddable: IContainer; getFactory: EmbeddableStart['getEmbeddableFactory']; getAllFactories: EmbeddableStart['getEmbeddableFactories']; overlays: OverlayStart; notifications: NotificationsStart; SavedObjectFinder: React.ComponentType; -}) { +}): OverlayRef { const { embeddable, getFactory, @@ -59,4 +59,5 @@ export async function openAddPanelFlyout(options: { ownFocus: true, } ); + return flyoutSession; } diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index ab039d15a2ce6e..b20d5866298d5b 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -34,6 +34,7 @@ import { MaybePromise } from '@kbn/utility-types'; import { NotificationsStart as NotificationsStart_2 } from 'src/core/public'; import { Observable } from 'rxjs'; import { Optional } from '@kbn/utility-types'; +import { OverlayRef as OverlayRef_2 } from 'src/core/public'; import { OverlayStart as OverlayStart_2 } from 'src/core/public'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; @@ -717,7 +718,7 @@ export function openAddPanelFlyout(options: { overlays: OverlayStart_2; notifications: NotificationsStart_2; SavedObjectFinder: React.ComponentType; -}): Promise; +}): 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) // From bd13284bfebcf8d1fb70039287f88aa5e0f11ddc Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 22 Dec 2020 12:41:18 -0500 Subject: [PATCH 049/100] [ML] Ensure job group badge fonts are same color (#86674) * use badge component to keep group badge consistent. sort groups * use all colors now that badge component is used * set left margin on group badge wrapped in link to match badge margin * add badge colors to edit flyout job groups --- .../ml/common/util/group_color_utils.ts | 2 +- .../edit_job_flyout/tabs/job_details.js | 7 ++-- .../job_filter_bar/job_filter_bar.tsx | 1 - .../job_group/{index.js => index.ts} | 0 .../components/job_group/job_group.js | 34 ------------------- .../components/job_group/job_group.tsx | 15 ++++++++ .../components/jobs_list/job_id_link.tsx | 3 +- .../ml/server/models/job_service/groups.ts | 4 ++- 8 files changed, 26 insertions(+), 40 deletions(-) rename x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/{index.js => index.ts} (100%) delete mode 100644 x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.js create mode 100644 x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.tsx diff --git a/x-pack/plugins/ml/common/util/group_color_utils.ts b/x-pack/plugins/ml/common/util/group_color_utils.ts index 71059192741850..b5749641d2b57f 100644 --- a/x-pack/plugins/ml/common/util/group_color_utils.ts +++ b/x-pack/plugins/ml/common/util/group_color_utils.ts @@ -13,7 +13,7 @@ const COLORS = [ euiVars.euiColorVis1, euiVars.euiColorVis2, euiVars.euiColorVis3, - // euiVars.euiColorVis4, // light pink, too hard to read with white text + euiVars.euiColorVis4, euiVars.euiColorVis5, euiVars.euiColorVis6, euiVars.euiColorVis7, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/job_details.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/job_details.js index ec5ef6fce26b5c..97b705177ed858 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/job_details.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/job_details.js @@ -19,6 +19,7 @@ import { import { ml } from '../../../../../services/ml_api_service'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { tabColor } from '../../../../../../../common/util/group_color_utils'; export class JobDetails extends Component { constructor(props) { @@ -43,7 +44,7 @@ export class JobDetails extends Component { ml.jobs .groups() .then((resp) => { - const groups = resp.map((g) => ({ label: g.id })); + const groups = resp.map((g) => ({ label: g.id, color: tabColor(g.id) })); this.setState({ groups }); }) .catch((error) => { @@ -53,7 +54,9 @@ export class JobDetails extends Component { static getDerivedStateFromProps(props) { const selectedGroups = - props.jobGroups !== undefined ? props.jobGroups.map((g) => ({ label: g })) : []; + props.jobGroups !== undefined + ? props.jobGroups.map((g) => ({ label: g, color: tabColor(g) })) + : []; return { description: props.jobDescription, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.tsx index 1b1bea889925fc..71be8126622997 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_filter_bar/job_filter_bar.tsx @@ -17,7 +17,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -// @ts-ignore import { JobGroup } from '../job_group'; import { useMlKibana } from '../../../../contexts/kibana'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/index.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/index.js rename to x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/index.ts diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.js deleted file mode 100644 index e8892c076c7a9a..00000000000000 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { tabColor } from '../../../../../../common/util/group_color_utils'; - -import PropTypes from 'prop-types'; -import React from 'react'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; - -export function JobGroup({ name }) { - return ( -
    - {name} -
    - ); -} -JobGroup.propTypes = { - name: PropTypes.string.isRequired, -}; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.tsx new file mode 100644 index 00000000000000..eab3e5ae3c1c7f --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_group/job_group.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; +import { EuiBadge } from '@elastic/eui'; +import { tabColor } from '../../../../../../common/util/group_color_utils'; + +export const JobGroup: FC<{ name: string }> = ({ name }) => ( + + {name} + +); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_id_link.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_id_link.tsx index f1c82dbb83eb47..b81934630facd1 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_id_link.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/job_id_link.tsx @@ -55,7 +55,8 @@ export const AnomalyDetectionJobIdLink = (props: AnomalyDetectionJobIdLinkProps) if (isGroupIdLink(props)) { return ( - + // Set margin-left to match EuiBadge (in JobGroup) built-in left margin for consistent badge spacing in management and plugin jobs list + ); diff --git a/x-pack/plugins/ml/server/models/job_service/groups.ts b/x-pack/plugins/ml/server/models/job_service/groups.ts index 59090f30ccca9f..f6073ae7071b0b 100644 --- a/x-pack/plugins/ml/server/models/job_service/groups.ts +++ b/x-pack/plugins/ml/server/models/job_service/groups.ts @@ -72,7 +72,9 @@ export function groupsProvider(mlClient: MlClient) { }); } - return Object.keys(groups).map((g) => groups[g]); + return Object.keys(groups) + .sort() + .map((g) => groups[g]); } async function updateGroups(jobs: Job[]) { From a2d13f6211f898cb6af947e0c29752daaf125587 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Tue, 22 Dec 2020 10:08:50 -0800 Subject: [PATCH 050/100] [DOCS] Fixes typo in saved search doc (#86808) --- docs/discover/search.asciidoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/discover/search.asciidoc b/docs/discover/search.asciidoc index 75c6fddb484ac2..45f0df5bd773fc 100644 --- a/docs/discover/search.asciidoc +++ b/docs/discover/search.asciidoc @@ -74,7 +74,7 @@ status codes, you could enter `status:[400 TO 499]`. codes and have an extension of `php` or `html`, you could enter `status:[400 TO 499] AND (extension:php OR extension:html)`. -IMPORTANT: When you use the Lucene Query Syntax in the *KQL* search bar, {kib} is unable to search on nested objects and perform aggregations across fields that contain nested objects. +IMPORTANT: When you use the Lucene Query Syntax in the *KQL* search bar, {kib} is unable to search on nested objects and perform aggregations across fields that contain nested objects. Using `include_in_parent` or `copy_to` as a workaround can cause {kib} to fail. For more detailed information about the Lucene query syntax, see the @@ -107,7 +107,8 @@ To save the current search: . Click *Save* in the Kibana toolbar. . Enter a name for the search and click *Save*. -To import, export, and delete saved searches, open the main menu, then click *Stack Management > Saved Ojbects*. +To import, export, and delete saved searches, open the main menu, +then click *Stack Management > Saved Objects*. ==== Open a saved search To load a saved search into Discover: From 4c17faa023f31f98c4a2664f7c56ac4502ef4437 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Tue, 22 Dec 2020 12:12:12 -0600 Subject: [PATCH 051/100] [ML] Fix zoom missing in Anomaly detection URLs (#86182) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ml/common/types/ml_url_generator.ts | 28 ++++---- .../explorer_charts_container.js | 70 +++++++++++++------ .../explorer_charts_container.test.js | 5 ++ .../ml/public/application/util/chart_utils.js | 2 +- .../anomaly_detection_urls_generator.ts | 5 ++ .../ml_url_generator/ml_url_generator.test.ts | 2 +- 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index fb432189c6dd3f..3c70cf4c27b5dc 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -146,30 +146,28 @@ export interface TimeSeriesExplorerGlobalState { refreshInterval?: RefreshInterval; } -export interface TimeSeriesExplorerAppState { - mlTimeSeriesExplorer?: { - forecastId?: string; - detectorIndex?: number; - entities?: Record; - zoom?: { - from?: string; - to?: string; - }; - functionDescription?: string; +export interface TimeSeriesExplorerParams { + forecastId?: string; + detectorIndex?: number; + entities?: Record; + zoom?: { + from?: string; + to?: string; }; + functionDescription?: string; +} +export interface TimeSeriesExplorerAppState { + mlTimeSeriesExplorer?: TimeSeriesExplorerParams; query?: any; } export interface TimeSeriesExplorerPageState - extends Pick, + extends TimeSeriesExplorerParams, + Pick, Pick { jobIds?: JobId[]; timeRange?: TimeRange; - detectorIndex?: number; - entities?: Record; - forecastId?: string; globalState?: MlCommonGlobalState; - functionDescription?: string; } export type TimeSeriesExplorerUrlState = MLPageState< diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index ee5247d193e8ad..774372f678c9b1 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiButtonEmpty, @@ -54,14 +54,33 @@ function getChartId(series) { } // Wrapper for a single explorer chart -function ExplorerChartContainer({ series, severity, tooManyBuckets, wrapLabel, mlUrlGenerator }) { - const redirectToSingleMetricViewer = useCallback(async () => { - const singleMetricViewerLink = await getExploreSeriesLink(mlUrlGenerator, series); - addItemToRecentlyAccessed('timeseriesexplorer', series.jobId, singleMetricViewerLink); - - window.open(singleMetricViewerLink, '_blank'); - }, [mlUrlGenerator]); - +function ExplorerChartContainer({ + series, + severity, + tooManyBuckets, + wrapLabel, + mlUrlGenerator, + basePath, +}) { + const [explorerSeriesLink, setExplorerSeriesLink] = useState(); + + useEffect(() => { + let isCancelled = false; + const generateLink = async () => { + const singleMetricViewerLink = await getExploreSeriesLink(mlUrlGenerator, series); + if (!isCancelled) { + setExplorerSeriesLink(singleMetricViewerLink); + } + }; + generateLink(); + return () => { + isCancelled = true; + }; + }, [mlUrlGenerator, series]); + + const addToRecentlyAccessed = useCallback(() => { + addItemToRecentlyAccessed('timeseriesexplorer', series.jobId, explorerSeriesLink); + }, [explorerSeriesLink]); const { detectorLabel, entityFields } = series; const chartType = getChartType(series); @@ -111,16 +130,22 @@ function ExplorerChartContainer({ series, severity, tooManyBuckets, wrapLabel, m /> )} - - - - - + {explorerSeriesLink && ( + + {/* href needs to be full link with base path while ChromeRecentlyAccessed requires only relative path */} + {/* disabling because we need button to behave as link and to have a callback */} + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + + + + + )}
    @@ -170,14 +195,13 @@ export const ExplorerChartsContainerUI = ({ }) => { const { services: { - application: { navigateToApp }, - + http: { basePath }, share: { urlGenerators: { getUrlGenerator }, }, }, } = kibana; - const mlUrlGenerator = getUrlGenerator(ML_APP_URL_GENERATOR); + const mlUrlGenerator = useMemo(() => getUrlGenerator(ML_APP_URL_GENERATOR), [getUrlGenerator]); // doesn't allow a setting of `columns={1}` when chartsPerRow would be 1. // If that's the case we trick it doing that with the following settings: @@ -201,8 +225,8 @@ export const ExplorerChartsContainerUI = ({ severity={severity} tooManyBuckets={tooManyBuckets} wrapLabel={wrapLabel} - navigateToApp={navigateToApp} mlUrlGenerator={mlUrlGenerator} + basePath={basePath.get()} /> ))} diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.test.js index 2da212c8f2f293..96004516135d05 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.test.js @@ -56,6 +56,11 @@ describe('ExplorerChartsContainer', () => { const kibanaContextMock = { services: { application: { navigateToApp: jest.fn() }, + http: { + basePath: { + get: jest.fn(), + }, + }, share: { urlGenerators: { getUrlGenerator: jest.fn() }, }, diff --git a/x-pack/plugins/ml/public/application/util/chart_utils.js b/x-pack/plugins/ml/public/application/util/chart_utils.js index d142d2e2466596..402c922a0034ff 100644 --- a/x-pack/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/plugins/ml/public/application/util/chart_utils.js @@ -264,7 +264,7 @@ export async function getExploreSeriesLink(mlUrlGenerator, series) { }, }, }, - excludeBasePath: false, + excludeBasePath: true, }); return url; } diff --git a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts index d2814bd63b0b03..b6a3ca0ce7139d 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/anomaly_detection_urls_generator.ts @@ -163,6 +163,7 @@ export function createSingleMetricViewerUrl( entities, globalState, functionDescription, + zoom, } = params; let queryState: Partial = {}; @@ -193,6 +194,10 @@ export function createSingleMetricViewerUrl( mlTimeSeriesExplorer.functionDescription = functionDescription; } + if (zoom !== undefined) { + mlTimeSeriesExplorer.zoom = zoom; + } + appState.mlTimeSeriesExplorer = mlTimeSeriesExplorer; if (query) diff --git a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts index 7dcd901c2c0ef4..21da0424cdca0e 100644 --- a/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts +++ b/x-pack/plugins/ml/public/ml_url_generator/ml_url_generator.test.ts @@ -161,7 +161,7 @@ describe('MlUrlGenerator', () => { }, }); expect(url).toBe( - "/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(logs_categorization_1)),refreshInterval:(pause:!f,value:0),time:(from:'2020-07-12T00:39:02.912Z',mode:absolute,to:'2020-07-22T15:52:18.613Z'))&_a=(timeseriesexplorer:(mlTimeSeriesExplorer:(detectorIndex:0,entities:(mlcategory:'2')),query:(query_string:(analyze_wildcard:!t,query:'*'))))" + "/app/ml/timeseriesexplorer?_g=(ml:(jobIds:!(logs_categorization_1)),refreshInterval:(pause:!f,value:0),time:(from:'2020-07-12T00:39:02.912Z',mode:absolute,to:'2020-07-22T15:52:18.613Z'))&_a=(timeseriesexplorer:(mlTimeSeriesExplorer:(detectorIndex:0,entities:(mlcategory:'2'),zoom:(from:'2020-07-20T23:58:29.367Z',to:'2020-07-21T11:00:13.173Z')),query:(query_string:(analyze_wildcard:!t,query:'*'))))" ); }); }); From 61eb83b467dbaec1418ee8234814b905e47a2cf3 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Tue, 22 Dec 2020 12:22:56 -0600 Subject: [PATCH 052/100] [ML] Fix error callout for Anomaly detection jobs using runtime_mappings (#86407) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/ml/common/util/job_utils.ts | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/common/util/job_utils.ts b/x-pack/plugins/ml/common/util/job_utils.ts index 60635118794489..4f4d9851c49578 100644 --- a/x-pack/plugins/ml/common/util/job_utils.ts +++ b/x-pack/plugins/ml/common/util/job_utils.ts @@ -49,6 +49,22 @@ export function calculateDatafeedFrequencyDefaultSeconds(bucketSpanSeconds: numb return freq; } +export function hasRuntimeMappings(job: CombinedJob): boolean { + const hasDatafeed = + typeof job.datafeed_config === 'object' && Object.keys(job.datafeed_config).length > 0; + if (hasDatafeed) { + const runtimeMappings = + typeof job.datafeed_config.runtime_mappings === 'object' + ? Object.keys(job.datafeed_config.runtime_mappings) + : undefined; + + if (Array.isArray(runtimeMappings) && runtimeMappings.length > 0) { + return true; + } + } + return false; +} + export function isTimeSeriesViewJob(job: CombinedJob): boolean { return getSingleMetricViewerJobErrorMessage(job) === undefined; } @@ -94,10 +110,10 @@ export function isSourceDataChartableForDetector(job: CombinedJob, detectorIndex scriptFields.indexOf(dtr.over_field_name!) === -1; } - // We cannot plot the source data for some specific aggregation configurations const hasDatafeed = typeof job.datafeed_config === 'object' && Object.keys(job.datafeed_config).length > 0; if (hasDatafeed) { + // We cannot plot the source data for some specific aggregation configurations const aggs = getDatafeedAggregations(job.datafeed_config); if (aggs !== undefined) { const aggBucketsName = getAggregationBucketsName(aggs); @@ -110,6 +126,11 @@ export function isSourceDataChartableForDetector(job: CombinedJob, detectorIndex } } } + + // We also cannot plot the source data if they datafeed uses any field defined by runtime_mappings + if (hasRuntimeMappings(job)) { + return false; + } } } @@ -149,6 +170,12 @@ export function isModelPlotChartableForDetector(job: Job, detectorIndex: number) // Returns a reason to indicate why the job configuration is not supported // if the result is undefined, that means the single metric job should be viewable export function getSingleMetricViewerJobErrorMessage(job: CombinedJob): string | undefined { + // if job has runtime mappings with no model plot + if (hasRuntimeMappings(job) && !job.model_plot_config?.enabled) { + return i18n.translate('xpack.ml.timeSeriesJob.jobWithRunTimeMessage', { + defaultMessage: 'the datafeed contains runtime fields and model plot is disabled', + }); + } // only allow jobs with at least one detector whose function corresponds to // an ES aggregation which can be viewed in the single metric view and which // doesn't use a scripted field which can be very difficult or impossible to From a98550b4f1d741126cd57a3d43c48fc4156013ce Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 22 Dec 2020 11:23:54 -0700 Subject: [PATCH 053/100] Reporting usage collector fetch migration (#86675) --- .../reporting/server/usage/get_reporting_usage.ts | 15 +++++++++------ .../usage/reporting_usage_collector.test.ts | 2 +- .../server/usage/reporting_usage_collector.ts | 4 ++-- x-pack/plugins/reporting/server/usage/types.ts | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts index 1211d4c2cf1c33..6d7f5c2e367fa0 100644 --- a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient, SearchResponse } from 'kibana/server'; import { ReportingConfig } from '../'; import { ExportTypesRegistry } from '../lib/export_types_registry'; import { GetLicense } from './'; @@ -18,7 +18,7 @@ import { KeyCountBucket, RangeStats, ReportingUsageType, - SearchResponse, + ReportingUsageSearchResponse, StatusByAppBucket, } from './types'; @@ -99,7 +99,9 @@ type RangeStatSets = Partial & { last7Days: Partial; }; -async function handleResponse(response: SearchResponse): Promise> { +type ESResponse = Partial>; + +async function handleResponse(response: ESResponse): Promise> { const buckets = get(response, 'aggregations.ranges.buckets'); if (!buckets) { return {}; @@ -118,7 +120,7 @@ async function handleResponse(response: SearchResponse): Promise { const reportingIndex = config.get('index'); @@ -165,8 +167,9 @@ export async function getReportingUsage( }; const featureAvailability = await getLicense(); - return callCluster('search', params) - .then((response: SearchResponse) => handleResponse(response)) + return esClient + .search(params) + .then(({ body: response }) => handleResponse(response)) .then( (usage: Partial): ReportingUsageType => { // Allow this to explicitly throw an exception if/when this config is deprecated, diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts index 7cae5e9b6f9563..8b0c442c12b977 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.test.ts @@ -59,7 +59,7 @@ const getResponseMock = (base = {}) => base; const getMockFetchClients = (resp: any) => { const fetchParamsMock = createCollectorFetchContextMock(); - fetchParamsMock.callCluster.mockResolvedValue(resp); + fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: resp }); return fetchParamsMock; }; describe('license checks', () => { diff --git a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts index f4209730b68ce9..547c331784c5f4 100644 --- a/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -26,9 +26,9 @@ export function getReportingUsageCollector( ) { return usageCollection.makeUsageCollector({ type: 'reporting', - fetch: ({ callCluster }: CollectorFetchContext) => { + fetch: ({ esClient }: CollectorFetchContext) => { const config = reporting.getConfig(); - return getReportingUsage(config, getLicense, callCluster, exportTypesRegistry); + return getReportingUsage(config, getLicense, esClient, exportTypesRegistry); }, isReady, schema: reportingSchema, diff --git a/x-pack/plugins/reporting/server/usage/types.ts b/x-pack/plugins/reporting/server/usage/types.ts index 1ff680eff8eafb..fe7838240f2fa2 100644 --- a/x-pack/plugins/reporting/server/usage/types.ts +++ b/x-pack/plugins/reporting/server/usage/types.ts @@ -152,7 +152,7 @@ export interface AggregationResultBuckets { doc_count: number; } -export interface SearchResponse { +export interface ReportingUsageSearchResponse { aggregations: { ranges: { buckets: { From 24db3a00707ac84c3c8af76a31922ee61902945a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 22 Dec 2020 19:36:02 +0100 Subject: [PATCH 054/100] [Logs UI] Fix initial selection of log threshold alert condition field if missing from mapping (#86488) This avoid selecting the `log.level` field as the default in log threshold alerts if it is not present in the mapping of at least one source index. --- .../alerting/logs/log_threshold/types.ts | 74 ++++++--- .../http_api/log_alerts/chart_preview_data.ts | 8 +- x-pack/plugins/infra/common/utility_types.ts | 3 + .../components/expression_editor/criteria.tsx | 79 ++++------ .../expression_editor/criterion.tsx | 28 ++-- .../criterion_preview_chart.tsx | 4 +- .../components/expression_editor/editor.tsx | 140 +++++++++++------- .../expression_editor/type_switcher.tsx | 6 +- .../log_threshold/log_threshold_alert_type.ts | 7 +- .../alerting/log_threshold/validation.ts | 62 ++++---- .../containers/logs/log_source/log_source.ts | 2 + .../log_threshold/log_threshold_executor.ts | 18 +-- .../register_log_threshold_alert_type.ts | 4 +- 13 files changed, 254 insertions(+), 181 deletions(-) diff --git a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts index c505a234c7b2bb..5f2e355ca3a47a 100644 --- a/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts +++ b/x-pack/plugins/infra/common/alerting/logs/log_threshold/types.ts @@ -105,29 +105,38 @@ const ThresholdRT = rt.type({ export type Threshold = rt.TypeOf; -export const CriterionRT = rt.type({ +export const criterionRT = rt.type({ field: rt.string, comparator: ComparatorRT, value: rt.union([rt.string, rt.number]), }); +export type Criterion = rt.TypeOf; -export type Criterion = rt.TypeOf; -export const criteriaRT = rt.array(CriterionRT); -export type Criteria = rt.TypeOf; +export const partialCriterionRT = rt.partial(criterionRT.props); +export type PartialCriterion = rt.TypeOf; -export const countCriteriaRT = criteriaRT; +export const countCriteriaRT = rt.array(criterionRT); export type CountCriteria = rt.TypeOf; -export const ratioCriteriaRT = rt.tuple([criteriaRT, criteriaRT]); +export const partialCountCriteriaRT = rt.array(partialCriterionRT); +export type PartialCountCriteria = rt.TypeOf; + +export const ratioCriteriaRT = rt.tuple([countCriteriaRT, countCriteriaRT]); export type RatioCriteria = rt.TypeOf; -export const TimeUnitRT = rt.union([ +export const partialRatioCriteriaRT = rt.tuple([partialCountCriteriaRT, partialCountCriteriaRT]); +export type PartialRatioCriteria = rt.TypeOf; + +export const partialCriteriaRT = rt.union([partialCountCriteriaRT, partialRatioCriteriaRT]); +export type PartialCriteria = rt.TypeOf; + +export const timeUnitRT = rt.union([ rt.literal('s'), rt.literal('m'), rt.literal('h'), rt.literal('d'), ]); -export type TimeUnit = rt.TypeOf; +export type TimeUnit = rt.TypeOf; export const timeSizeRT = rt.number; export const groupByRT = rt.array(rt.string); @@ -136,15 +145,18 @@ const RequiredAlertParamsRT = rt.type({ // NOTE: "count" would be better named as "threshold", but this would require a // migration of encrypted saved objects, so we'll keep "count" until it's problematic. count: ThresholdRT, - timeUnit: TimeUnitRT, + timeUnit: timeUnitRT, timeSize: timeSizeRT, }); +const partialRequiredAlertParamsRT = rt.partial(RequiredAlertParamsRT.props); +export type PartialRequiredAlertParams = rt.TypeOf; + const OptionalAlertParamsRT = rt.partial({ groupBy: groupByRT, }); -export const alertParamsRT = rt.intersection([ +export const countAlertParamsRT = rt.intersection([ rt.type({ criteria: countCriteriaRT, ...RequiredAlertParamsRT.props, @@ -153,8 +165,18 @@ export const alertParamsRT = rt.intersection([ ...OptionalAlertParamsRT.props, }), ]); +export type CountAlertParams = rt.TypeOf; -export type CountAlertParams = rt.TypeOf; +export const partialCountAlertParamsRT = rt.intersection([ + rt.type({ + criteria: partialCountCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); +export type PartialCountAlertParams = rt.TypeOf; export const ratioAlertParamsRT = rt.intersection([ rt.type({ @@ -165,13 +187,29 @@ export const ratioAlertParamsRT = rt.intersection([ ...OptionalAlertParamsRT.props, }), ]); - export type RatioAlertParams = rt.TypeOf; -export const AlertParamsRT = rt.union([alertParamsRT, ratioAlertParamsRT]); -export type AlertParams = rt.TypeOf; +export const partialRatioAlertParamsRT = rt.intersection([ + rt.type({ + criteria: partialRatioCriteriaRT, + ...RequiredAlertParamsRT.props, + }), + rt.partial({ + ...OptionalAlertParamsRT.props, + }), +]); +export type PartialRatioAlertParams = rt.TypeOf; + +export const alertParamsRT = rt.union([countAlertParamsRT, ratioAlertParamsRT]); +export type AlertParams = rt.TypeOf; + +export const partialAlertParamsRT = rt.union([ + partialCountAlertParamsRT, + partialRatioAlertParamsRT, +]); +export type PartialAlertParams = rt.TypeOf; -export const isRatioAlert = (criteria: AlertParams['criteria']): criteria is RatioCriteria => { +export const isRatioAlert = (criteria: PartialCriteria): criteria is PartialRatioCriteria => { return criteria.length > 0 && Array.isArray(criteria[0]) ? true : false; }; @@ -179,11 +217,13 @@ export const isRatioAlertParams = (params: AlertParams): params is RatioAlertPar return isRatioAlert(params.criteria); }; -export const getNumerator = (criteria: RatioCriteria): Criteria => { +export const getNumerator = (criteria: C): C[0] => { return criteria[0]; }; -export const getDenominator = (criteria: RatioCriteria): Criteria => { +export const getDenominator = ( + criteria: C +): C[1] => { return criteria[1]; }; diff --git a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts index 3226287d4cbdee..90547e68122253 100644 --- a/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts +++ b/x-pack/plugins/infra/common/http_api/log_alerts/chart_preview_data.ts @@ -6,8 +6,8 @@ import * as rt from 'io-ts'; import { - criteriaRT, - TimeUnitRT, + countCriteriaRT, + timeUnitRT, timeSizeRT, groupByRT, } from '../../alerting/logs/log_threshold/types'; @@ -42,8 +42,8 @@ export type GetLogAlertsChartPreviewDataSuccessResponsePayload = rt.TypeOf< export const getLogAlertsChartPreviewDataAlertParamsSubsetRT = rt.intersection([ rt.type({ - criteria: criteriaRT, - timeUnit: TimeUnitRT, + criteria: countCriteriaRT, + timeUnit: timeUnitRT, timeSize: timeSizeRT, }), rt.partial({ diff --git a/x-pack/plugins/infra/common/utility_types.ts b/x-pack/plugins/infra/common/utility_types.ts index 93fc9b729ca745..6bd784fed9308d 100644 --- a/x-pack/plugins/infra/common/utility_types.ts +++ b/x-pack/plugins/infra/common/utility_types.ts @@ -43,3 +43,6 @@ export type DeepPartial = T extends any[] interface DeepPartialArray extends Array> {} type DeepPartialObject = { [P in keyof T]+?: DeepPartial }; + +export type ObjectEntry = [keyof T, T[keyof T]]; +export type ObjectEntries = Array>; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index 3c474ee1d0ec6a..555ac905d29634 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -11,12 +11,11 @@ import { i18n } from '@kbn/i18n'; import { IFieldType } from 'src/plugins/data/public'; import { Criterion } from './criterion'; import { - AlertParams, - Comparator, - Criteria as CriteriaType, - Criterion as CriterionType, - CountCriteria as CountCriteriaType, - RatioCriteria as RatioCriteriaType, + PartialAlertParams, + PartialCountCriteria as PartialCountCriteriaType, + PartialCriteria as PartialCriteriaType, + PartialCriterion as PartialCriterionType, + PartialRatioCriteria as PartialRatioCriteriaType, isRatioAlert, getNumerator, getDenominator, @@ -25,8 +24,6 @@ import { Errors, CriterionErrors } from '../../validation'; import { ExpressionLike } from './editor'; import { CriterionPreview } from './criterion_preview_chart'; -const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; - const QueryAText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCriteriaQueryAText', { defaultMessage: 'Query A', }); @@ -37,11 +34,12 @@ const QueryBText = i18n.translate('xpack.infra.logs.alerting.threshold.ratioCrit interface SharedProps { fields: IFieldType[]; - criteria?: AlertParams['criteria']; + criteria?: PartialCriteriaType; + defaultCriterion: PartialCriterionType; errors: Errors['criteria']; - alertParams: Partial; + alertParams: PartialAlertParams; sourceId: string; - updateCriteria: (criteria: AlertParams['criteria']) => void; + updateCriteria: (criteria: PartialCriteriaType) => void; } type CriteriaProps = SharedProps; @@ -60,10 +58,10 @@ export const Criteria: React.FC = (props) => { interface CriteriaWrapperProps { alertParams: SharedProps['alertParams']; fields: SharedProps['fields']; - updateCriterion: (idx: number, params: Partial) => void; + updateCriterion: (idx: number, params: PartialCriterionType) => void; removeCriterion: (idx: number) => void; addCriterion: () => void; - criteria: CriteriaType; + criteria: PartialCountCriteriaType; errors: CriterionErrors; sourceId: SharedProps['sourceId']; isRatio?: boolean; @@ -118,29 +116,24 @@ const CriteriaWrapper: React.FC = (props) => { ); }; -interface RatioCriteriaProps { - alertParams: SharedProps['alertParams']; - fields: SharedProps['fields']; - criteria: RatioCriteriaType; - errors: Errors['criteria']; - sourceId: SharedProps['sourceId']; - updateCriteria: (criteria: AlertParams['criteria']) => void; +interface RatioCriteriaProps extends SharedProps { + criteria: PartialRatioCriteriaType; } const RatioCriteria: React.FC = (props) => { - const { criteria, errors, updateCriteria } = props; + const { criteria, defaultCriterion, errors, updateCriteria } = props; const handleUpdateNumeratorCriteria = useCallback( - (criteriaParam: CriteriaType) => { - const nextCriteria: RatioCriteriaType = [criteriaParam, getDenominator(criteria)]; + (criteriaParam: PartialCountCriteriaType) => { + const nextCriteria: PartialRatioCriteriaType = [criteriaParam, getDenominator(criteria)]; updateCriteria(nextCriteria); }, [updateCriteria, criteria] ); const handleUpdateDenominatorCriteria = useCallback( - (criteriaParam: CriteriaType) => { - const nextCriteria: RatioCriteriaType = [getNumerator(criteria), criteriaParam]; + (criteriaParam: PartialCountCriteriaType) => { + const nextCriteria: PartialRatioCriteriaType = [getNumerator(criteria), criteriaParam]; updateCriteria(nextCriteria); }, [updateCriteria, criteria] @@ -150,13 +143,13 @@ const RatioCriteria: React.FC = (props) => { updateCriterion: updateNumeratorCriterion, addCriterion: addNumeratorCriterion, removeCriterion: removeNumeratorCriterion, - } = useCriteriaState(getNumerator(criteria), handleUpdateNumeratorCriteria); + } = useCriteriaState(getNumerator(criteria), defaultCriterion, handleUpdateNumeratorCriteria); const { updateCriterion: updateDenominatorCriterion, addCriterion: addDenominatorCriterion, removeCriterion: removeDenominatorCriterion, - } = useCriteriaState(getDenominator(criteria), handleUpdateDenominatorCriteria); + } = useCriteriaState(getDenominator(criteria), defaultCriterion, handleUpdateDenominatorCriteria); return ( <> @@ -191,28 +184,17 @@ const RatioCriteria: React.FC = (props) => { ); }; -interface CountCriteriaProps { - alertParams: SharedProps['alertParams']; - fields: SharedProps['fields']; - criteria: CountCriteriaType; - errors: Errors['criteria']; - sourceId: SharedProps['sourceId']; - updateCriteria: (criteria: AlertParams['criteria']) => void; +interface CountCriteriaProps extends SharedProps { + criteria: PartialCountCriteriaType; } const CountCriteria: React.FC = (props) => { - const { criteria, updateCriteria, errors } = props; - - const handleUpdateCriteria = useCallback( - (criteriaParam: CriteriaType) => { - updateCriteria(criteriaParam); - }, - [updateCriteria] - ); + const { criteria, defaultCriterion, updateCriteria, errors } = props; const { updateCriterion, addCriterion, removeCriterion } = useCriteriaState( criteria, - handleUpdateCriteria + defaultCriterion, + updateCriteria ); return ( @@ -227,8 +209,9 @@ const CountCriteria: React.FC = (props) => { }; const useCriteriaState = ( - criteria: CriteriaType, - onUpdateCriteria: (criteria: CriteriaType) => void + criteria: PartialCountCriteriaType, + defaultCriterion: PartialCriterionType, + onUpdateCriteria: (criteria: PartialCountCriteriaType) => void ) => { const updateCriterion = useCallback( (idx, criterionParams) => { @@ -241,13 +224,13 @@ const useCriteriaState = ( ); const addCriterion = useCallback(() => { - const nextCriteria = criteria ? [...criteria, DEFAULT_CRITERIA] : [DEFAULT_CRITERIA]; + const nextCriteria = [...criteria, defaultCriterion]; onUpdateCriteria(nextCriteria); - }, [criteria, onUpdateCriteria]); + }, [criteria, defaultCriterion, onUpdateCriteria]); const removeCriterion = useCallback( (idx) => { - const nextCriteria = criteria.filter((criterion, index) => { + const nextCriteria = criteria.filter((_criterion, index) => { return index !== idx; }); onUpdateCriteria(nextCriteria); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx index b2992ead3ea1b1..9763a973d2fbd1 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx @@ -90,7 +90,7 @@ const getFieldInfo = (fields: IFieldType[], fieldName: string): IFieldType | und interface Props { idx: number; fields: IFieldType[]; - criterion: CriterionType; + criterion: Partial; updateCriterion: (idx: number, params: Partial) => void; removeCriterion: (idx: number) => void; canDelete: boolean; @@ -116,7 +116,11 @@ export const Criterion: React.FC = ({ }, [fields]); const fieldInfo: IFieldType | undefined = useMemo(() => { - return getFieldInfo(fields, criterion.field); + if (criterion.field) { + return getFieldInfo(fields, criterion.field); + } else { + return undefined; + } }, [fields, criterion]); const compatibleComparatorOptions = useMemo(() => { @@ -129,10 +133,8 @@ export const Criterion: React.FC = ({ const nextFieldInfo = getFieldInfo(fields, fieldName); // If the field information we're dealing with has changed, reset the comparator and value. if ( - fieldInfo && - nextFieldInfo && - (fieldInfo.type !== nextFieldInfo.type || - fieldInfo.aggregatable !== nextFieldInfo.aggregatable) + fieldInfo?.type !== nextFieldInfo?.type || + fieldInfo?.aggregatable !== nextFieldInfo?.aggregatable ) { const compatibleComparators = getCompatibleComparatorsForField(nextFieldInfo); updateCriterion(idx, { @@ -160,7 +162,7 @@ export const Criterion: React.FC = ({ idx === 0 ? firstCriterionFieldPrefix : successiveCriterionFieldPrefix } uppercase={true} - value={criterion.field} + value={criterion.field ?? 'a chosen field'} isActive={isFieldPopoverOpen} color={errors.field.length === 0 ? 'secondary' : 'danger'} onClick={(e) => { @@ -180,7 +182,8 @@ export const Criterion: React.FC = ({ 0} error={errors.field}> @@ -194,9 +197,11 @@ export const Criterion: React.FC = ({ button={ = ({ 0} error={errors.comparator}> updateCriterion(idx, { comparator: e.target.value as Comparator }) diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 47dc4190228809..cb759afa66d5c0 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -34,7 +34,7 @@ import { NUM_BUCKETS, } from '../../../common/criterion_preview_chart/criterion_preview_chart'; import { - AlertParams, + PartialAlertParams, Threshold, Criterion, Comparator, @@ -50,7 +50,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; const GROUP_LIMIT = 5; interface Props { - alertParams: Partial; + alertParams: PartialAlertParams; chartCriterion: Partial; sourceId: string; showThreshold: boolean; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 854363aacca5c9..f69ca798c01b0e 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -4,25 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useMemo, useState } from 'react'; +import { EuiButton, EuiCallOut, EuiLoadingSpinner, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiLoadingSpinner, EuiSpacer, EuiButton, EuiCallOut } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; import useMount from 'react-use/lib/useMount'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; -import { ForLastExpression } from '../../../../../../triggers_actions_ui/public'; import { - AlertParams, + AlertTypeParamsExpressionProps, + ForLastExpression, +} from '../../../../../../triggers_actions_ui/public'; +import { + PartialAlertParams, Comparator, - ThresholdType, isRatioAlert, + PartialCriteria as PartialCriteriaType, + ThresholdType, + timeUnitRT, } from '../../../../../common/alerting/logs/log_threshold/types'; -import { Threshold } from './threshold'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { ObjectEntries } from '../../../../../common/utility_types'; +import { + LogIndexField, + LogSourceProvider, + useLogSourceContext, +} from '../../../../containers/logs/log_source'; +import { useSourceId } from '../../../../containers/source_id'; +import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; +import { errorsRT } from '../../validation'; import { Criteria } from './criteria'; +import { Threshold } from './threshold'; import { TypeSwitcher } from './type_switcher'; -import { useSourceId } from '../../../../containers/source_id'; -import { LogSourceProvider, useLogSourceContext } from '../../../../containers/logs/log_source'; -import { Errors } from '../../validation'; export interface ExpressionCriteria { field?: string; @@ -34,45 +45,46 @@ interface LogsContextMeta { isInternal?: boolean; } -interface Props { - errors: Errors; - alertParams: Partial; - setAlertParams(key: string, value: any): void; - setAlertProperty(key: string, value: any): void; - sourceId: string; - metadata: LogsContextMeta; -} - -const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; - const DEFAULT_BASE_EXPRESSION = { timeSize: 5, timeUnit: 'm', }; -const DEFAULT_COUNT_EXPRESSION = { +const DEFAULT_FIELD = 'log.level'; + +const createDefaultCriterion = ( + availableFields: LogIndexField[], + value: ExpressionCriteria['value'] +) => + availableFields.some((availableField) => availableField.name === DEFAULT_FIELD) + ? { field: DEFAULT_FIELD, comparator: Comparator.EQ, value } + : { field: undefined, comparator: undefined, value: undefined }; + +const createDefaultCountAlertParams = (availableFields: LogIndexField[]) => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 75, comparator: Comparator.GT, }, - criteria: [DEFAULT_CRITERIA], -}; + criteria: [createDefaultCriterion(availableFields, 'error')], +}); -const DEFAULT_RATIO_EXPRESSION = { +const createDefaultRatioAlertParams = (availableFields: LogIndexField[]) => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 2, comparator: Comparator.GT, }, criteria: [ - [DEFAULT_CRITERIA], - [{ field: 'log.level', comparator: Comparator.EQ, value: 'warning' }], + createDefaultCriterion(availableFields, 'error'), + createDefaultCriterion([], 'warning'), ], -}; +}); -export const ExpressionEditor: React.FC = (props) => { - const isInternal = props.metadata?.isInternal; +export const ExpressionEditor: React.FC< + AlertTypeParamsExpressionProps +> = (props) => { + const isInternal = props.metadata?.isInternal ?? false; const [sourceId] = useSourceId(); const { http } = useKibana().services; @@ -80,12 +92,12 @@ export const ExpressionEditor: React.FC = (props) => { <> {isInternal ? ( - + ) : ( - + )} @@ -93,7 +105,7 @@ export const ExpressionEditor: React.FC = (props) => { ); }; -export const SourceStatusWrapper: React.FC = (props) => { +export const SourceStatusWrapper: React.FC = ({ children }) => { const { initialize, isLoadingSourceStatus, @@ -101,7 +113,6 @@ export const SourceStatusWrapper: React.FC = (props) => { hasFailedLoadingSourceStatus, loadSourceStatus, } = useLogSourceContext(); - const { children } = props; useMount(() => { initialize(); @@ -136,16 +147,19 @@ export const SourceStatusWrapper: React.FC = (props) => { ); }; -export const Editor: React.FC = (props) => { - const { setAlertParams, alertParams, errors, sourceId } = props; +export const Editor: React.FC< + AlertTypeParamsExpressionProps +> = (props) => { + const { setAlertParams, alertParams, errors } = props; const [hasSetDefaults, setHasSetDefaults] = useState(false); - const { sourceStatus } = useLogSourceContext(); - useMount(() => { - for (const [key, value] of Object.entries({ ...DEFAULT_COUNT_EXPRESSION, ...alertParams })) { - setAlertParams(key, value); - } - setHasSetDefaults(true); - }); + const { sourceId, sourceStatus } = useLogSourceContext(); + + const { + criteria: criteriaErrors, + threshold: thresholdErrors, + timeSizeUnit: timeSizeUnitErrors, + timeWindowSize: timeWindowSizeErrors, + } = useMemo(() => decodeOrThrow(errorsRT)(errors), [errors]); const supportedFields = useMemo(() => { if (sourceStatus?.logIndexFields) { @@ -176,7 +190,7 @@ export const Editor: React.FC = (props) => { ); const updateCriteria = useCallback( - (criteria: AlertParams['criteria']) => { + (criteria: PartialCriteriaType) => { setAlertParams('criteria', criteria); }, [setAlertParams] @@ -191,7 +205,9 @@ export const Editor: React.FC = (props) => { const updateTimeUnit = useCallback( (tu: string) => { - setAlertParams('timeUnit', tu); + if (timeUnitRT.is(tu)) { + setAlertParams('timeUnit', tu); + } }, [setAlertParams] ); @@ -203,20 +219,31 @@ export const Editor: React.FC = (props) => { [setAlertParams] ); + const defaultCountAlertParams = useMemo(() => createDefaultCountAlertParams(supportedFields), [ + supportedFields, + ]); + const updateType = useCallback( (type: ThresholdType) => { - const defaults = type === 'count' ? DEFAULT_COUNT_EXPRESSION : DEFAULT_RATIO_EXPRESSION; + const defaults = + type === 'count' ? defaultCountAlertParams : createDefaultRatioAlertParams(supportedFields); // Reset properties that don't make sense switching from one context to the other - for (const [key, value] of Object.entries({ - criteria: defaults.criteria, - count: defaults.count, - })) { - setAlertParams(key, value); - } + setAlertParams('count', defaults.count); + setAlertParams('criteria', defaults.criteria); }, - [setAlertParams] + [defaultCountAlertParams, setAlertParams, supportedFields] ); + useMount(() => { + const newAlertParams = { ...defaultCountAlertParams, ...alertParams }; + for (const [key, value] of Object.entries(newAlertParams) as ObjectEntries< + typeof newAlertParams + >) { + setAlertParams(key, value); + } + setHasSetDefaults(true); + }); + // Wait until the alert param defaults have been set if (!hasSetDefaults) return null; @@ -224,7 +251,8 @@ export const Editor: React.FC = (props) => { = (props) => { comparator={alertParams.count?.comparator} value={alertParams.count?.value} updateThreshold={updateThreshold} - errors={errors.threshold} + errors={thresholdErrors} /> = (props) => { timeWindowUnit={alertParams.timeUnit} onChangeWindowSize={updateTimeSize} onChangeWindowUnit={updateTimeUnit} - errors={{ timeWindowSize: errors.timeWindowSize, timeSizeUnit: errors.timeSizeUnit }} + errors={{ timeWindowSize: timeWindowSizeErrors, timeSizeUnit: timeSizeUnitErrors }} /> void; } -const getThresholdType = (criteria: AlertParams['criteria']): ThresholdType => { +const getThresholdType = (criteria: PartialCriteria): ThresholdType => { return isRatioAlert(criteria) ? 'ratio' : 'count'; }; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts index 7154a77496b818..6cdb81155ec9a7 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts @@ -6,10 +6,13 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../common/alerting/logs/log_threshold/types'; +import { + LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, + PartialAlertParams, +} from '../../../common/alerting/logs/log_threshold/types'; import { validateExpression } from './validation'; -export function getAlertType(): AlertTypeModel { +export function getAlertType(): AlertTypeModel { return { id: LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, description: i18n.translate('xpack.infra.logs.alertFlyout.alertDescription', { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts index 6630b3d0791415..24d373558008da 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts @@ -5,45 +5,53 @@ */ import { i18n } from '@kbn/i18n'; +import * as rt from 'io-ts'; import { isNumber, isFinite } from 'lodash'; -import { ValidationResult } from '../../../../triggers_actions_ui/public'; +import { IErrorObject, ValidationResult } from '../../../../triggers_actions_ui/public'; import { - AlertParams, - Criteria, - RatioCriteria, + PartialCountCriteria, isRatioAlert, getNumerator, getDenominator, + PartialRequiredAlertParams, + PartialCriteria, } from '../../../common/alerting/logs/log_threshold/types'; -export interface CriterionErrors { - [id: string]: { - field: string[]; - comparator: string[]; - value: string[]; - }; -} +export const criterionErrorRT = rt.type({ + field: rt.array(rt.string), + comparator: rt.array(rt.string), + value: rt.array(rt.string), +}); -export interface Errors { - threshold: { - value: string[]; - }; +export const criterionErrorsRT = rt.record(rt.string, criterionErrorRT); + +export type CriterionErrors = rt.TypeOf; + +const alertingErrorRT: rt.Type = rt.recursion('AlertingError', () => + rt.record(rt.string, rt.union([rt.string, rt.array(rt.string), alertingErrorRT])) +); + +export const errorsRT = rt.type({ + threshold: rt.type({ + value: rt.array(rt.string), + }), // NOTE: The data structure for criteria errors isn't 100% // ideal but we need to conform to the interfaces that the alerting // framework expects. - criteria: { - [id: string]: CriterionErrors; - }; - timeWindowSize: string[]; - timeSizeUnit: string[]; -} + criteria: rt.record(rt.string, criterionErrorsRT), + timeWindowSize: rt.array(rt.string), + timeSizeUnit: rt.array(rt.string), +}); + +export type Errors = rt.TypeOf; export function validateExpression({ count, criteria, timeSize, - timeUnit, -}: Partial): ValidationResult { +}: PartialRequiredAlertParams & { + criteria: PartialCriteria; +}): ValidationResult { const validationResult = { errors: {} }; // NOTE: In the case of components provided by the Alerting framework the error property names @@ -79,7 +87,7 @@ export function validateExpression({ // Criteria validation if (criteria && criteria.length > 0) { - const getCriterionErrors = (_criteria: Criteria): CriterionErrors => { + const getCriterionErrors = (_criteria: PartialCountCriteria): CriterionErrors => { const _errors: CriterionErrors = {}; _criteria.forEach((criterion, idx) => { @@ -114,12 +122,12 @@ export function validateExpression({ }; if (!isRatioAlert(criteria)) { - const criteriaErrors = getCriterionErrors(criteria as Criteria); + const criteriaErrors = getCriterionErrors(criteria); errors.criteria[0] = criteriaErrors; } else { - const numeratorErrors = getCriterionErrors(getNumerator(criteria as RatioCriteria)); + const numeratorErrors = getCriterionErrors(getNumerator(criteria)); errors.criteria[0] = numeratorErrors; - const denominatorErrors = getCriterionErrors(getDenominator(criteria as RatioCriteria)); + const denominatorErrors = getCriterionErrors(getDenominator(criteria)); errors.criteria[1] = denominatorErrors; } } diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts index 879d2d95d79466..d7f40f603a9f7c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts @@ -9,6 +9,7 @@ import { useCallback, useMemo, useState } from 'react'; import useMountedState from 'react-use/lib/useMountedState'; import type { HttpHandler } from 'src/core/public'; import { + LogIndexField, LogSourceConfiguration, LogSourceConfigurationProperties, LogSourceConfigurationPropertiesPatch, @@ -20,6 +21,7 @@ import { callFetchLogSourceStatusAPI } from './api/fetch_log_source_status'; import { callPatchLogSourceConfigurationAPI } from './api/patch_log_source_configuration'; export { + LogIndexField, LogSourceConfiguration, LogSourceConfigurationProperties, LogSourceConfigurationPropertiesPatch, diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index dccab5168fb60a..09d7e482772c2d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -23,12 +23,12 @@ import { UngroupedSearchQueryResponseRT, UngroupedSearchQueryResponse, GroupedSearchQueryResponse, - AlertParamsRT, + alertParamsRT, isRatioAlertParams, hasGroupBy, getNumerator, getDenominator, - Criteria, + CountCriteria, CountAlertParams, RatioAlertParams, } from '../../../../common/alerting/logs/log_threshold/types'; @@ -67,7 +67,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); try { - const validatedParams = decodeOrThrow(AlertParamsRT)(params); + const validatedParams = decodeOrThrow(alertParamsRT)(params); if (!isRatioAlertParams(validatedParams)) { await executeAlert( @@ -174,7 +174,7 @@ async function executeRatioAlert( } const getESQuery = ( - alertParams: Omit & { criteria: Criteria }, + alertParams: Omit & { criteria: CountCriteria }, timestampField: string, indexPattern: string ) => { @@ -366,7 +366,7 @@ export const updateAlertInstance: AlertInstanceUpdater = (alertInstance, state, }; export const buildFiltersFromCriteria = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string ) => { const { timeSize, timeUnit, criteria } = params; @@ -417,7 +417,7 @@ export const buildFiltersFromCriteria = ( }; export const getGroupedESQuery = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string, index: string ): object | undefined => { @@ -475,7 +475,7 @@ export const getGroupedESQuery = ( }; export const getUngroupedESQuery = ( - params: Pick & { criteria: Criteria }, + params: Pick & { criteria: CountCriteria }, timestampField: string, index: string ): object => { @@ -509,7 +509,7 @@ type Filter = { [key in SupportedESQueryTypes]?: object; }; -const buildFiltersForCriteria = (criteria: Criteria) => { +const buildFiltersForCriteria = (criteria: CountCriteria) => { let filters: Filter[] = []; criteria.forEach((criterion) => { @@ -643,7 +643,7 @@ const getGroupedResults = async ( return compositeGroupBuckets; }; -const createConditionsMessageForCriteria = (criteria: Criteria) => { +const createConditionsMessageForCriteria = (criteria: CountCriteria) => { const parts = criteria.map((criterion, index) => { const { field, comparator, value } = criterion; return `${index === 0 ? '' : 'and'} ${field} ${comparator} ${value}`; diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts index 4703371f5e0def..e248d3b3ddcfa3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/register_log_threshold_alert_type.ts @@ -8,7 +8,7 @@ import { PluginSetupContract } from '../../../../../alerts/server'; import { createLogThresholdExecutor, FIRED_ACTIONS } from './log_threshold_executor'; import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID, - AlertParamsRT, + alertParamsRT, } from '../../../../common/alerting/logs/log_threshold/types'; import { InfraBackendLibs } from '../../infra_types'; import { decodeOrThrow } from '../../../../common/runtime_types'; @@ -86,7 +86,7 @@ export async function registerLogThresholdAlertType( }), validate: { params: { - validate: (params) => decodeOrThrow(AlertParamsRT)(params), + validate: (params) => decodeOrThrow(alertParamsRT)(params), }, }, defaultActionGroupId: FIRED_ACTIONS.id, From c0d6bd95b78ad70f5df0f76bf69c6312f75c48fd Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Tue, 22 Dec 2020 12:59:05 -0600 Subject: [PATCH 055/100] Upgrade EUI to v31.0.0 (#86689) * eui to 31.0.0 * snapshot updates * remove obsolete snapshot --- package.json | 2 +- .../dashboard_empty_screen.test.tsx.snap | 4 ++-- .../__snapshots__/dashboard_listing.test.tsx.snap | 12 ++++++------ .../workpad_templates.stories.storyshot | 6 +----- .../__tests__/__snapshots__/no_data.test.js.snap | 4 ++-- .../__snapshots__/page_loading.test.js.snap | 2 +- .../__snapshots__/reset_session_page.test.tsx.snap | 2 +- .../__snapshots__/index.test.tsx.snap | 7 +------ yarn.lock | 8 ++++---- 9 files changed, 19 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 985998e54cb880..49787bb8ab34ed 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "@elastic/datemath": "link:packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary", "@elastic/ems-client": "7.11.0", - "@elastic/eui": "30.6.0", + "@elastic/eui": "31.0.0", "@elastic/filesaver": "1.1.2", "@elastic/good": "^9.0.1-kibana3", "@elastic/node-crypto": "1.2.1", diff --git a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap index bdfb1c45f351af..a50aadc12e6c01 100644 --- a/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap +++ b/src/plugins/dashboard/public/application/embeddable/empty_screen/__snapshots__/dashboard_empty_screen.test.tsx.snap @@ -597,7 +597,7 @@ exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = ` restrictWidth="500px" >
    @@ -218,7 +218,7 @@ exports[`after fetch hideWriteControls 1`] = ` restrictWidth={true} >
    @@ -358,7 +358,7 @@ exports[`after fetch initialFilter 1`] = ` restrictWidth={true} >
    @@ -497,7 +497,7 @@ exports[`after fetch renders all table rows 1`] = ` restrictWidth={true} >
    @@ -636,7 +636,7 @@ exports[`after fetch renders call to action when no dashboards exist 1`] = ` restrictWidth={true} >
    @@ -775,7 +775,7 @@ exports[`after fetch renders warning when listingLimit is exceeded 1`] = ` restrictWidth={true} >
    diff --git a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot index 1f7105b80de4c2..d267ba07078fe3 100644 --- a/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot +++ b/x-pack/plugins/canvas/public/components/workpad_templates/examples/__snapshots__/workpad_templates.stories.storyshot @@ -178,11 +178,7 @@ exports[`Storyshots components/WorkpadTemplates default 1`] = ` > - } + title="; Sorted in ascending order" > Template name diff --git a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap index ffb1620b60fa33..8f820252d5b6b7 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap @@ -2,7 +2,7 @@ exports[`NoData should show a default message if reason is unknown 1`] = `

    MockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; +exports[`ResetSessionPage renders as expected 1`] = `"MockedFonts

    You do not have permission to access the requested page

    Either go back to the previous page or log in as a different user.

    "`; diff --git a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap index a6aa8449197098..a2a9c30ca4e1c7 100644 --- a/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/paginated_table/__snapshots__/index.test.tsx.snap @@ -460,12 +460,7 @@ exports[`Paginated Table Component rendering it renders the default load more ta "euiRangeTrackHeight": "2px", "euiRangeTrackRadius": "4px", "euiRangeTrackWidth": "100%", - "euiResizableButtonSizeModifiers": Object { - "sizeExtraLarge": "40px", - "sizeLarge": "24px", - "sizeMedium": "16px", - "sizeSmall": "12px", - }, + "euiResizableButtonSize": "16px", "euiResizableButtonTransitionSpeed": "150ms", "euiScrollBar": "16px", "euiScrollBarCorner": "6px", diff --git a/yarn.lock b/yarn.lock index de9efb26c384b3..6fa3ea09731b32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1439,10 +1439,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@30.6.0": - version "30.6.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-30.6.0.tgz#6653223223f52407ac05303825d9bd08382df1d5" - integrity sha512-40Jiy54MpJAx3lD3NSZZLkMkVySwKpX6RxIKnvT3somE95pwIjXrWB688m2nL2g05y7kNhjrhwfdctVzNXZENA== +"@elastic/eui@31.0.0": + version "31.0.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-31.0.0.tgz#7d17386c04a0ad343d70c3652902fcd3f46ed337" + integrity sha512-oj63HpQQKg/Cgwz5B0ZBQCkcgZiEdQzBT9PbmEiR/VRz5P0WqJpgZPyIF7jiFaFlGP1a9hPjkUTo+ramWNCpiw== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" From a75ca7abe9cce841514bd1abf14e65109812bc07 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Tue, 22 Dec 2020 13:12:06 -0600 Subject: [PATCH 056/100] [ML] Stabilize data visualizer functional tests (#86790) --- .../ml/data_visualizer_index_based.ts | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 5fc5caf81c23b4..fc9dd3d7b033a7 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -33,25 +33,33 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async assertTotalDocCountHeaderExist() { - await testSubjects.existOrFail(`mlDataVisualizerTotalDocCountHeader`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerTotalDocCountHeader`); + }); }, async assertTotalDocCountChartExist() { - await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlFieldDataDocumentCountChart`); + }); }, async assertFieldCountPanelExist() { - await testSubjects.existOrFail(`mlDataVisualizerFieldCountPanel`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerFieldCountPanel`); + }); }, async assertMetricFieldsSummaryExist() { - await testSubjects.existOrFail(`mlDataVisualizerMetricFieldsSummary`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerMetricFieldsSummary`); + }); }, async assertVisibleMetricFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerVisibleMetricFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerVisibleMetricFieldsCount'); const actualCount = await testSubjects.getVisibleText( 'mlDataVisualizerVisibleMetricFieldsCount' ); @@ -64,8 +72,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertTotalMetricFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerMetricFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerMetricFieldsCount'); const actualCount = await testSubjects.getVisibleText( 'mlDataVisualizerVisibleMetricFieldsCount' ); @@ -78,8 +86,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertVisibleFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerVisibleFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerVisibleFieldsCount'); const actualCount = await testSubjects.getVisibleText('mlDataVisualizerVisibleFieldsCount'); expect(expectedCount).to.eql( expectedCount, @@ -90,8 +98,8 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ async assertTotalFieldsCount(count: number) { const expectedCount = count.toString(); - await testSubjects.existOrFail('mlDataVisualizerTotalFieldsCount'); await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail('mlDataVisualizerTotalFieldsCount'); const actualCount = await testSubjects.getVisibleText('mlDataVisualizerTotalFieldsCount'); expect(expectedCount).to.contain( expectedCount, @@ -101,11 +109,15 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async assertFieldsSummaryExist() { - await testSubjects.existOrFail(`mlDataVisualizerFieldsSummary`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerFieldsSummary`); + }); }, async assertDataVisualizerTableExist() { - await testSubjects.existOrFail(`mlDataVisualizerTable`); + await retry.tryForTime(5000, async () => { + await testSubjects.existOrFail(`mlDataVisualizerTable`); + }); }, async assertActionsPanelExists() { From 5e3c84ca05c292b16478454a927d33345dd9c552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Tue, 22 Dec 2020 20:27:17 +0100 Subject: [PATCH 057/100] useSavedDashboard: call toast methods in their context (#86803) --- .../public/application/hooks/use_saved_dashboard.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/dashboard/public/application/hooks/use_saved_dashboard.ts b/src/plugins/dashboard/public/application/hooks/use_saved_dashboard.ts index f0d8b5f5e000d9..d946e00e1c8e43 100644 --- a/src/plugins/dashboard/public/application/hooks/use_saved_dashboard.ts +++ b/src/plugins/dashboard/public/application/hooks/use_saved_dashboard.ts @@ -36,7 +36,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: // abstraction of service dependencies easier. const { indexPatterns } = data; const { recentlyAccessed: recentlyAccessedPaths, docTitle } = chrome; - const { addDanger: showDangerToast, addWarning: showWarningToast } = core.notifications.toasts; + const { toasts } = core.notifications; useEffect(() => { (async function loadSavedDashboard() { @@ -46,7 +46,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: pathname: DashboardConstants.CREATE_NEW_DASHBOARD_URL, }); - showWarningToast(getDashboard60Warning()); + toasts.addWarning(getDashboard60Warning()); return; } @@ -63,7 +63,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: setSavedDashboard(dashboard); } catch (error) { // E.g. a corrupt or deleted dashboard - showDangerToast(error.message); + toasts.addDanger(error.message); history.push(DashboardConstants.LANDING_PAGE_PATH); } })(); @@ -75,8 +75,7 @@ export const useSavedDashboard = (savedDashboardId: string | undefined, history: recentlyAccessedPaths, savedDashboardId, savedDashboards, - showDangerToast, - showWarningToast, + toasts, ]); return savedDashboard; From 97858d697b8367583632283b83d80d09bb69d70e Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Tue, 22 Dec 2020 20:40:40 +0100 Subject: [PATCH 058/100] Re-add support for configuring server.host: "0" (#86806) Support for this was lost in #85406. Fixes #86716 --- src/core/server/http/http_config.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 2bd296fe338aba..61a9b5f04b23f8 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -195,7 +195,13 @@ export class HttpConfig { rawExternalUrlConfig: ExternalUrlConfig ) { this.autoListen = rawHttpConfig.autoListen; - this.host = rawHttpConfig.host; + // TODO: Consider dropping support for '0' in v8.0.0. This value is passed + // to hapi, which validates it. Prior to hapi v20, '0' was considered a + // valid host, however the validation logic internally in hapi was + // re-written for v20 and hapi no longer considers '0' a valid host. For + // details, see: + // https://github.com/elastic/kibana/issues/86716#issuecomment-749623781 + this.host = rawHttpConfig.host === '0' ? '0.0.0.0' : rawHttpConfig.host; this.port = rawHttpConfig.port; this.cors = rawHttpConfig.cors; this.customResponseHeaders = Object.entries(rawHttpConfig.customResponseHeaders ?? {}).reduce( From 978fa4325c900120a4605de4bbb957844466e723 Mon Sep 17 00:00:00 2001 From: Pete Hampton Date: Tue, 22 Dec 2020 19:55:40 +0000 Subject: [PATCH 059/100] Add run and alert count to task state. (#86776) --- .../server/lib/telemetry/task.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/task.ts b/x-pack/plugins/security_solution/server/lib/telemetry/task.ts index 28b8524f645168..a723cb9a3e6378 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/task.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/task.ts @@ -36,6 +36,8 @@ export class TelemetryDiagTask { title: 'Security Solution Telemetry Diagnostics task', timeout: TelemetryDiagTaskConstants.TIMEOUT, createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + const { state } = taskInstance; + return { run: async () => { const executeTo = moment().utc().toISOString(); @@ -43,11 +45,13 @@ export class TelemetryDiagTask { executeTo, taskInstance.state?.lastExecutionTimestamp ); - await this.runTask(taskInstance.id, executeFrom, executeTo); + const hits = await this.runTask(taskInstance.id, executeFrom, executeTo); return { state: { lastExecutionTimestamp: executeTo, + lastDiagAlertCount: hits, + runs: (state.runs || 0) + 1, }, }; }, @@ -81,7 +85,7 @@ export class TelemetryDiagTask { schedule: { interval: TelemetryDiagTaskConstants.INTERVAL, }, - state: {}, + state: { runs: 0 }, params: { version: TelemetryDiagTaskConstants.VERSION }, }); } catch (e) { @@ -97,13 +101,13 @@ export class TelemetryDiagTask { this.logger.debug(`Running task ${taskId}`); if (taskId !== this.getTaskId()) { this.logger.debug(`Outdated task running: ${taskId}`); - return; + return 0; } const isOptedIn = await this.sender.isTelemetryOptedIn(); if (!isOptedIn) { this.logger.debug(`Telemetry is not opted-in.`); - return; + return 0; } const response = await this.sender.fetchDiagnosticAlerts(searchFrom, searchTo); @@ -111,11 +115,12 @@ export class TelemetryDiagTask { const hits = response.hits?.hits || []; if (!Array.isArray(hits) || !hits.length) { this.logger.debug('no diagnostic alerts retrieved'); - return; + return 0; } + this.logger.debug(`Received ${hits.length} diagnostic alerts`); const diagAlerts: TelemetryEvent[] = hits.map((h) => h._source); - this.logger.debug(`Received ${diagAlerts.length} diagnostic alerts`); this.sender.queueTelemetryEvents(diagAlerts); + return diagAlerts.length; }; } From 23c9c7e302dce25ba80b6de773b63d7afaf68a15 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Tue, 22 Dec 2020 15:41:33 -0500 Subject: [PATCH 060/100] fix uptime monitors donut chart labels (#86319) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../__snapshots__/donut_chart.test.tsx.snap | 12 ++++++---- .../donut_chart_legend.test.tsx.snap | 21 ---------------- .../donut_chart_legend_row.test.tsx.snap | 1 + .../__tests__/donut_chart_legend.test.tsx | 24 +++++++++++++++---- .../common/charts/donut_chart_legend.tsx | 4 ++-- .../common/charts/donut_chart_legend_row.tsx | 2 +- 6 files changed, 32 insertions(+), 32 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap index 1a18cf5651bee2..21d65f63783c5a 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap @@ -490,8 +490,9 @@ exports[`DonutChart component renders a donut chart 1`] = ` - Up + Down - Down + Up - Up + Down - Down + Up - - - - -`; diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap index bc6033ea7109ab..6e2a58cf528edb 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap @@ -15,6 +15,7 @@ exports[`DonutChartLegendRow passes appropriate props 1`] = ` Foo diff --git a/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx index 1b71b87884fb80..2ef02106e6e668 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/__tests__/donut_chart_legend.test.tsx @@ -3,14 +3,30 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; +import { renderWithIntl } from '@kbn/test/jest'; import { DonutChartLegend } from '../donut_chart_legend'; -import { shallowWithIntl } from '@kbn/test/jest'; -import React from 'react'; + +import { STATUS_DOWN_LABEL, STATUS_UP_LABEL } from '../../translations'; describe('DonutChartLegend', () => { it('applies valid props as expected', () => { - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); + const up = 45; + const down = 23; + const component = renderWithIntl(); + + expect( + component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up.label"]').text() + ).toBe(STATUS_UP_LABEL); + expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.up"]').text()).toBe( + `${up}` + ); + expect( + component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down.label"]').text() + ).toBe(STATUS_DOWN_LABEL); + expect(component.find('[data-test-subj="xpack.uptime.snapshot.donutChart.down"]').text()).toBe( + `${down}` + ); }); }); diff --git a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx index f3b50895fff639..92b9c72e3f1e68 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx @@ -34,14 +34,14 @@ export const DonutChartLegend = ({ down, up }: Props) => { diff --git a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx index fc67a86db3b487..0f637aff3bfa4e 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx @@ -31,7 +31,7 @@ export const DonutChartLegendRow = ({ color, content, message, 'data-test-subj': - + {message} From 59298c5f910f64a2940ff287ebe4afccb9d023e8 Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Tue, 22 Dec 2020 17:01:39 -0500 Subject: [PATCH 061/100] [Security Solution] [Detections] Add "read index" privilege check on rule execution (#83134) * adds privilege check in rule execution function, need to abstract these lines into a util function to be used in create rules and use that check on the UI too * fixes tests * cleanup code, adds a unit test * set rule to failure status if the rule does not have read privileges to ANY of the index patterns provided --- .../signals/signal_rule_alert_type.test.ts | 64 +++++++++++++++++++ .../signals/signal_rule_alert_type.ts | 47 +++++++++++++- .../lib/detection_engine/signals/utils.ts | 14 ++++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 9c2ea0945297e3..0ba2507e2e649a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -16,6 +16,7 @@ import { getListsClient, getExceptions, sortExceptionItems, + checkPrivileges, } from './utils'; import { parseScheduleDates } from '../../../../common/detection_engine/parse_schedule_dates'; import { RuleExecutorOptions, SearchAfterAndBulkCreateReturnType } from './types'; @@ -42,6 +43,7 @@ jest.mock('./utils', () => { getListsClient: jest.fn(), getExceptions: jest.fn(), sortExceptionItems: jest.fn(), + checkPrivileges: jest.fn(), }; }); jest.mock('../notifications/schedule_notification_actions'); @@ -105,6 +107,7 @@ describe('rules_notification_alert_type', () => { find: jest.fn(), goingToRun: jest.fn(), error: jest.fn(), + partialFailure: jest.fn(), }; (ruleStatusServiceFactory as jest.Mock).mockReturnValue(ruleStatusService); (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(0)); @@ -124,6 +127,21 @@ describe('rules_notification_alert_type', () => { searchAfterTimes: [], createdSignalsCount: 10, }); + (checkPrivileges as jest.Mock).mockImplementation((_, indices) => { + return { + index: indices.reduce( + (acc: { index: { [x: string]: { read: boolean } } }, index: string) => { + return { + [index]: { + read: true, + }, + ...acc, + }; + }, + {} + ), + }; + }); alertServices.callCluster.mockResolvedValue({ hits: { total: { value: 10 }, @@ -170,6 +188,52 @@ describe('rules_notification_alert_type', () => { }); }); + it('should set a partial failure for when rules cannot read ALL provided indices', async () => { + (checkPrivileges as jest.Mock).mockResolvedValueOnce({ + username: 'elastic', + has_all_requested: false, + cluster: {}, + index: { + 'myfa*': { + read: true, + }, + 'some*': { + read: false, + }, + }, + application: {}, + }); + payload.params.index = ['some*', 'myfa*']; + await alert.executor(payload); + expect(ruleStatusService.partialFailure).toHaveBeenCalled(); + expect(ruleStatusService.partialFailure.mock.calls[0][0]).toContain( + 'Missing required read permissions on indexes: ["some*"]' + ); + }); + + it('should set a failure status for when rules cannot read ANY provided indices', async () => { + (checkPrivileges as jest.Mock).mockResolvedValueOnce({ + username: 'elastic', + has_all_requested: false, + cluster: {}, + index: { + 'myfa*': { + read: false, + }, + 'some*': { + read: false, + }, + }, + application: {}, + }); + payload.params.index = ['some*', 'myfa*']; + await alert.executor(payload); + expect(ruleStatusService.error).toHaveBeenCalled(); + expect(ruleStatusService.error.mock.calls[0][0]).toContain( + 'The rule does not have read privileges to any of the following indices: ["myfa*","some*"]' + ); + }); + it('should NOT warn about the gap between runs if gap small', async () => { (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(1, 'm')); (getGapMaxCatchupRatio as jest.Mock).mockReturnValue({ 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 476b9aa56f572d..1b8fd580e1718c 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 @@ -7,6 +7,7 @@ /* eslint-disable complexity */ import { Logger, KibanaRequest } from 'src/core/server'; +import { partition } from 'lodash'; import { SIGNALS_ID, @@ -41,6 +42,7 @@ import { createSearchAfterReturnType, mergeReturns, createSearchAfterReturnTypeFromResponse, + checkPrivileges, } from './utils'; import { signalParamsSchema } from './signal_params_schema'; import { siemRuleActionGroups } from './siem_rule_action_groups'; @@ -171,8 +173,51 @@ export const signalRulesAlertType = ({ logger.debug(buildRuleMessage('[+] Starting Signal Rule execution')); logger.debug(buildRuleMessage(`interval: ${interval}`)); + let wroteStatus = false; await ruleStatusService.goingToRun(); + // check if rule has permissions to access given index pattern + // move this collection of lines into a function in utils + // so that we can use it in create rules route, bulk, etc. + try { + const inputIndex = await getInputIndex(services, version, index); + const privileges = await checkPrivileges(services, inputIndex); + + const indexNames = Object.keys(privileges.index); + const [indexesWithReadPrivileges, indexesWithNoReadPrivileges] = partition( + indexNames, + (indexName) => privileges.index[indexName].read + ); + + if ( + indexesWithReadPrivileges.length > 0 && + indexesWithNoReadPrivileges.length >= indexesWithReadPrivileges.length + ) { + // some indices have read privileges others do not. + // set a partial failure status + const errorString = `Missing required read permissions on indexes: ${JSON.stringify( + indexesWithNoReadPrivileges + )}`; + logger.debug(buildRuleMessage(errorString)); + await ruleStatusService.partialFailure(errorString); + wroteStatus = true; + } else if ( + indexesWithReadPrivileges.length === 0 && + indexesWithNoReadPrivileges.length === indexNames.length + ) { + // none of the indices had read privileges so set the status to failed + // since we can't search on any indices we do not have read privileges on + const errorString = `The rule does not have read privileges to any of the following indices: ${JSON.stringify( + indexesWithNoReadPrivileges + )}`; + logger.debug(buildRuleMessage(errorString)); + await ruleStatusService.error(errorString); + wroteStatus = true; + } + } catch (exc) { + logger.error(buildRuleMessage(`Check privileges failed to execute ${exc}`)); + } + const gap = getGapBetweenRuns({ previousStartedAt, interval, from, to }); if (gap != null && gap.asMilliseconds() > 0) { const fromUnit = from[from.length - 1]; @@ -600,7 +645,7 @@ export const signalRulesAlertType = ({ `[+] Finished indexing ${result.createdSignalsCount} signals into ${outputIndex}` ) ); - if (!hasError) { + if (!hasError && !wroteStatus) { await ruleStatusService.success('succeeded', { bulkCreateTimeDurations: result.bulkCreateTimes, searchAfterTimeDurations: result.searchAfterTimes, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 18f6e8d127b1b3..92a27319723d6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -52,6 +52,20 @@ export const shorthandMap = { }, }; +export const checkPrivileges = async (services: AlertServices, indices: string[]) => + services.callCluster('transport.request', { + path: '/_security/user/_has_privileges', + method: 'POST', + body: { + index: [ + { + names: indices ?? [], + privileges: ['read'], + }, + ], + }, + }); + export const getGapMaxCatchupRatio = ({ logger, previousStartedAt, From 39dc4d0237a6ab690d8d43d69f665b88d965e22e Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 22 Dec 2020 17:58:22 -0500 Subject: [PATCH 062/100] fix group edit popup format (#86836) --- .../group_selector/group_list/group_list.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/group_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/group_list.js index d989064c5057f2..b17172aba6a959 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/group_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_list/group_list.js @@ -7,7 +7,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { EuiIcon, keys } from '@elastic/eui'; +import { EuiFlexItem, EuiFlexGroup, EuiIcon, keys } from '@elastic/eui'; import { JobGroup } from '../../../job_group'; @@ -97,8 +97,14 @@ export class GroupList extends Component { onClick={() => this.selectGroup(g)} ref={(ref) => this.setRef(ref, index)} > - - + + + + + + + +

    ))}
    From f2105c85fbcc4c311ae105440338c693cb18bd86 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 22 Dec 2020 16:27:05 -0700 Subject: [PATCH 063/100] Sample data usage collector es client migration (#86657) * Uses new esClient to fetch sample data telemetry * Import SearchResponse from core --- .../sample_data/usage/collector_fetch.test.ts | 2 +- .../sample_data/usage/collector_fetch.ts | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts index 54fed3db1de4d2..58bb037f8d614e 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.test.ts @@ -23,7 +23,7 @@ import { fetchProvider } from './collector_fetch'; const getMockFetchClients = (hits?: unknown[]) => { const fetchParamsMock = createCollectorFetchContextMock(); - fetchParamsMock.callCluster.mockResolvedValue({ hits: { hits } }); + fetchParamsMock.esClient.search = jest.fn().mockResolvedValue({ body: { hits: { hits } } }); return fetchParamsMock; }; diff --git a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts index 7df9b14d2efb18..ef958873d96632 100644 --- a/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts +++ b/src/plugins/home/server/services/sample_data/usage/collector_fetch.ts @@ -19,6 +19,7 @@ import { get } from 'lodash'; import moment from 'moment'; +import { SearchResponse } from 'src/core/server'; import { CollectorFetchContext } from '../../../../../usage_collection/server'; interface SearchHit { @@ -41,17 +42,23 @@ export interface TelemetryResponse { last_uninstall_set: string | null; } +type ESResponse = SearchResponse; + export function fetchProvider(index: string) { - return async ({ callCluster }: CollectorFetchContext) => { - const response = await callCluster('search', { - index, - body: { - query: { term: { type: { value: 'sample-data-telemetry' } } }, - _source: { includes: ['sample-data-telemetry', 'type', 'updated_at'] }, + return async ({ esClient }: CollectorFetchContext) => { + const { body: response } = await esClient.search( + { + index, + body: { + query: { term: { type: { value: 'sample-data-telemetry' } } }, + _source: { includes: ['sample-data-telemetry', 'type', 'updated_at'] }, + }, + filter_path: 'hits.hits._id,hits.hits._source', }, - filter_path: 'hits.hits._id,hits.hits._source', - ignore: [404], - }); + { + ignore: [404], + } + ); const getLast = ( dataSet: string, From c9879c6fbdf3fc919be49497faa20d56c13115d0 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 22 Dec 2020 16:31:14 -0800 Subject: [PATCH 064/100] Skip test preventing ES snapshot promotion Signed-off-by: Tyler Smalley --- .../security_and_spaces/tests/generating_signals.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index 64ee42fdb3f3ee..e3264786ff38bb 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -201,7 +201,8 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('EQL Rules', () => { + // ES PROMOTION FAILURE: http://github.com/elastic/kibana/issues/86709 + describe.skip('EQL Rules', () => { it('generates signals from EQL sequences in the expected form', async () => { const rule: EqlCreateSchema = { ...getRuleForSignalTesting(['auditbeat-*']), From 62b5ef9459797724c54f54ced8469e708423fa1a Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 22 Dec 2020 17:52:32 -0800 Subject: [PATCH 065/100] Minor responsive text overflow tweaks to ApiCodeExample POST (#86848) --- .../creation_mode_components/api_code_example.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx index 9ebe404659ca2c..c33cda9f7e4290 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/creation_mode_components/api_code_example.tsx @@ -23,6 +23,8 @@ import { EuiBadge, EuiCode, EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { getEnterpriseSearchUrl } from '../../../../shared/enterprise_search_url'; @@ -95,8 +97,14 @@ export const FlyoutBody: React.FC = () => { - POST - {documentsApiUrl} + + + POST + + + {documentsApiUrl} + + {dedent(` From e3896050fc022b2ac981e48e8e63e427ba3a2e6e Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 22 Dec 2020 17:53:07 -0800 Subject: [PATCH 066/100] [App Search] Misc credentials key UI enhancements (#81817) * Fix screen reader still reading out bullet bullet bullet * Even out horizontal spacing * Break long password text * Make EUI table cell full width on mobile --- .../credentials/credentials_list/credentials_list.test.tsx | 6 +++++- .../credentials/credentials_list/credentials_list.tsx | 5 +++++ .../components/credentials/credentials_list/key.tsx | 1 + .../public/applications/shared/hidden_text/hidden_text.tsx | 4 +++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx index 4f5ded0a3ccc17..ec018f0faf5ff1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.test.tsx @@ -204,7 +204,11 @@ describe('Credentials', () => { copy: expect.any(Function), toggleIsHidden: expect.any(Function), isHidden: expect.any(Boolean), - text: •••••••, + text: ( + + ••••••• + + ), }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx index 9240bade4975e1..df85a9c3053a69 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/credentials_list.tsx @@ -40,6 +40,7 @@ export const CredentialsList: React.FC = () => { { name: 'Key', width: '36%', + className: 'eui-textBreakAll', render: (token: ApiToken) => { const { key } = token; if (!key) return null; @@ -60,6 +61,10 @@ export const CredentialsList: React.FC = () => { ); }, + mobileOptions: { + // @ts-ignore - EUI's type definitions need to be updated + width: '100%', + }, }, { name: 'Modes', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx index fa2d124cbccdf6..8ea2b6c284fc6e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/credentials/credentials_list/key.tsx @@ -39,6 +39,7 @@ export const Key: React.FC = ({ copy, toggleIsHidden, isHidden, text }) = iconType={hideIcon} aria-label={hideIconLabel} aria-pressed={!isHidden} + style={{ marginRight: '0.25em' }} /> {text} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/hidden_text/hidden_text.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/hidden_text/hidden_text.tsx index 69176b8a139e79..dae22a47035c41 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/hidden_text/hidden_text.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/hidden_text/hidden_text.tsx @@ -25,7 +25,9 @@ export const HiddenText: React.FC = ({ text, children }) => { defaultMessage: 'Hidden text', }); const hiddenText = isHidden ? ( - {text.replace(/./g, '•')} + + {text.replace(/./g, '•')} + ) : ( text ); From 35b10b53549c954a5df2ed3446b84418eae5b45b Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Tue, 22 Dec 2020 21:14:22 -0500 Subject: [PATCH 067/100] [Security Solution] [Detections] Bug fix for read privilege check during rule exec (#86852) --- .../detection_engine/signals/signal_rule_alert_type.test.ts | 5 ++++- .../lib/detection_engine/signals/signal_rule_alert_type.ts | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 0ba2507e2e649a..f8983061d7a7a8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -197,13 +197,16 @@ describe('rules_notification_alert_type', () => { 'myfa*': { read: true, }, + 'anotherindex*': { + read: true, + }, 'some*': { read: false, }, }, application: {}, }); - payload.params.index = ['some*', 'myfa*']; + payload.params.index = ['some*', 'myfa*', 'anotherindex*']; await alert.executor(payload); expect(ruleStatusService.partialFailure).toHaveBeenCalled(); expect(ruleStatusService.partialFailure.mock.calls[0][0]).toContain( 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 1b8fd580e1718c..8a219d926a96d4 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 @@ -189,10 +189,7 @@ export const signalRulesAlertType = ({ (indexName) => privileges.index[indexName].read ); - if ( - indexesWithReadPrivileges.length > 0 && - indexesWithNoReadPrivileges.length >= indexesWithReadPrivileges.length - ) { + if (indexesWithReadPrivileges.length > 0 && indexesWithNoReadPrivileges.length > 0) { // some indices have read privileges others do not. // set a partial failure status const errorString = `Missing required read permissions on indexes: ${JSON.stringify( From 3dfb1aba2a652b5f1464604e0e59302d9fa1d122 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Wed, 23 Dec 2020 00:27:37 -0500 Subject: [PATCH 068/100] [Security Solution][Detections] - Fix export on exceptions list view (#86135) ## Summary This PR addresses a fix on the exceptions list table export functionality. A dedicated route for exception list export needed to be created. List is exported into an `.ndjson` format. Exception lists consist of two elements - the list itself, and its items. The export file should now contain both these elements, the list followed by its items. --- ...export_exception_list_query_schema.mock.ts | 15 +++ ...export_exception_list_query_schema.test.ts | 80 ++++++++++++++ .../export_exception_list_query_schema.ts | 20 ++++ .../lists/common/schemas/request/index.ts | 1 + .../lists/public/exceptions/api.test.ts | 47 ++++++++ x-pack/plugins/lists/public/exceptions/api.ts | 25 +++++ .../lists/public/exceptions/hooks/use_api.ts | 25 ++++- .../plugins/lists/public/exceptions/types.ts | 19 ++++ .../routes/export_exception_list_route.ts | 103 ++++++++++++++++++ x-pack/plugins/lists/server/routes/index.ts | 1 + .../lists/server/routes/init_routes.ts | 2 + .../auto_download}/auto_download.test.tsx | 0 .../auto_download}/auto_download.tsx | 0 .../value_lists_management_modal/modal.tsx | 2 +- .../rules/all/exceptions/columns.tsx | 23 +++- .../rules/all/exceptions/exceptions_table.tsx | 94 +++++++++++++--- .../rules/all/exceptions/translations.ts | 23 +++- .../exceptions/use_all_exception_lists.tsx | 2 +- 18 files changed, 459 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.mock.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.test.ts create mode 100644 x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.ts create mode 100644 x-pack/plugins/lists/server/routes/export_exception_list_route.ts rename x-pack/plugins/security_solution/public/{detections/components/value_lists_management_modal => common/components/auto_download}/auto_download.test.tsx (100%) rename x-pack/plugins/security_solution/public/{detections/components/value_lists_management_modal => common/components/auto_download}/auto_download.tsx (100%) diff --git a/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.mock.ts new file mode 100644 index 00000000000000..4e6655ec1d1d61 --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.mock.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ID, LIST_ID, NAMESPACE_TYPE } from '../../constants.mock'; + +import { ExportExceptionListQuerySchema } from './export_exception_list_query_schema'; + +export const getExportExceptionListQuerySchemaMock = (): ExportExceptionListQuerySchema => ({ + id: ID, + list_id: LIST_ID, + namespace_type: NAMESPACE_TYPE, +}); diff --git a/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.test.ts new file mode 100644 index 00000000000000..6af7f6323c135e --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { left } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; + +import { exactCheck, foldLeftRight, getPaths } from '../../shared_imports'; + +import { + ExportExceptionListQuerySchema, + exportExceptionListQuerySchema, +} from './export_exception_list_query_schema'; +import { getExportExceptionListQuerySchemaMock } from './export_exception_list_query_schema.mock'; + +describe('export_exception_list_schema', () => { + test('it should validate a typical lists request', () => { + const payload = getExportExceptionListQuerySchemaMock(); + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should NOT accept an undefined for an id', () => { + const payload = getExportExceptionListQuerySchemaMock(); + // @ts-expect-error + delete payload.id; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['Invalid value "undefined" supplied to "id"']); + expect(message.schema).toEqual({}); + }); + + test('it should default namespace_type to "single" if an undefined given for namespacetype', () => { + const payload = getExportExceptionListQuerySchemaMock(); + delete payload.namespace_type; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(message.schema).toEqual({ + id: 'uuid_here', + list_id: 'some-list-id', + namespace_type: 'single', + }); + }); + + test('it should NOT accept an undefined for an list_id', () => { + const payload = getExportExceptionListQuerySchemaMock(); + // @ts-expect-error + delete payload.list_id; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "list_id"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not allow an extra key to be sent in', () => { + const payload: ExportExceptionListQuerySchema & { + extraKey?: string; + } = getExportExceptionListQuerySchemaMock(); + payload.extraKey = 'some new value'; + const decoded = exportExceptionListQuerySchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extraKey"']); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.ts b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.ts new file mode 100644 index 00000000000000..b5061e903a824f --- /dev/null +++ b/x-pack/plugins/lists/common/schemas/request/export_exception_list_query_schema.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +import { id, list_id, namespace_type } from '../common/schemas'; + +export const exportExceptionListQuerySchema = t.exact( + t.type({ + id, + list_id, + namespace_type, + // TODO: Add file_name here with a default value + }) +); + +export type ExportExceptionListQuerySchema = t.OutputOf; diff --git a/x-pack/plugins/lists/common/schemas/request/index.ts b/x-pack/plugins/lists/common/schemas/request/index.ts index 172d73a5c7377a..36e41bf52aa40f 100644 --- a/x-pack/plugins/lists/common/schemas/request/index.ts +++ b/x-pack/plugins/lists/common/schemas/request/index.ts @@ -14,6 +14,7 @@ export * from './delete_exception_list_item_schema'; export * from './delete_exception_list_schema'; export * from './delete_list_item_schema'; export * from './delete_list_schema'; +export * from './export_exception_list_query_schema'; export * from './export_list_item_query_schema'; export * from './find_endpoint_list_item_schema'; export * from './find_exception_list_item_schema'; diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index e45403e319c29e..7570e1f050abba 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -25,6 +25,7 @@ import { addExceptionListItem, deleteExceptionListById, deleteExceptionListItemById, + exportExceptionList, fetchExceptionListById, fetchExceptionListItemById, fetchExceptionLists, @@ -870,4 +871,50 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual({}); }); }); + + describe('#exportExceptionList', () => { + const blob: Blob = { + arrayBuffer: jest.fn(), + size: 89, + slice: jest.fn(), + stream: jest.fn(), + text: jest.fn(), + type: 'json', + } as Blob; + + beforeEach(() => { + httpMock.fetch.mockResolvedValue(blob); + }); + + test('it invokes "exportExceptionList" with expected url and body values', async () => { + await exportExceptionList({ + http: httpMock, + id: 'some-id', + listId: 'list-id', + namespaceType: 'single', + signal: abortCtrl.signal, + }); + + expect(httpMock.fetch).toHaveBeenCalledWith('/api/exception_lists/_export', { + method: 'GET', + query: { + id: 'some-id', + list_id: 'list-id', + namespace_type: 'single', + }, + signal: abortCtrl.signal, + }); + }); + + test('it returns expected list to export on success', async () => { + const exceptionResponse = await exportExceptionList({ + http: httpMock, + id: 'some-id', + listId: 'list-id', + namespaceType: 'single', + signal: abortCtrl.signal, + }); + expect(exceptionResponse).toEqual(blob); + }); + }); }); diff --git a/x-pack/plugins/lists/public/exceptions/api.ts b/x-pack/plugins/lists/public/exceptions/api.ts index fc0c8934d6397d..f7032c22cb6c26 100644 --- a/x-pack/plugins/lists/public/exceptions/api.ts +++ b/x-pack/plugins/lists/public/exceptions/api.ts @@ -41,6 +41,7 @@ import { ApiCallByIdProps, ApiCallByListIdProps, ApiCallFetchExceptionListsProps, + ExportExceptionListProps, UpdateExceptionListItemProps, UpdateExceptionListProps, } from './types'; @@ -537,3 +538,27 @@ export const addEndpointExceptionList = async ({ return Promise.reject(error); } }; + +/** + * Fetch an ExceptionList by providing a ExceptionList ID + * + * @param http Kibana http service + * @param id ExceptionList ID (not list_id) + * @param listId ExceptionList LIST_ID (not id) + * @param namespaceType ExceptionList namespace_type + * @param signal to cancel request + * + * @throws An error if response is not OK + */ +export const exportExceptionList = async ({ + http, + id, + listId, + namespaceType, + signal, +}: ExportExceptionListProps): Promise => + http.fetch(`${EXCEPTION_LIST_URL}/_export`, { + method: 'GET', + query: { id, list_id: listId, namespace_type: namespaceType }, + signal, + }); diff --git a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts b/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts index def2f2626b8ec3..31a8d3ac5f598d 100644 --- a/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts +++ b/x-pack/plugins/lists/public/exceptions/hooks/use_api.ts @@ -9,7 +9,7 @@ import { useMemo } from 'react'; import * as Api from '../api'; import { HttpStart } from '../../../../../../src/core/public'; import { ExceptionListItemSchema, ExceptionListSchema } from '../../../common/schemas'; -import { ApiCallFindListsItemsMemoProps, ApiCallMemoProps } from '../types'; +import { ApiCallFindListsItemsMemoProps, ApiCallMemoProps, ApiListExportProps } from '../types'; import { getIdsAndNamespaces } from '../utils'; export interface ExceptionsApi { @@ -22,6 +22,7 @@ export interface ExceptionsApi { arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void } ) => Promise; getExceptionListsItems: (arg: ApiCallFindListsItemsMemoProps) => Promise; + exportExceptionList: (arg: ApiListExportProps) => Promise; } export const useApi = (http: HttpStart): ExceptionsApi => { @@ -67,6 +68,28 @@ export const useApi = (http: HttpStart): ExceptionsApi => { onError(error); } }, + async exportExceptionList({ + id, + listId, + namespaceType, + onError, + onSuccess, + }: ApiListExportProps): Promise { + const abortCtrl = new AbortController(); + + try { + const blob = await Api.exportExceptionList({ + http, + id, + listId, + namespaceType, + signal: abortCtrl.signal, + }); + onSuccess(blob); + } catch (error) { + onError(error); + } + }, async getExceptionItem({ id, namespaceType, diff --git a/x-pack/plugins/lists/public/exceptions/types.ts b/x-pack/plugins/lists/public/exceptions/types.ts index 02b78bc1a5e585..6a238e22344b66 100644 --- a/x-pack/plugins/lists/public/exceptions/types.ts +++ b/x-pack/plugins/lists/public/exceptions/types.ts @@ -90,6 +90,17 @@ export interface ApiCallMemoProps { onSuccess: () => void; } +// TODO: Switch to use ApiCallMemoProps +// after cleaning up exceptions/api file to +// remove unnecessary validation checks +export interface ApiListExportProps { + id: string; + listId: string; + namespaceType: NamespaceType; + onError: (err: Error) => void; + onSuccess: (blob: Blob) => void; +} + export interface ApiCallFindListsItemsMemoProps { lists: ExceptionListIdentifiers[]; filterOptions: FilterExceptionsOptions[]; @@ -156,3 +167,11 @@ export interface AddEndpointExceptionListProps { http: HttpStart; signal: AbortSignal; } + +export interface ExportExceptionListProps { + http: HttpStart; + id: string; + listId: string; + namespaceType: NamespaceType; + signal: AbortSignal; +} diff --git a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts new file mode 100644 index 00000000000000..1394bf48cd2c7a --- /dev/null +++ b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'kibana/server'; + +import { EXCEPTION_LIST_URL } from '../../common/constants'; +import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; +import { exportExceptionListQuerySchema } from '../../common/schemas'; + +import { getExceptionListClient } from './utils'; + +export const exportExceptionListRoute = (router: IRouter): void => { + router.get( + { + options: { + tags: ['access:lists-read'], + }, + path: `${EXCEPTION_LIST_URL}/_export`, + validate: { + query: buildRouteValidation(exportExceptionListQuerySchema), + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + const { id, list_id: listId, namespace_type: namespaceType } = request.query; + const exceptionLists = getExceptionListClient(context); + const exceptionList = await exceptionLists.getExceptionList({ + id, + listId, + namespaceType, + }); + + if (exceptionList == null) { + return siemResponse.error({ + body: `list_id: ${listId} does not exist`, + statusCode: 400, + }); + } else { + const { exportData: exportList } = getExport([exceptionList]); + const listItems = await exceptionLists.findExceptionListItem({ + filter: undefined, + listId, + namespaceType, + page: 1, + perPage: 10000, + sortField: 'exception-list.created_at', + sortOrder: 'desc', + }); + + const { exportData: exportListItems, exportDetails } = getExport(listItems?.data ?? []); + + const responseBody = [ + exportList, + exportListItems, + { exception_list_items_details: exportDetails }, + ]; + + // TODO: Allow the API to override the name of the file to export + const fileName = exceptionList.list_id; + return response.ok({ + body: transformDataToNdjson(responseBody), + headers: { + 'Content-Disposition': `attachment; filename="${fileName}"`, + 'Content-Type': 'application/ndjson', + }, + }); + } + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +const transformDataToNdjson = (data: unknown[]): string => { + if (data.length !== 0) { + const dataString = data.map((dataItem) => JSON.stringify(dataItem)).join('\n'); + return `${dataString}\n`; + } else { + return ''; + } +}; + +export const getExport = ( + data: unknown[] +): { + exportData: string; + exportDetails: string; +} => { + const ndjson = transformDataToNdjson(data); + const exportDetails = JSON.stringify({ + exported_count: data.length, + }); + return { exportData: ndjson, exportDetails: `${exportDetails}\n` }; +}; diff --git a/x-pack/plugins/lists/server/routes/index.ts b/x-pack/plugins/lists/server/routes/index.ts index 0d99d726d232da..a1a54a88c0ed0c 100644 --- a/x-pack/plugins/lists/server/routes/index.ts +++ b/x-pack/plugins/lists/server/routes/index.ts @@ -17,6 +17,7 @@ export * from './delete_exception_list_item_route'; export * from './delete_list_index_route'; export * from './delete_list_item_route'; export * from './delete_list_route'; +export * from './export_exception_list_route'; export * from './export_list_item_route'; export * from './find_endpoint_list_item_route'; export * from './find_exception_list_item_route'; diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 163126f1277c17..1f29d0aaeeb485 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -22,6 +22,7 @@ import { deleteListIndexRoute, deleteListItemRoute, deleteListRoute, + exportExceptionListRoute, exportListItemRoute, findEndpointListItemRoute, findExceptionListItemRoute, @@ -76,6 +77,7 @@ export const initRoutes = (router: IRouter, config: ConfigType): void => { updateExceptionListRoute(router); deleteExceptionListRoute(router); findExceptionListRoute(router); + exportExceptionListRoute(router); // exception list items createExceptionListItemRoute(router); diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.test.tsx b/x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.test.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.test.tsx rename to x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.test.tsx diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.tsx b/x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/auto_download.tsx rename to x-pack/plugins/security_solution/public/common/components/auto_download/auto_download.tsx diff --git a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx index f0e47fcd5c1044..57c4eee95cd8cc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/value_lists_management_modal/modal.tsx @@ -32,8 +32,8 @@ import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import * as i18n from './translations'; import { buildColumns } from './table_helpers'; import { ValueListsForm } from './form'; -import { AutoDownload } from './auto_download'; import { ReferenceErrorModal } from './reference_error_modal'; +import { AutoDownload } from '../../../common/components/auto_download/auto_download'; interface ValueListsModalProps { onClose: () => void; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx index 57b86119dc164b..79cfd53a4fa001 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/columns.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { EuiButtonIcon, EuiBasicTableColumn, EuiToolTip } from '@elastic/eui'; import { History } from 'history'; +import { NamespaceType } from '../../../../../../../../lists/common'; import { FormatUrl } from '../../../../../../common/components/link_to'; import { LinkAnchor } from '../../../../../../common/components/links'; import * as i18n from './translations'; @@ -16,7 +17,11 @@ import { ExceptionListInfo } from './use_all_exception_lists'; import { getRuleDetailsUrl } from '../../../../../../common/components/link_to/redirect_to_detection_engine'; export type AllExceptionListsColumns = EuiBasicTableColumn; -export type Func = (listId: string) => () => void; +export type Func = (arg: { + id: string; + listId: string; + namespaceType: NamespaceType; +}) => () => void; export const getAllExceptionListsColumns = ( onExport: Func, @@ -96,9 +101,13 @@ export const getAllExceptionListsColumns = ( align: 'center', isExpander: false, width: '25px', - render: (list: ExceptionListInfo) => ( + render: ({ id, list_id: listId, namespace_type: namespaceType }: ExceptionListInfo) => ( @@ -108,10 +117,14 @@ export const getAllExceptionListsColumns = ( align: 'center', width: '25px', isExpander: false, - render: (list: ExceptionListInfo) => ( + render: ({ id, list_id: listId, namespace_type: namespaceType }: ExceptionListInfo) => ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx index 65aaaea06b40f0..ac9c558022c262 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo, useEffect, useCallback, useState, ChangeEvent } from 'react'; +import React, { useMemo, useEffect, useCallback, useState } from 'react'; import { EuiBasicTable, EuiEmptyPrompt, @@ -16,8 +16,10 @@ import styled from 'styled-components'; import { History } from 'history'; import { set } from 'lodash/fp'; +import { AutoDownload } from '../../../../../../common/components/auto_download/auto_download'; +import { NamespaceType } from '../../../../../../../../lists/common'; import { useKibana } from '../../../../../../common/lib/kibana'; -import { useExceptionLists } from '../../../../../../shared_imports'; +import { useApi, useExceptionLists } from '../../../../../../shared_imports'; import { FormatUrl } from '../../../../../../common/components/link_to'; import { HeaderSection } from '../../../../../../common/components/header_section'; import { Loader } from '../../../../../../common/components/loader'; @@ -51,6 +53,7 @@ export const ExceptionListsTable = React.memo( const { services: { http, notifications }, } = useKibana(); + const { exportExceptionList } = useApi(http); const [filters, setFilters] = useState({ name: null, list_id: null, @@ -69,10 +72,67 @@ export const ExceptionListsTable = React.memo( }); const [initLoading, setInitLoading] = useState(true); const [lastUpdated, setLastUpdated] = useState(Date.now()); + const [deletingListIds, setDeletingListIds] = useState([]); + const [exportingListIds, setExportingListIds] = useState([]); + const [exportDownload, setExportDownload] = useState<{ name?: string; blob?: Blob }>({}); - const handleDelete = useCallback((id: string) => () => {}, []); + const handleDelete = useCallback( + ({ + id, + listId, + namespaceType, + }: { + id: string; + listId: string; + namespaceType: NamespaceType; + }) => async () => { + try { + setDeletingListIds((ids) => [...ids, id]); + // route to patch rules with associated exception list + } catch (error) { + notifications.toasts.addError(error, { title: i18n.EXCEPTION_DELETE_ERROR }); + } finally { + setDeletingListIds((ids) => [...ids.filter((_id) => _id !== id)]); + } + }, + [notifications.toasts] + ); - const handleExport = useCallback((id: string) => () => {}, []); + const handleExportSuccess = useCallback( + (listId: string) => (blob: Blob): void => { + setExportDownload({ name: listId, blob }); + }, + [] + ); + + const handleExportError = useCallback( + (err: Error) => { + notifications.toasts.addError(err, { title: i18n.EXCEPTION_EXPORT_ERROR }); + }, + [notifications.toasts] + ); + + const handleExport = useCallback( + ({ + id, + listId, + namespaceType, + }: { + id: string; + listId: string; + namespaceType: NamespaceType; + }) => async () => { + setExportingListIds((ids) => [...ids, id]); + await exportExceptionList({ + id, + listId, + namespaceType, + onError: handleExportError, + onSuccess: handleExportSuccess(listId), + }); + }, + [exportExceptionList, handleExportError, handleExportSuccess] + ); const exceptionsColumns = useMemo((): AllExceptionListsColumns[] => { return getAllExceptionListsColumns(handleExport, handleDelete, history, formatUrl); @@ -122,14 +182,6 @@ export const ExceptionListsTable = React.memo( setFilters(formattedFilter); }, []); - const handleSearchChange = useCallback( - (event: ChangeEvent) => { - const val = event.target.value; - handleSearch(val); - }, - [handleSearch] - ); - const paginationMemo = useMemo( () => ({ pageIndex: pagination.page - 1, @@ -140,8 +192,23 @@ export const ExceptionListsTable = React.memo( [pagination] ); + const handleOnDownload = useCallback(() => { + setExportDownload({}); + }, []); + + const tableItems = (data ?? []).map((item) => ({ + ...item, + isDeleting: deletingListIds.includes(item.id), + isExporting: exportingListIds.includes(item.id), + })); + return ( <> + <> {loadingTableInfo && ( @@ -162,7 +229,6 @@ export const ExceptionListsTable = React.memo( aria-label={i18n.EXCEPTIONS_LISTS_SEARCH_PLACEHOLDER} placeholder={i18n.EXCEPTIONS_LISTS_SEARCH_PLACEHOLDER} onSearch={handleSearch} - onChange={handleSearchChange} disabled={initLoading} incremental={false} fullWidth @@ -188,7 +254,7 @@ export const ExceptionListsTable = React.memo( columns={exceptionsColumns} isSelectable={!hasNoPermissions ?? false} itemId="id" - items={data ?? []} + items={tableItems} noItemsMessage={emptyPrompt} onChange={() => {}} pagination={paginationMemo} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts index 2eba8fb2e579b6..7483b8e943d309 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/translations.ts @@ -35,7 +35,7 @@ export const LIST_DATE_CREATED_TITLE = i18n.translate( ); export const LIST_DATE_UPDATED_TITLE = i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.all.exceptions.dateUPdatedTitle', + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.dateUpdatedTitle', { defaultMessage: 'Last edited', } @@ -75,3 +75,24 @@ export const NO_LISTS_BODY = i18n.translate( defaultMessage: "We weren't able to find any exception lists.", } ); + +export const EXCEPTION_EXPORT_SUCCESS = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.exportSuccess', + { + defaultMessage: 'Exception list export success', + } +); + +export const EXCEPTION_EXPORT_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.exportError', + { + defaultMessage: 'Exception list export error', + } +); + +export const EXCEPTION_DELETE_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.all.exceptions.deleteError', + { + defaultMessage: 'Error occurred deleting exception list', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx index 4b47080cc2da1f..3f343da6052134 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/use_all_exception_lists.tsx @@ -61,7 +61,7 @@ export const useAllExceptionLists = ({ const { data: rules } = await fetchRules({ pagination: { page: 1, - perPage: 500, + perPage: 10000, total: 0, }, signal: abortCtrl.signal, From 4f2fd01661a5095ce7b4db33909b6d1f6addc572 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 22 Dec 2020 23:05:42 -0800 Subject: [PATCH 069/100] [CI] Updates branch env variables (#86712) Signed-off-by: Tyler Smalley --- packages/kbn-apm-config-loader/src/config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts index 6e5a830d04b171..5e3d52cfd27d1a 100644 --- a/packages/kbn-apm-config-loader/src/config.ts +++ b/packages/kbn-apm-config-loader/src/config.ts @@ -153,8 +153,8 @@ export class ApmConfiguration { return { globalLabels: { - branch: process.env.ghprbSourceBranch || '', - targetBranch: process.env.ghprbTargetBranch || '', + branch: process.env.GIT_BRANCH || '', + targetBranch: process.env.PR_TARGET_BRANCH || '', ciBuildNumber: process.env.BUILD_NUMBER || '', isPr: process.env.GITHUB_PR_NUMBER ? true : false, prId: process.env.GITHUB_PR_NUMBER || '', From 21b34bcd242daeb402a0a43c51f821e0d8989987 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 23 Dec 2020 09:36:38 +0200 Subject: [PATCH 070/100] [Security Solution][Case] Fix case status dropdown on modals (#86243) --- .../all_cases/table_filters.test.tsx | 18 +++++++- .../components/all_cases/table_filters.tsx | 46 ++++++++++++------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx index 0c9a725f918e54..a92fc793c796e2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.test.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { mount } from 'enzyme'; import { CaseStatuses } from '../../../../../case/common/api'; -import { CasesTableFilters } from './table_filters'; import { TestProviders } from '../../../common/mock'; import { useGetTags } from '../../containers/use_get_tags'; import { useGetReporters } from '../../containers/use_get_reporters'; import { DEFAULT_FILTER_OPTIONS } from '../../containers/use_get_cases'; +import { CasesTableFilters } from './table_filters'; jest.mock('../../containers/use_get_reporters'); jest.mock('../../containers/use_get_tags'); @@ -151,4 +151,20 @@ describe('CasesTableFilters ', () => { ); expect(onFilterChanged).toHaveBeenCalledWith({ reporters: [{ username: 'casetester' }] }); }); + + it('StatusFilterWrapper should have a fixed width of 180px', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="status-filter-wrapper"]').first()).toHaveStyleRule( + 'flex-basis', + '180px', + { + modifier: '&&', + } + ); + }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx index f5ec0bf1441540..768ad300c02e61 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/table_filters.tsx @@ -6,6 +6,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { isEqual } from 'lodash/fp'; +import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup } from '@elastic/eui'; import { CaseStatuses } from '../../../../../case/common/api'; @@ -25,6 +26,13 @@ interface CasesTableFiltersProps { setFilterRefetch: (val: () => void) => void; } +// Fix the width of the status dropdown to prevent hiding long text items +const StatusFilterWrapper = styled(EuiFlexItem)` + && { + flex-basis: 180px; + } +`; + /** * Collection of filters for filtering data within the CasesTable. Contains search bar, * and tag selection @@ -131,23 +139,27 @@ const CasesTableFiltersComponent = ({ ); return ( - - - - - - + + + + + + + + + + From 4613da5c27dd6a2c4a5bb13d17a4ac50c827204e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Wed, 23 Dec 2020 09:02:15 +0100 Subject: [PATCH 071/100] [APM] Truncate long service names in Trace overview (#86759) * truncating service name * truncating service name --- .../apm/public/components/app/TraceOverview/TraceList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx index e68f8a9809bf5e..eebd03772f238f 100644 --- a/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx @@ -44,7 +44,7 @@ const traceListColumns: Array> = [ _: string, { serviceName, transactionName, transactionType }: TraceGroup ) => ( - + Date: Wed, 23 Dec 2020 09:05:29 +0100 Subject: [PATCH 072/100] [Discover] Integration of EuiDataGrid (#67259) Co-authored-by: Michail Yasonik Co-authored-by: Marta Bondyra Co-authored-by: Dave Snider Co-authored-by: Andrea Del Rio Co-authored-by: cchaos --- docs/management/advanced-options.asciidoc | 4 + src/core/public/chrome/ui/header/_index.scss | 14 + src/plugins/discover/common/index.ts | 1 + .../public/__mocks__/index_pattern.ts | 18 +- .../public/application/angular/discover.js | 28 +- .../angular/discover_datagrid.html | 31 ++ .../application/angular/discover_legacy.html | 3 +- .../application/angular/discover_state.ts | 5 + .../components/create_discover_directive.ts | 52 +++ .../create_discover_grid_directive.tsx | 56 +++ .../create_discover_legacy_directive.ts | 2 +- .../application/components/discover.scss | 12 + .../application/components/discover.tsx | 321 +++++++++++++++++ .../components/discover_grid/constants.ts | 38 ++ .../discover_grid/discover_grid.scss | 68 ++++ .../discover_grid/discover_grid.tsx | 336 ++++++++++++++++++ .../discover_grid_cell_actions.test.tsx | 80 +++++ .../discover_grid_cell_actions.tsx | 97 +++++ .../discover_grid_columns.test.tsx | 154 ++++++++ .../discover_grid/discover_grid_columns.tsx | 122 +++++++ .../discover_grid/discover_grid_context.tsx | 34 ++ .../discover_grid_expand_button.test.tsx | 106 ++++++ .../discover_grid_expand_button.tsx | 62 ++++ .../discover_grid/discover_grid_flyout.tsx | 143 ++++++++ .../discover_grid/discover_grid_schema.tsx | 103 ++++++ .../get_render_cell_value.test.tsx | 132 +++++++ .../discover_grid/get_render_cell_value.tsx | 116 ++++++ .../components/discover_grid/types.ts | 29 ++ .../components/discover_legacy.test.tsx | 2 +- .../components/discover_legacy.tsx | 127 ++++++- .../__snapshots__/doc_viewer.test.tsx.snap | 1 + .../components/doc_viewer/doc_viewer.scss | 1 - .../components/doc_viewer/doc_viewer.tsx | 2 +- .../__snapshots__/field_name.test.tsx.snap | 2 +- .../components/field_name/field_name.test.tsx | 2 +- .../sidebar/discover_field.test.tsx | 1 - .../sidebar/lib/group_fields.test.ts | 117 +++--- .../components/sidebar/lib/group_fields.tsx | 9 + .../embeddable/search_embeddable.ts | 35 +- .../embeddable/search_embeddable_factory.ts | 1 + .../embeddable/search_template.html | 16 +- .../embeddable/search_template_datagrid.html | 19 + .../helpers/get_sharing_data.test.ts | 6 +- .../helpers/persist_saved_search.ts | 3 + .../discover/public/get_inner_angular.ts | 7 +- .../public/saved_searches/_saved_search.ts | 2 + .../discover/public/saved_searches/types.ts | 2 + .../discover/server/saved_objects/search.ts | 1 + src/plugins/discover/server/ui_settings.ts | 18 + .../apps/dashboard/embeddable_data_grid.ts | 60 ++++ test/functional/apps/dashboard/index.ts | 1 + test/functional/apps/discover/_data_grid.ts | 67 ++++ .../apps/discover/_data_grid_context.ts | 91 +++++ .../discover/_data_grid_doc_navigation.ts | 91 +++++ .../apps/discover/_data_grid_doc_table.ts | 132 +++++++ .../apps/discover/_data_grid_field_data.ts | 99 ++++++ test/functional/apps/discover/_doc_table.ts | 15 +- test/functional/apps/discover/index.ts | 5 + test/functional/services/data_grid.ts | 138 ++++++- 59 files changed, 3156 insertions(+), 84 deletions(-) create mode 100644 src/plugins/discover/public/application/angular/discover_datagrid.html create mode 100644 src/plugins/discover/public/application/components/create_discover_directive.ts create mode 100644 src/plugins/discover/public/application/components/create_discover_grid_directive.tsx create mode 100644 src/plugins/discover/public/application/components/discover.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/constants.ts create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid.scss create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx create mode 100644 src/plugins/discover/public/application/components/discover_grid/types.ts create mode 100644 src/plugins/discover/public/application/embeddable/search_template_datagrid.html create mode 100644 test/functional/apps/dashboard/embeddable_data_grid.ts create mode 100644 test/functional/apps/discover/_data_grid.ts create mode 100644 test/functional/apps/discover/_data_grid_context.ts create mode 100644 test/functional/apps/discover/_data_grid_doc_navigation.ts create mode 100644 test/functional/apps/discover/_data_grid_doc_table.ts create mode 100644 test/functional/apps/discover/_data_grid_field_data.ts diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 99fadb240335a5..7e7c8953fd527d 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -262,6 +262,10 @@ Hides the "Time" column in *Discover* and in all saved searches on dashboards. Highlights results in *Discover* and saved searches on dashboards. Highlighting slows requests when working on big documents. +[[doctable-legacy]]`doc_table:legacy`:: +Control the way the Discover's table looks and works. Set this property to `true` to revert to the legacy implementation. + + [float] [[kibana-ml-settings]] ==== Machine learning diff --git a/src/core/public/chrome/ui/header/_index.scss b/src/core/public/chrome/ui/header/_index.scss index 44cd8642783252..b11e7e47f4ae78 100644 --- a/src/core/public/chrome/ui/header/_index.scss +++ b/src/core/public/chrome/ui/header/_index.scss @@ -1,5 +1,19 @@ @include euiHeaderAffordForFixed; +.euiDataGrid__restrictBody { + .headerGlobalNav, + .kbnQueryBar { + display: none; + } +} + +.euiDataGrid__restrictBody.euiBody--headerIsFixed { + .euiFlyout { + top: 0; + height: 100%; + } +} + .chrHeaderHelpMenu__version { text-transform: none; } diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts index 4334af63539e36..321a102e8d7827 100644 --- a/src/plugins/discover/common/index.ts +++ b/src/plugins/discover/common/index.ts @@ -27,4 +27,5 @@ export const FIELDS_LIMIT_SETTING = 'fields:popularLimit'; export const CONTEXT_DEFAULT_SIZE_SETTING = 'context:defaultSize'; export const CONTEXT_STEP_SETTING = 'context:step'; export const CONTEXT_TIE_BREAKER_FIELDS_SETTING = 'context:tieBreakerFields'; +export const DOC_TABLE_LEGACY = 'doc_table:legacy'; export const MODIFY_COLUMNS_ON_SWITCH = 'discover:modifyColumnsOnSwitch'; diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 706118cb713507..f2c12315d4b90d 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -22,29 +22,40 @@ import { IndexPattern } from '../../../data/common'; import { indexPatterns } from '../../../data/public'; const fields = [ + { + name: '_source', + type: '_source', + scripted: false, + filterable: false, + aggregatable: false, + }, { name: '_index', type: 'string', scripted: false, filterable: true, + aggregatable: false, }, { name: 'message', type: 'string', scripted: false, filterable: false, + aggregatable: false, }, { name: 'extension', type: 'string', scripted: false, filterable: true, + aggregatable: true, }, { name: 'bytes', type: 'number', scripted: false, filterable: true, + aggregatable: true, }, { name: 'scripted', @@ -62,16 +73,21 @@ const indexPattern = ({ id: 'the-index-pattern-id', title: 'the-index-pattern-title', metaFields: ['_index', '_score'], + formatField: jest.fn(), flattenHit: undefined, formatHit: jest.fn((hit) => hit._source), fields, - getComputedFields: () => ({}), + getComputedFields: () => ({ docvalueFields: [], scriptFields: {}, storedFields: ['*'] }), getSourceFiltering: () => ({}), getFieldByName: () => ({}), timeFieldName: '', + docvalueFields: [], } as unknown) as IndexPattern; indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; +indexPattern.formatField = (hit: Record, fieldName: string) => { + return fieldName === '_source' ? hit._source : indexPattern.flattenHit(hit)[fieldName]; +}; export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 99497d61c716e8..639e2212392cc3 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -24,7 +24,6 @@ import moment from 'moment'; import dateMath from '@elastic/datemath'; import { i18n } from '@kbn/i18n'; import { createSearchSessionRestorationDataProvider, getState, splitState } from './discover_state'; - import { RequestAdapter } from '../../../../inspector/public'; import { connectToQueryState, @@ -35,6 +34,7 @@ import { import { getSortArray } from './doc_table'; import * as columnActions from './doc_table/actions/columns'; import indexTemplateLegacy from './discover_legacy.html'; +import indexTemplateGrid from './discover_datagrid.html'; import { addHelpMenuToAppChrome } from '../components/help_menu/help_menu_util'; import { discoverResponseHandler } from './response_handler'; import { @@ -124,7 +124,9 @@ app.config(($routeProvider) => { }; const discoverRoute = { ...defaults, - template: indexTemplateLegacy, + template: getServices().uiSettings.get('doc_table:legacy', true) + ? indexTemplateLegacy + : indexTemplateGrid, reloadOnSearch: false, resolve: { savedObjects: function ($route, Promise) { @@ -340,6 +342,8 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab $scope.minimumVisibleRows = 50; $scope.fetchStatus = fetchStatuses.UNINITIALIZED; $scope.showSaveQuery = uiCapabilities.discover.saveQuery; + $scope.showTimeCol = + !config.get('doc_table:hideTimeColumn', false) && $scope.indexPattern.timeFieldName; let abortController; $scope.$on('$destroy', () => { @@ -414,7 +418,7 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab const query = $scope.searchSource.getField('query') || data.query.queryString.getDefaultQuery(); const sort = getSortArray(savedSearch.sort, $scope.indexPattern); - return { + const defaultState = { query, sort: !sort.length ? getDefaultSort($scope.indexPattern, config.get(SORT_DEFAULT_ORDER_SETTING, 'desc')) @@ -427,6 +431,11 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab interval: 'auto', filters: _.cloneDeep($scope.searchSource.getOwnField('filter')), }; + if (savedSearch.grid) { + defaultState.grid = savedSearch.grid; + } + + return defaultState; } $scope.state.index = $scope.indexPattern.id; @@ -440,6 +449,8 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab indexPatternList: $route.current.locals.savedObjects.ip.list, config: config, setHeaderActionMenu: getHeaderActionMenuMounter(), + filterManager, + setAppState, data, }; @@ -783,6 +794,17 @@ function discoverController($element, $route, $scope, $timeout, Promise, uiCapab const columns = columnActions.moveColumn($scope.state.columns, columnName, newIndex); setAppState({ columns }); }; + + $scope.setColumns = function setColumns(columns) { + // remove first element of columns if it's the configured timeFieldName, which is prepended automatically + const actualColumns = + $scope.indexPattern.timeFieldName && $scope.indexPattern.timeFieldName === columns[0] + ? columns.slice(1) + : columns; + $scope.state = { ...$scope.state, columns: actualColumns }; + setAppState({ columns: actualColumns }); + }; + async function setupVisualization() { // If no timefield has been specified we don't create a histogram of messages if (!getTimeField()) return; diff --git a/src/plugins/discover/public/application/angular/discover_datagrid.html b/src/plugins/discover/public/application/angular/discover_datagrid.html new file mode 100644 index 00000000000000..e59ebbb0fafd03 --- /dev/null +++ b/src/plugins/discover/public/application/angular/discover_datagrid.html @@ -0,0 +1,31 @@ + + + + diff --git a/src/plugins/discover/public/application/angular/discover_legacy.html b/src/plugins/discover/public/application/angular/discover_legacy.html index 7cdcd6cbbca3a8..3596c0a2519ed0 100644 --- a/src/plugins/discover/public/application/angular/discover_legacy.html +++ b/src/plugins/discover/public/application/angular/discover_legacy.html @@ -1,6 +1,5 @@ ( + +)); + +export function DiscoverGridEmbeddable(props: DiscoverGridProps) { + return ( + + + + ); +} + +/** + * this is just needed for the embeddable + */ +export function createDiscoverGridDirective(reactDirective: any) { + return reactDirective(DiscoverGridEmbeddable, [ + ['columns', { watchDepth: 'collection' }], + ['indexPattern', { watchDepth: 'reference' }], + ['onAddColumn', { watchDepth: 'reference', wrapApply: false }], + ['onFilter', { watchDepth: 'reference', wrapApply: false }], + ['onRemoveColumn', { watchDepth: 'reference', wrapApply: false }], + ['onSetColumns', { watchDepth: 'reference', wrapApply: false }], + ['onSort', { watchDepth: 'reference', wrapApply: false }], + ['rows', { watchDepth: 'collection' }], + ['sampleSize', { watchDepth: 'reference' }], + ['searchDescription', { watchDepth: 'reference' }], + ['searchTitle', { watchDepth: 'reference' }], + ['settings', { watchDepth: 'reference' }], + ['showTimeCol', { watchDepth: 'value' }], + ['sort', { watchDepth: 'value' }], + ]); +} diff --git a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts index cb3cb06aa90a39..6e5d47be987d80 100644 --- a/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts +++ b/src/plugins/discover/public/application/components/create_discover_legacy_directive.ts @@ -21,7 +21,6 @@ import { DiscoverLegacy } from './discover_legacy'; export function createDiscoverLegacyDirective(reactDirective: any) { return reactDirective(DiscoverLegacy, [ - ['addColumn', { watchDepth: 'reference' }], ['fetch', { watchDepth: 'reference' }], ['fetchCounter', { watchDepth: 'reference' }], ['fetchError', { watchDepth: 'reference' }], @@ -30,6 +29,7 @@ export function createDiscoverLegacyDirective(reactDirective: any) { ['hits', { watchDepth: 'reference' }], ['indexPattern', { watchDepth: 'reference' }], ['minimumVisibleRows', { watchDepth: 'reference' }], + ['onAddColumn', { watchDepth: 'reference' }], ['onAddFilter', { watchDepth: 'reference' }], ['onChangeInterval', { watchDepth: 'reference' }], ['onMoveColumn', { watchDepth: 'reference' }], diff --git a/src/plugins/discover/public/application/components/discover.scss b/src/plugins/discover/public/application/components/discover.scss index b17da97a459307..665bd98c232a5b 100644 --- a/src/plugins/discover/public/application/components/discover.scss +++ b/src/plugins/discover/public/application/components/discover.scss @@ -35,6 +35,10 @@ discover-app { } } +.dscPageContent { + border: $euiBorderThin; +} + .dscPageContent, .dscPageContent__inner { height: 100%; @@ -46,6 +50,7 @@ discover-app { .dscResultCount { padding: $euiSizeS; + min-height: $euiSize * 3; @include euiBreakpoint('xs', 's') { .dscResultCount__toggle { @@ -76,6 +81,13 @@ discover-app { padding: $euiSizeS; } +// new slimmer layout for data grid +.dscHistogramGrid { + display: flex; + height: $euiSize * 8; + padding: $euiSizeS $euiSizeS 0 $euiSizeS; +} + .dscTable { // SASSTODO: add a monospace modifier to the doc-table component .kbnDocTable__row { diff --git a/src/plugins/discover/public/application/components/discover.tsx b/src/plugins/discover/public/application/components/discover.tsx new file mode 100644 index 00000000000000..aa756d960e4353 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover.tsx @@ -0,0 +1,321 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import './discover.scss'; +import React, { useState, useRef } from 'react'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiHideFor, + EuiHorizontalRule, + EuiPage, + EuiPageBody, + EuiPageContent, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import classNames from 'classnames'; +import { HitsCounter } from './hits_counter'; +import { TimechartHeader } from './timechart_header'; +import { getServices } from '../../kibana_services'; +import { DiscoverUninitialized, DiscoverHistogram } from '../angular/directives'; +import { DiscoverNoResults } from './no_results'; +import { LoadingSpinner } from './loading_spinner/loading_spinner'; +import { search } from '../../../../data/public'; +import { + DiscoverSidebarResponsive, + DiscoverSidebarResponsiveProps, +} from './sidebar/discover_sidebar_responsive'; +import { DiscoverProps } from './discover_legacy'; +import { SortPairArr } from '../angular/doc_table/lib/get_sort'; +import { DiscoverGrid, DiscoverGridProps } from './discover_grid/discover_grid'; + +export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps) => ( + +)); + +export const DataGridMemoized = React.memo((props: DiscoverGridProps) => ( + +)); + +export function Discover({ + fetch, + fetchCounter, + fetchError, + fieldCounts, + histogramData, + hits, + indexPattern, + onAddColumn, + onAddFilter, + onChangeInterval, + onRemoveColumn, + onSetColumns, + onSort, + opts, + resetQuery, + resultState, + rows, + searchSource, + setIndexPattern, + showSaveQuery, + state, + timefilterUpdateHandler, + timeRange, + topNavMenu, + updateQuery, + updateSavedQueryId, +}: DiscoverProps) { + const scrollableDesktop = useRef(null); + const collapseIcon = useRef(null); + const [toggleOn, toggleChart] = useState(true); + const [isSidebarClosed, setIsSidebarClosed] = useState(false); + const services = getServices(); + const { TopNavMenu } = services.navigation.ui; + const { trackUiMetric } = services; + const { savedSearch, indexPatternList, config } = opts; + const bucketAggConfig = opts.chartAggConfigs?.aggs[1]; + const bucketInterval = + bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) + ? bucketAggConfig.buckets?.getInterval() + : undefined; + const contentCentered = resultState === 'uninitialized'; + const showTimeCol = !config.get('doc_table:hideTimeColumn', false) && indexPattern.timeFieldName; + const columns = + state.columns && + state.columns.length > 0 && + // check if all columns where removed except the configured timeField (this can't be removed) + !(state.columns.length === 1 && state.columns[0] === indexPattern.timeFieldName) + ? state.columns + : ['_source']; + // if columns include _source this is considered as default view, so you can't remove columns + // until you add a column using Discover's sidebar + const defaultColumns = columns.includes('_source'); + + return ( + + + + +

    + {savedSearch.title} +

    + + + + + + + setIsSidebarClosed(!isSidebarClosed)} + data-test-subj="collapseSideBarButton" + aria-controls="discover-sidebar" + aria-expanded={isSidebarClosed ? 'false' : 'true'} + aria-label={i18n.translate('discover.toggleSidebarAriaLabel', { + defaultMessage: 'Toggle sidebar', + })} + buttonRef={collapseIcon} + /> + + + + + {resultState === 'none' && ( + + )} + {resultState === 'uninitialized' && } + {resultState === 'loading' && } + {resultState === 'ready' && ( + + + + + 0 ? hits : 0} + showResetButton={!!(savedSearch && savedSearch.id)} + onResetQuery={resetQuery} + /> + + {toggleOn && ( + + + + )} + + { + toggleChart(!toggleOn); + }} + > + {toggleOn + ? i18n.translate('discover.hideChart', { + defaultMessage: 'Hide chart', + }) + : i18n.translate('discover.showChart', { + defaultMessage: 'Show chart', + })} + + + + + {toggleOn && opts.timefield && ( + +
    + {opts.chartAggConfigs && histogramData && rows.length !== 0 && ( +
    + +
    + )} +
    + +
    + )} + + + + +
    +

    + +

    + {rows && rows.length && ( +
    + { + const grid = { ...state.grid } || {}; + const newColumns = { ...grid.columns } || {}; + newColumns[colSettings.columnId] = { + width: colSettings.width, + }; + const newGrid = { ...grid, columns: newColumns }; + opts.setAppState({ grid: newGrid }); + }} + /> +
    + )} +
    +
    +
    + )} +
    +
    +
    +
    +
    +
    + ); +} diff --git a/src/plugins/discover/public/application/components/discover_grid/constants.ts b/src/plugins/discover/public/application/components/discover_grid/constants.ts new file mode 100644 index 00000000000000..dec483da8f8a11 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/constants.ts @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +// data types +export const kibanaJSON = 'kibana-json'; +export const geoPoint = 'geo-point'; +export const unknownType = 'unknown'; +export const gridStyle = { + border: 'all', + fontSize: 's', + cellPadding: 's', + rowHover: 'none', +}; + +export const pageSizeArr = [25, 50, 100]; +export const defaultPageSize = 25; +export const toolbarVisibility = { + showColumnSelector: { + allowHide: false, + allowReorder: true, + }, + showStyleSelector: false, +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss b/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss new file mode 100644 index 00000000000000..64a7eda963349b --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.scss @@ -0,0 +1,68 @@ +.dscDiscoverGrid { + width: 100%; + max-width: 100%; + height: 100%; + overflow: hidden; + + .euiDataGrid__controls { + border: none; + border-bottom: $euiBorderThin; + } + + .euiDataGridRowCell:first-of-type, + .euiDataGrid--headerShade.euiDataGrid--bordersAll .euiDataGridHeaderCell:first-of-type { + border-left: none; + border-right: none; + } + + .euiDataGridRowCell:last-of-type, + .euiDataGridHeaderCell:last-of-type { + border-right: none; + } +} + +.dscDiscoverGrid__footer { + background-color: $euiColorLightShade; + padding: $euiSize / 2 $euiSize; + margin-top: $euiSize / 4; + text-align: center; +} + +.dscTable__flyoutHeader { + white-space: nowrap; +} + +// We only truncate if the cell is not a control column. +.euiDataGridHeader { + .euiDataGridHeaderCell__content { + @include euiTextTruncate; + overflow: hidden; + white-space: nowrap; + flex-grow: 1; + } + + .euiDataGridHeaderCell__popover { + flex-grow: 0; + flex-basis: auto; + width: auto; + padding-left: $euiSizeXS; + } +} + +.euiDataGridRowCell--numeric { + text-align: right; +} + +.euiDataGrid__noResults { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1 0 100%; + text-align: center; + height: 100%; + width: 100%; +} + +.dscFormatSource { + @include euiTextTruncate; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx new file mode 100644 index 00000000000000..9588f74ed2bc2a --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx @@ -0,0 +1,336 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useCallback, useMemo, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import './discover_grid.scss'; +import { + EuiDataGridSorting, + EuiDataGridStyle, + EuiDataGridProps, + EuiDataGrid, + EuiIcon, + EuiScreenReaderOnly, + EuiSpacer, + EuiText, + htmlIdGenerator, +} from '@elastic/eui'; +import { IndexPattern } from '../../../kibana_services'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { getPopoverContents, getSchemaDetectors } from './discover_grid_schema'; +import { DiscoverGridFlyout } from './discover_grid_flyout'; +import { DiscoverGridContext } from './discover_grid_context'; +import { getRenderCellValueFn } from './get_render_cell_value'; +import { DiscoverGridSettings } from './types'; +import { SortPairArr } from '../../angular/doc_table/lib/get_sort'; +import { + getEuiGridColumns, + getLeadControlColumns, + getVisibleColumns, +} from './discover_grid_columns'; +import { defaultPageSize, gridStyle, pageSizeArr, toolbarVisibility } from './constants'; +import { DiscoverServices } from '../../../build_services'; + +interface SortObj { + id: string; + direction: string; +} + +export interface DiscoverGridProps { + /** + * Determines which element labels the grid for ARIA + */ + ariaLabelledBy: string; + /** + * Determines which columns are displayed + */ + columns: string[]; + /** + * Determines whether the given columns are the default ones, so parts of the document + * are displayed (_source) with limited actions (cannor move, remove columns) + * Implemented for matching with legacy behavior + */ + defaultColumns: boolean; + /** + * The used index pattern + */ + indexPattern: IndexPattern; + /** + * Function used to add a column in the document flyout + */ + onAddColumn: (column: string) => void; + /** + * Function to add a filter in the grid cell or document flyout + */ + onFilter: DocViewFilterFn; + /** + * Function used in the grid header and flyout to remove a column + * @param column + */ + onRemoveColumn: (column: string) => void; + /** + * Function triggered when a column is resized by the user + */ + onResize?: (colSettings: { columnId: string; width: number }) => void; + /** + * Function to set all columns + */ + onSetColumns: (columns: string[]) => void; + /** + * function to change sorting of the documents + */ + onSort: (sort: string[][]) => void; + /** + * Array of documents provided by Elasticsearch + */ + rows?: ElasticSearchHit[]; + /** + * The max size of the documents returned by Elasticsearch + */ + sampleSize: number; + /** + * Grid display settings persisted in Elasticsearch (e.g. column width) + */ + settings?: DiscoverGridSettings; + /** + * Saved search description + */ + searchDescription?: string; + /** + * Saved search title + */ + searchTitle?: string; + /** + * Discover plugin services + */ + services: DiscoverServices; + /** + * Determines whether the time columns should be displayed (legacy settings) + */ + showTimeCol: boolean; + /** + * Current sort setting + */ + sort: SortPairArr[]; +} + +export const EuiDataGridMemoized = React.memo((props: EuiDataGridProps) => { + return ; +}); + +export const DiscoverGrid = ({ + ariaLabelledBy, + columns, + defaultColumns, + indexPattern, + onAddColumn, + onFilter, + onRemoveColumn, + onResize, + onSetColumns, + onSort, + rows, + sampleSize, + searchDescription, + searchTitle, + services, + settings, + showTimeCol, + sort, +}: DiscoverGridProps) => { + const [expanded, setExpanded] = useState(undefined); + + /** + * Pagination + */ + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: defaultPageSize }); + const rowCount = useMemo(() => (rows ? rows.length : 0), [rows]); + const pageCount = useMemo(() => Math.ceil(rowCount / pagination.pageSize), [ + rowCount, + pagination, + ]); + const isOnLastPage = pagination.pageIndex === pageCount - 1; + + const paginationObj = useMemo(() => { + const onChangeItemsPerPage = (pageSize: number) => + setPagination((paginationData) => ({ ...paginationData, pageSize })); + + const onChangePage = (pageIndex: number) => + setPagination((paginationData) => ({ ...paginationData, pageIndex })); + + return { + onChangeItemsPerPage, + onChangePage, + pageIndex: pagination.pageIndex > pageCount - 1 ? 0 : pagination.pageIndex, + pageSize: pagination.pageSize, + pageSizeOptions: pageSizeArr, + }; + }, [pagination, pageCount]); + + /** + * Sorting + */ + const sortingColumns = useMemo(() => sort.map(([id, direction]) => ({ id, direction })), [sort]); + + const onTableSort = useCallback( + (sortingColumnsData) => { + onSort(sortingColumnsData.map(({ id, direction }: SortObj) => [id, direction])); + }, + [onSort] + ); + + /** + * Cell rendering + */ + const renderCellValue = useMemo( + () => + getRenderCellValueFn( + indexPattern, + rows, + rows ? rows.map((hit) => indexPattern.flattenHit(hit)) : [] + ), + [rows, indexPattern] + ); + + /** + * Render variables + */ + const showDisclaimer = rowCount === sampleSize && isOnLastPage; + const randomId = useMemo(() => htmlIdGenerator()(), []); + + const euiGridColumns = useMemo( + () => getEuiGridColumns(columns, settings, indexPattern, showTimeCol, defaultColumns), + [columns, indexPattern, showTimeCol, settings, defaultColumns] + ); + const schemaDetectors = useMemo(() => getSchemaDetectors(), []); + const popoverContents = useMemo(() => getPopoverContents(), []); + const columnsVisibility = useMemo( + () => ({ + visibleColumns: getVisibleColumns(columns, indexPattern, showTimeCol) as string[], + setVisibleColumns: (newColumns: string[]) => { + onSetColumns(newColumns); + }, + }), + [columns, indexPattern, showTimeCol, onSetColumns] + ); + const sorting = useMemo(() => ({ columns: sortingColumns, onSort: onTableSort }), [ + sortingColumns, + onTableSort, + ]); + const lead = useMemo(() => getLeadControlColumns(), []); + + if (!rowCount) { + return ( +
    + + + + + +
    + ); + } + + return ( + + <> + { + if (onResize) { + onResize(col); + } + }} + pagination={paginationObj} + popoverContents={popoverContents} + renderCellValue={renderCellValue} + rowCount={rowCount} + schemaDetectors={schemaDetectors} + sorting={sorting as EuiDataGridSorting} + toolbarVisibility={ + defaultColumns + ? { + ...toolbarVisibility, + showColumnSelector: false, + } + : toolbarVisibility + } + /> + + {showDisclaimer && ( +

    + + + + +

    + )} + {searchTitle && ( + +

    + {searchDescription ? ( + + ) : ( + + )} +

    +
    + )} + {expanded && ( + setExpanded(undefined)} + services={services} + /> + )} + +
    + ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx new file mode 100644 index 00000000000000..a85583f66c6fae --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.test.tsx @@ -0,0 +1,80 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { FilterInBtn, FilterOutBtn } from './discover_grid_cell_actions'; +import { DiscoverGridContext } from './discover_grid_context'; + +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { esHits } from '../../../__mocks__/es_hits'; +import { EuiButton } from '@elastic/eui'; + +describe('Discover cell actions ', function () { + it('triggers filter function when FilterInBtn is clicked', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + } + rowIndex={1} + columnId={'extension'} + isExpanded={false} + closePopover={jest.fn()} + /> + + ); + const button = findTestSubject(component, 'filterForButton'); + await button.simulate('click'); + expect(contextMock.onFilter).toHaveBeenCalledWith('extension', 'jpg', '+'); + }); + it('triggers filter function when FilterOutBtn is clicked', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + } + rowIndex={1} + columnId={'extension'} + isExpanded={false} + closePopover={jest.fn()} + /> + + ); + const button = findTestSubject(component, 'filterOutButton'); + await button.simulate('click'); + expect(contextMock.onFilter).toHaveBeenCalledWith('extension', 'jpg', '-'); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx new file mode 100644 index 00000000000000..ef56166258c9b5 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_cell_actions.tsx @@ -0,0 +1,97 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useContext } from 'react'; +import { EuiDataGridColumnCellActionProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IndexPatternField } from '../../../../../data/common/index_patterns/fields'; +import { DiscoverGridContext } from './discover_grid_context'; + +export const FilterInBtn = ({ + Component, + rowIndex, + columnId, +}: EuiDataGridColumnCellActionProps) => { + const context = useContext(DiscoverGridContext); + const buttonTitle = i18n.translate('discover.grid.filterForAria', { + defaultMessage: 'Filter for this {value}', + values: { value: columnId }, + }); + + return ( + { + const row = context.rows[rowIndex]; + const flattened = context.indexPattern.flattenHit(row); + + if (flattened) { + context.onFilter(columnId, flattened[columnId], '+'); + } + }} + iconType="plusInCircle" + aria-label={buttonTitle} + title={buttonTitle} + data-test-subj="filterForButton" + > + {i18n.translate('discover.grid.filterFor', { + defaultMessage: 'Filter for', + })} + + ); +}; + +export const FilterOutBtn = ({ + Component, + rowIndex, + columnId, +}: EuiDataGridColumnCellActionProps) => { + const context = useContext(DiscoverGridContext); + const buttonTitle = i18n.translate('discover.grid.filterOutAria', { + defaultMessage: 'Filter out this {value}', + values: { value: columnId }, + }); + + return ( + { + const row = context.rows[rowIndex]; + const flattened = context.indexPattern.flattenHit(row); + + if (flattened) { + context.onFilter(columnId, flattened[columnId], '-'); + } + }} + iconType="minusInCircle" + aria-label={buttonTitle} + title={buttonTitle} + data-test-subj="filterOutButton" + > + {i18n.translate('discover.grid.filterOut', { + defaultMessage: 'Filter out', + })} + + ); +}; + +export function buildCellActions(field: IndexPatternField) { + if (!field.aggregatable && !field.searchable) { + return undefined; + } + + return [FilterInBtn, FilterOutBtn]; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx new file mode 100644 index 00000000000000..dad7e1363fdd9d --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.test.tsx @@ -0,0 +1,154 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { getEuiGridColumns } from './discover_grid_columns'; +import { indexPatternWithTimefieldMock } from '../../../__mocks__/index_pattern_with_timefield'; + +describe('Discover grid columns ', function () { + it('returns eui grid columns without time column', async () => { + const actual = getEuiGridColumns(['extension', 'message'], {}, indexPatternMock, false, false); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); + it('returns eui grid columns without time column showing default columns', async () => { + const actual = getEuiGridColumns( + ['extension', 'message'], + {}, + indexPatternWithTimefieldMock, + false, + true + ); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": false, + "showMoveRight": false, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": false, + "showMoveRight": false, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); + it('returns eui grid columns with time column', async () => { + const actual = getEuiGridColumns( + ['extension', 'message'], + {}, + indexPatternWithTimefieldMock, + true, + false + ); + expect(actual).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Object { + "showHide": false, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": "Time (timestamp)", + "id": "timestamp", + "initialWidth": 180, + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "extension", + "isSortable": undefined, + "schema": "unknown", + }, + Object { + "actions": Object { + "showHide": Object { + "iconType": "cross", + "label": "Remove column", + }, + "showMoveLeft": true, + "showMoveRight": true, + }, + "cellActions": undefined, + "display": undefined, + "id": "message", + "isSortable": undefined, + "schema": "unknown", + }, + ] + `); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx new file mode 100644 index 00000000000000..1cf9c84405a61c --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_columns.tsx @@ -0,0 +1,122 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiDataGridColumn, EuiScreenReaderOnly } from '@elastic/eui'; +import { ExpandButton } from './discover_grid_expand_button'; +import { DiscoverGridSettings } from './types'; +import { IndexPattern } from '../../../../../data/common/index_patterns/index_patterns'; +import { buildCellActions } from './discover_grid_cell_actions'; +import { getSchemaByKbnType } from './discover_grid_schema'; + +export function getLeadControlColumns() { + return [ + { + id: 'openDetails', + width: 32, + headerCellRender: () => ( + + + {i18n.translate('discover.controlColumnHeader', { + defaultMessage: 'Control column', + })} + + + ), + rowCellRender: ExpandButton, + }, + ]; +} + +export function buildEuiGridColumn( + columnName: string, + columnWidth: number | undefined = 0, + indexPattern: IndexPattern, + defaultColumns: boolean +) { + const timeString = i18n.translate('discover.timeLabel', { + defaultMessage: 'Time', + }); + const indexPatternField = indexPattern.getFieldByName(columnName); + const column: EuiDataGridColumn = { + id: columnName, + schema: getSchemaByKbnType(indexPatternField?.type), + isSortable: indexPatternField?.sortable, + display: indexPatternField?.displayName, + actions: { + showHide: + defaultColumns || columnName === indexPattern.timeFieldName + ? false + : { + label: i18n.translate('discover.removeColumnLabel', { + defaultMessage: 'Remove column', + }), + iconType: 'cross', + }, + showMoveLeft: !defaultColumns, + showMoveRight: !defaultColumns, + }, + cellActions: indexPatternField ? buildCellActions(indexPatternField) : [], + }; + + if (column.id === indexPattern.timeFieldName) { + column.display = `${timeString} (${indexPattern.timeFieldName})`; + column.initialWidth = 180; + } + if (columnWidth > 0) { + column.initialWidth = Number(columnWidth); + } + return column; +} + +export function getEuiGridColumns( + columns: string[], + settings: DiscoverGridSettings | undefined, + indexPattern: IndexPattern, + showTimeCol: boolean, + defaultColumns: boolean +) { + const timeFieldName = indexPattern.timeFieldName; + const getColWidth = (column: string) => settings?.columns?.[column]?.width ?? 0; + + if (showTimeCol && indexPattern.timeFieldName && !columns.find((col) => col === timeFieldName)) { + const usedColumns = [indexPattern.timeFieldName, ...columns]; + return usedColumns.map((column) => + buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns) + ); + } + + return columns.map((column) => + buildEuiGridColumn(column, getColWidth(column), indexPattern, defaultColumns) + ); +} + +export function getVisibleColumns( + columns: string[], + indexPattern: IndexPattern, + showTimeCol: boolean +) { + const timeFieldName = indexPattern.timeFieldName; + + if (showTimeCol && !columns.find((col) => col === timeFieldName)) { + return [timeFieldName, ...columns]; + } + + return columns; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx new file mode 100644 index 00000000000000..dcc404a0e48df3 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_context.tsx @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { IndexPattern } from '../../../kibana_services'; + +export interface GridContext { + expanded: ElasticSearchHit | undefined; + setExpanded: (hit: ElasticSearchHit | undefined) => void; + rows: ElasticSearchHit[]; + onFilter: DocViewFilterFn; + indexPattern: IndexPattern; + isDarkMode: boolean; +} + +const defaultContext = ({} as unknown) as GridContext; + +export const DiscoverGridContext = React.createContext(defaultContext); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx new file mode 100644 index 00000000000000..82fcad8c2cd6f4 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.test.tsx @@ -0,0 +1,106 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { mountWithIntl } from '@kbn/test/jest'; +import { findTestSubject } from '@elastic/eui/lib/test'; +import { ExpandButton } from './discover_grid_expand_button'; +import { DiscoverGridContext } from './discover_grid_context'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +import { esHits } from '../../../__mocks__/es_hits'; + +describe('Discover grid view button ', function () { + it('when no document is expanded, setExpanded is called with current document', async () => { + const contextMock = { + expanded: undefined, + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(esHits[0]); + }); + it('when the current document is expanded, setExpanded is called with undefined', async () => { + const contextMock = { + expanded: esHits[0], + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(undefined); + }); + it('when another document is expanded, setExpanded is called with the current document', async () => { + const contextMock = { + expanded: esHits[0], + setExpanded: jest.fn(), + rows: esHits, + onFilter: jest.fn(), + indexPattern: indexPatternMock, + isDarkMode: false, + }; + + const component = mountWithIntl( + + + + ); + const button = findTestSubject(component, 'docTableExpandToggleColumn'); + await button.simulate('click'); + expect(contextMock.setExpanded).toHaveBeenCalledWith(esHits[1]); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx new file mode 100644 index 00000000000000..d4a3fe85e34ef2 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_expand_button.tsx @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useContext, useEffect } from 'react'; +import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui'; +import themeDark from '@elastic/eui/dist/eui_theme_dark.json'; +import themeLight from '@elastic/eui/dist/eui_theme_light.json'; +import { i18n } from '@kbn/i18n'; +import { DiscoverGridContext } from './discover_grid_context'; +/** + * Button to expand a given row + */ +export const ExpandButton = ({ rowIndex, setCellProps }: EuiDataGridCellValueElementProps) => { + const { expanded, setExpanded, rows, isDarkMode } = useContext(DiscoverGridContext); + const current = rows[rowIndex]; + useEffect(() => { + if (expanded && current && expanded._id === current._id) { + setCellProps({ + style: { + backgroundColor: isDarkMode ? themeDark.euiColorHighlight : themeLight.euiColorHighlight, + }, + }); + } else { + setCellProps({ style: undefined }); + } + }, [expanded, current, setCellProps, isDarkMode]); + + const isCurrentRowExpanded = current === expanded; + const buttonLabel = i18n.translate('discover.grid.viewDoc', { + defaultMessage: 'Toggle dialog with details', + }); + + return ( + + setExpanded(isCurrentRowExpanded ? undefined : current)} + color={isCurrentRowExpanded ? 'primary' : 'subdued'} + iconType={isCurrentRowExpanded ? 'minimize' : 'expand'} + isSelected={isCurrentRowExpanded} + /> + + ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx new file mode 100644 index 00000000000000..79ad98ae2babe1 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx @@ -0,0 +1,143 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiTitle, + EuiButtonEmpty, + EuiText, + EuiSpacer, + EuiPortal, +} from '@elastic/eui'; +import { DocViewer } from '../doc_viewer/doc_viewer'; +import { IndexPattern } from '../../../kibana_services'; +import { DocViewFilterFn, ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { DiscoverServices } from '../../../build_services'; +import { getContextUrl } from '../../helpers/get_context_url'; + +interface Props { + columns: string[]; + hit: ElasticSearchHit; + indexPattern: IndexPattern; + onAddColumn: (column: string) => void; + onClose: () => void; + onFilter: DocViewFilterFn; + onRemoveColumn: (column: string) => void; + services: DiscoverServices; +} + +/** + * Flyout displaying an expanded Elasticsearch document + */ +export function DiscoverGridFlyout({ + hit, + indexPattern, + columns, + onFilter, + onClose, + onRemoveColumn, + onAddColumn, + services, +}: Props) { + return ( + + + + +

    + {i18n.translate('discover.grid.tableRow.detailHeading', { + defaultMessage: 'Expanded document', + })} +

    +
    + + + + + + + {i18n.translate('discover.grid.tableRow.viewText', { + defaultMessage: 'View:', + })} + + + + + + {i18n.translate('discover.grid.tableRow.viewSingleDocumentLinkTextSimple', { + defaultMessage: 'Single document', + })} + + + {indexPattern.isTimeBased() && indexPattern.id && ( + + + {i18n.translate('discover.grid.tableRow.viewSurroundingDocumentsLinkTextSimple', { + defaultMessage: 'Surrounding documents', + })} + + + )} + +
    + + { + onFilter(mapping, value, mode); + onClose(); + }} + onRemoveColumn={(columnName: string) => { + onRemoveColumn(columnName); + onClose(); + }} + onAddColumn={(columnName: string) => { + onAddColumn(columnName); + onClose(); + }} + /> + +
    +
    + ); +} diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx new file mode 100644 index 00000000000000..aa87d3982fa062 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_schema.tsx @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { ReactNode } from 'react'; +import { EuiCodeBlock } from '@elastic/eui'; +import { geoPoint, kibanaJSON, unknownType } from './constants'; +import { KBN_FIELD_TYPES } from '../../../../../data/common'; + +export function getSchemaByKbnType(kbnType: string | undefined) { + // Default DataGrid schemas: boolean, numeric, datetime, json, currency, string + switch (kbnType) { + case KBN_FIELD_TYPES.IP: + case KBN_FIELD_TYPES.GEO_SHAPE: + case KBN_FIELD_TYPES.NUMBER: + return 'numeric'; + case KBN_FIELD_TYPES.BOOLEAN: + return 'boolean'; + case KBN_FIELD_TYPES.STRING: + return 'string'; + case KBN_FIELD_TYPES.DATE: + return 'datetime'; + case KBN_FIELD_TYPES._SOURCE: + return kibanaJSON; + case KBN_FIELD_TYPES.GEO_POINT: + return geoPoint; + default: + return unknownType; + } +} + +export function getSchemaDetectors() { + return [ + { + type: kibanaJSON, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: '', + color: '', + }, + { + type: unknownType, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: '', + color: '', + }, + { + type: geoPoint, + detector() { + return 0; // this schema is always explicitly defined + }, + sortTextAsc: '', + sortTextDesc: '', + icon: 'tokenGeo', + }, + ]; +} + +/** + * Returns custom popover content for certain schemas + */ +export function getPopoverContents() { + return { + [geoPoint]: ({ children }: { children: ReactNode }) => { + return {children}; + }, + [unknownType]: ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); + }, + [kibanaJSON]: ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); + }, + }; +} diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx new file mode 100644 index 00000000000000..d9896f4c539072 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx @@ -0,0 +1,132 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { shallow } from 'enzyme'; +import { getRenderCellValueFn } from './get_render_cell_value'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; +const rows = [ + { + _id: '1', + _index: 'test', + _type: 'test', + _score: 1, + _source: { bytes: 100 }, + }, +]; + +describe('Discover grid cell rendering', function () { + it('renders bytes column correctly', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"100"`); + }); + it('renders _source column correctly', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot( + `"
    bytes
    100
    "` + ); + }); + + it('renders _source column correctly when isDetails is set to true', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(` + "{ + "bytes": 100 + }" + `); + }); + + it('renders correctly when invalid row is given', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"-"`); + }); + it('renders correctly when invalid column is given', () => { + const DiscoverGridCellValue = getRenderCellValueFn( + indexPatternMock, + rows, + rows.map((row) => indexPatternMock.flattenHit(row)) + ); + const component = shallow( + + ); + expect(component.html()).toMatchInlineSnapshot(`"-"`); + }); +}); diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx new file mode 100644 index 00000000000000..2157e778f84db0 --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx @@ -0,0 +1,116 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { Fragment, useContext, useEffect } from 'react'; +import { i18n } from '@kbn/i18n'; +import themeLight from '@elastic/eui/dist/eui_theme_light.json'; +import themeDark from '@elastic/eui/dist/eui_theme_dark.json'; + +import { + EuiDataGridCellValueElementProps, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { IndexPattern } from '../../../kibana_services'; +import { ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { DiscoverGridContext } from './discover_grid_context'; + +export const getRenderCellValueFn = ( + indexPattern: IndexPattern, + rows: ElasticSearchHit[] | undefined, + rowsFlattened: Array> +) => ({ rowIndex, columnId, isDetails, setCellProps }: EuiDataGridCellValueElementProps) => { + const row = rows ? (rows[rowIndex] as Record) : undefined; + const rowFlattened = rowsFlattened + ? (rowsFlattened[rowIndex] as Record) + : undefined; + + const field = indexPattern.fields.getByName(columnId); + const ctx = useContext(DiscoverGridContext); + + useEffect(() => { + if (ctx.expanded && row && ctx.expanded._id === row._id) { + setCellProps({ + style: { + backgroundColor: ctx.isDarkMode + ? themeDark.euiColorHighlight + : themeLight.euiColorHighlight, + }, + }); + } else { + setCellProps({ style: undefined }); + } + }, [ctx, row, setCellProps]); + + if (typeof row === 'undefined' || typeof rowFlattened === 'undefined') { + return -; + } + + if (field && field.type === '_source') { + if (isDetails) { + // nicely formatted JSON for the expanded view + return {JSON.stringify(row[columnId], null, 2)}; + } + const formatted = indexPattern.formatHit(row); + + return ( + + {Object.keys(formatted).map((key) => ( + + {key} + + + ))} + + ); + } + + if (!field?.type && rowFlattened && typeof rowFlattened[columnId] === 'object') { + if (isDetails) { + // nicely formatted JSON for the expanded view + return {JSON.stringify(rowFlattened[columnId], null, 2)}; + } + + return {JSON.stringify(rowFlattened[columnId])}; + } + + if (field?.type === 'geo_point' && rowFlattened && rowFlattened[columnId]) { + const valueFormatted = rowFlattened[columnId] as { lat: number; lon: number }; + return ( +
    + {i18n.translate('discover.latitudeAndLongitude', { + defaultMessage: 'Lat: {lat} Lon: {lon}', + values: { + lat: valueFormatted?.lat, + lon: valueFormatted?.lon, + }, + })} +
    + ); + } + + const valueFormatted = indexPattern.formatField(row, columnId); + if (typeof valueFormatted === 'undefined') { + return -; + } + return ( + // eslint-disable-next-line react/no-danger + + ); +}; diff --git a/src/plugins/discover/public/application/components/discover_grid/types.ts b/src/plugins/discover/public/application/components/discover_grid/types.ts new file mode 100644 index 00000000000000..3d57dbffe924ef --- /dev/null +++ b/src/plugins/discover/public/application/components/discover_grid/types.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * User configurable state of data grid, persisted in saved search + */ +export interface DiscoverGridSettings { + columns?: Record; +} + +export interface DiscoverGridSettingsColumn { + width?: number; +} diff --git a/src/plugins/discover/public/application/components/discover_legacy.test.tsx b/src/plugins/discover/public/application/components/discover_legacy.test.tsx index e2f4ba7ab6e2e3..bad5c1d2e532d5 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.test.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.test.tsx @@ -67,7 +67,6 @@ function getProps(indexPattern: IndexPattern) { } as unknown) as DiscoverServices; return { - addColumn: jest.fn(), fetch: jest.fn(), fetchCounter: 0, fetchError: undefined, @@ -75,6 +74,7 @@ function getProps(indexPattern: IndexPattern) { hits: esHits.length, indexPattern, minimumVisibleRows: 10, + onAddColumn: jest.fn(), onAddFilter: jest.fn(), onChangeInterval: jest.fn(), onMoveColumn: jest.fn(), diff --git a/src/plugins/discover/public/application/components/discover_legacy.tsx b/src/plugins/discover/public/application/components/discover_legacy.tsx index d228be66990bde..436a145024437c 100644 --- a/src/plugins/discover/public/application/components/discover_legacy.tsx +++ b/src/plugins/discover/public/application/components/discover_legacy.tsx @@ -63,46 +63,161 @@ import { import { DocViewFilterFn, ElasticSearchHit } from '../doc_views/doc_views_types'; export interface DiscoverProps { - addColumn: (column: string) => void; + /** + * Function to fetch documents from Elasticsearch + */ fetch: () => void; + /** + * Counter how often data was fetched (used for testing) + */ fetchCounter: number; + /** + * Error in case of a failing document fetch + */ fetchError?: Error; + /** + * Statistics by fields calculated using the fetched documents + */ fieldCounts: Record; + /** + * Histogram aggregation data + */ histogramData?: Chart; + /** + * Number of documents found by recent fetch + */ hits: number; + /** + * Current IndexPattern + */ indexPattern: IndexPattern; + /** + * Value needed for legacy "infinite" loading functionality + * Determins how much records are rendered using the legacy table + * Increased when scrolling down + */ minimumVisibleRows: number; + /** + * Function to add a column to state + */ + onAddColumn: (column: string) => void; + /** + * Function to add a filter to state + */ onAddFilter: DocViewFilterFn; + /** + * Function to change the used time interval of the date histogram + */ onChangeInterval: (interval: string) => void; + /** + * Function to move a given column to a given index, used in legacy table + */ onMoveColumn: (columns: string, newIdx: number) => void; + /** + * Function to remove a given column from state + */ onRemoveColumn: (column: string) => void; + /** + * Function to replace columns in state + */ onSetColumns: (columns: string[]) => void; + /** + * Function to scroll down the legacy table to the bottom + */ onSkipBottomButtonClick: () => void; + /** + * Function to change sorting of the table, triggers a fetch + */ onSort: (sort: string[][]) => void; opts: { + /** + * Date histogram aggregation config + */ chartAggConfigs?: AggConfigs; + /** + * Client of uiSettings + */ config: IUiSettingsClient; + /** + * Data plugin + */ data: DataPublicPluginStart; - fixedScroll: (el: HTMLElement) => void; + /** + * Data plugin filter manager + */ filterManager: FilterManager; + /** + * List of available index patterns + */ indexPatternList: Array>; + /** + * The number of documents that can be displayed in the table/grid + */ sampleSize: number; + /** + * Current instance of SavedSearch + */ savedSearch: SavedSearch; + /** + * Function to set the header menu + */ setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; + /** + * Timefield of the currently used index pattern + */ timefield: string; + /** + * Function to set the current state + */ setAppState: (state: Partial) => void; }; + /** + * Function to reset the current query + */ resetQuery: () => void; + /** + * Current state of the actual query, one of 'uninitialized', 'loading' ,'ready', 'none' + */ resultState: string; + /** + * Array of document of the recent successful search request + */ rows: ElasticSearchHit[]; + /** + * Instance of SearchSource, the high level search API + */ searchSource: ISearchSource; + /** + * Function to change the current index pattern + */ setIndexPattern: (id: string) => void; + /** + * Determines whether the user should be able to use the save query feature + */ showSaveQuery: boolean; + /** + * Current app state of URL + */ state: AppState; + /** + * Function to update the time filter + */ timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; + /** + * Currently selected time range + */ timeRange?: { from: string; to: string }; + /** + * Menu data of top navigation (New, save ...) + */ topNavMenu: TopNavMenuData[]; + /** + * Function to update the actual query + */ updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; + /** + * Function to update the actual savedQuery id + */ updateSavedQueryId: (savedQueryId?: string) => void; } @@ -114,7 +229,6 @@ export const SidebarMemoized = React.memo((props: DiscoverSidebarResponsiveProps )); export function DiscoverLegacy({ - addColumn, fetch, fetchCounter, fieldCounts, @@ -123,6 +237,7 @@ export function DiscoverLegacy({ hits, indexPattern, minimumVisibleRows, + onAddColumn, onAddFilter, onChangeInterval, onMoveColumn, @@ -192,7 +307,7 @@ export function DiscoverLegacy({ fieldCounts={fieldCounts} hits={rows} indexPatternList={indexPatternList} - onAddField={addColumn} + onAddField={onAddColumn} onAddFilter={onAddFilter} onRemoveField={onRemoveColumn} selectedIndexPattern={searchSource && searchSource.getField('index')} @@ -206,6 +321,8 @@ export function DiscoverLegacy({ setIsSidebarClosed(!isSidebarClosed)} data-test-subj="collapseSideBarButton" aria-controls="discover-sidebar" @@ -335,7 +452,7 @@ export function DiscoverLegacy({ sort={state.sort || []} searchDescription={opts.savedSearch.description} searchTitle={opts.savedSearch.lastSavedTitle} - onAddColumn={addColumn} + onAddColumn={onAddColumn} onFilter={onAddFilter} onMoveColumn={onMoveColumn} onRemoveColumn={onRemoveColumn} diff --git a/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap index b5bd961037e21a..d02b484a06a49f 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap @@ -6,6 +6,7 @@ exports[`Render with 3 different tabs 1`] = ` > - +
    ); } diff --git a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap index 2fa96f9372380b..6b5e45f8a04488 100644 --- a/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap +++ b/src/plugins/discover/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap @@ -31,7 +31,7 @@ exports[`FieldName renders a geo field 1`] = `
    `; -exports[`FieldName renders a number field by providing a field record, useShortDots is set to false 1`] = ` +exports[`FieldName renders a number field by providing a field record 1`] = `
    diff --git a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx index 0deddce1c40a8d..248191acf9ab92 100644 --- a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx +++ b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx @@ -27,7 +27,7 @@ test('FieldName renders a string field by providing fieldType and fieldName', () expect(component).toMatchSnapshot(); }); -test('FieldName renders a number field by providing a field record, useShortDots is set to false', () => { +test('FieldName renders a number field by providing a field record', () => { const component = render(); expect(component).toMatchSnapshot(); }); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx index 391e15485f0744..0957ee101bd270 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_field.test.tsx @@ -56,7 +56,6 @@ function getComponent({ }: { selected?: boolean; showDetails?: boolean; - useShortDots?: boolean; field?: IndexPatternField; }) { const indexPattern = getStubIndexPattern( diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts index d4670a1e76011b..22cacae4c3b45b 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.test.ts @@ -19,51 +19,58 @@ import { groupFields } from './group_fields'; import { getDefaultFieldFilter } from './field_filter'; +import { IndexPatternField } from '../../../../../../data/common/index_patterns/fields'; -describe('group_fields', function () { - it('should group fields in selected, popular, unpopular group', function () { - const fields = [ - { - name: 'category', - type: 'string', - esTypes: ['text'], - count: 1, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'currency', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'customer_birth_date', - type: 'date', - esTypes: ['date'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - ]; +const fields = [ + { + name: 'category', + type: 'string', + esTypes: ['text'], + count: 1, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'currency', + type: 'string', + esTypes: ['keyword'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + { + name: 'customer_birth_date', + type: 'date', + esTypes: ['date'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, +]; - const fieldCounts = { - category: 1, - currency: 1, - customer_birth_date: 1, - }; +const fieldCounts = { + category: 1, + currency: 1, + customer_birth_date: 1, +}; +describe('group_fields', function () { + it('should group fields in selected, popular, unpopular group', function () { const fieldFilterState = getDefaultFieldFilter(); - const actual = groupFields(fields as any, ['currency'], 5, fieldCounts, fieldFilterState); + const actual = groupFields( + fields as IndexPatternField[], + ['currency'], + 5, + fieldCounts, + fieldFilterState + ); expect(actual).toMatchInlineSnapshot(` Object { "popular": Array [ @@ -111,4 +118,34 @@ describe('group_fields', function () { } `); }); + + it('should sort selected fields by columns order ', function () { + const fieldFilterState = getDefaultFieldFilter(); + + const actual1 = groupFields( + fields as IndexPatternField[], + ['customer_birth_date', 'currency', 'unknown'], + 5, + fieldCounts, + fieldFilterState + ); + expect(actual1.selected.map((field) => field.name)).toEqual([ + 'customer_birth_date', + 'currency', + 'unknown', + ]); + + const actual2 = groupFields( + fields as IndexPatternField[], + ['currency', 'customer_birth_date', 'unknown'], + 5, + fieldCounts, + fieldFilterState + ); + expect(actual2.selected.map((field) => field.name)).toEqual([ + 'currency', + 'customer_birth_date', + 'unknown', + ]); + }); }); diff --git a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx index c6a06618900fd5..c34becc97cb93f 100644 --- a/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx +++ b/src/plugins/discover/public/application/components/sidebar/lib/group_fields.tsx @@ -70,6 +70,15 @@ export function groupFields( result.unpopular.push(field); } } + // add columns, that are not part of the index pattern, to be removeable + for (const column of columns) { + if (!result.selected.find((field) => field.name === column)) { + result.selected.push({ name: column, displayName: column } as IndexPatternField); + } + } + result.selected.sort((a, b) => { + return columns.indexOf(a.name) - columns.indexOf(b.name); + }); return result; } diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts index d0c3907d312420..e4a8ab7bc67ffa 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts @@ -36,6 +36,7 @@ import { import { Container, Embeddable } from '../../../../embeddable/public'; import * as columnActions from '../angular/doc_table/actions/columns'; import searchTemplate from './search_template.html'; +import searchTemplateGrid from './search_template_datagrid.html'; import { ISearchEmbeddable, SearchInput, SearchOutput } from './types'; import { SortOrder } from '../angular/doc_table/components/table_header/helpers'; import { getSortForSearchSource } from '../angular/doc_table'; @@ -49,23 +50,29 @@ import { import { SEARCH_EMBEDDABLE_TYPE } from './constants'; import { SavedSearch } from '../..'; import { SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../common'; +import { DiscoverGridSettings } from '../components/discover_grid/types'; +import { DiscoverServices } from '../../build_services'; +import { ElasticSearchHit } from '../doc_views/doc_views_types'; import { getDefaultSort } from '../angular/doc_table/lib/get_default_sort'; interface SearchScope extends ng.IScope { columns?: string[]; + settings?: DiscoverGridSettings; description?: string; sort?: SortOrder[]; sharedItemTitle?: string; inspectorAdapters?: Adapters; setSortOrder?: (sortPair: SortOrder[]) => void; + setColumns?: (columns: string[]) => void; removeColumn?: (column: string) => void; addColumn?: (column: string) => void; moveColumn?: (column: string, index: number) => void; filter?: (field: IFieldType, value: string[], operator: string) => void; - hits?: any[]; + hits?: ElasticSearchHit[]; indexPattern?: IndexPattern; totalHitCount?: number; isLoading?: boolean; + showTimeCol?: boolean; } interface SearchEmbeddableConfig { @@ -77,6 +84,7 @@ interface SearchEmbeddableConfig { indexPatterns?: IndexPattern[]; editable: boolean; filterManager: FilterManager; + services: DiscoverServices; } export class SearchEmbeddable @@ -95,6 +103,7 @@ export class SearchEmbeddable public readonly type = SEARCH_EMBEDDABLE_TYPE; private filterManager: FilterManager; private abortController?: AbortController; + private services: DiscoverServices; private prevTimeRange?: TimeRange; private prevFilters?: Filter[]; @@ -111,6 +120,7 @@ export class SearchEmbeddable indexPatterns, editable, filterManager, + services, }: SearchEmbeddableConfig, initialInput: SearchInput, private readonly executeTriggerActions: UiActionsStart['executeTriggerActions'], @@ -128,7 +138,7 @@ export class SearchEmbeddable }, parent ); - + this.services = services; this.filterManager = filterManager; this.savedSearch = savedSearch; this.$rootScope = $rootScope; @@ -138,8 +148,8 @@ export class SearchEmbeddable }; this.initializeSearchScope(); - this.autoRefreshFetchSubscription = getServices() - .timefilter.getAutoRefreshFetch$() + this.autoRefreshFetchSubscription = this.services.timefilter + .getAutoRefreshFetch$() .subscribe(this.fetch); this.subscription = this.getUpdated$().subscribe(() => { @@ -167,7 +177,9 @@ export class SearchEmbeddable if (!this.searchScope) { throw new Error('Search scope not defined'); } - this.searchInstance = this.$compile(searchTemplate)(this.searchScope); + this.searchInstance = this.$compile( + this.services.uiSettings.get('doc_table:legacy', true) ? searchTemplate : searchTemplateGrid + )(this.searchScope); const rootNode = angular.element(domNode); rootNode.append(this.searchInstance); @@ -250,6 +262,15 @@ export class SearchEmbeddable this.updateInput({ columns }); }; + searchScope.setColumns = (columns: string[]) => { + this.updateInput({ columns }); + }; + + if (this.savedSearch.grid) { + searchScope.settings = this.savedSearch.grid; + } + searchScope.showTimeCol = !this.services.uiSettings.get('doc_table:hideTimeColumn', false); + searchScope.filter = async (field, value, operator) => { let filters = esFilters.generateFilters( this.filterManager, @@ -286,13 +307,13 @@ export class SearchEmbeddable if (this.abortController) this.abortController.abort(); this.abortController = new AbortController(); - searchSource.setField('size', getServices().uiSettings.get(SAMPLE_SIZE_SETTING)); + searchSource.setField('size', this.services.uiSettings.get(SAMPLE_SIZE_SETTING)); searchSource.setField( 'sort', getSortForSearchSource( this.searchScope.sort, this.searchScope.indexPattern, - getServices().uiSettings.get(SORT_DEFAULT_ORDER_SETTING) + this.services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING) ) ); diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts index f61fa361f0c0e6..d85476568201ff 100644 --- a/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts +++ b/src/plugins/discover/public/application/embeddable/search_embeddable_factory.ts @@ -103,6 +103,7 @@ export class SearchEmbeddableFactory filterManager, editable: getServices().capabilities.discover.save as boolean, indexPatterns: indexPattern ? [indexPattern] : [], + services: getServices(), }, input, executeTriggerActions, diff --git a/src/plugins/discover/public/application/embeddable/search_template.html b/src/plugins/discover/public/application/embeddable/search_template.html index e188d230ea307f..be2f5cceac0809 100644 --- a/src/plugins/discover/public/application/embeddable/search_template.html +++ b/src/plugins/discover/public/application/embeddable/search_template.html @@ -1,20 +1,20 @@ diff --git a/src/plugins/discover/public/application/embeddable/search_template_datagrid.html b/src/plugins/discover/public/application/embeddable/search_template_datagrid.html new file mode 100644 index 00000000000000..6524783897f8f3 --- /dev/null +++ b/src/plugins/discover/public/application/embeddable/search_template_datagrid.html @@ -0,0 +1,19 @@ + diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts index 4dec1f75ba322b..2ab1b93d6c37ed 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -51,7 +51,7 @@ describe('getSharingData', () => { "searchRequest": Object { "body": Object { "_source": Object {}, - "fields": undefined, + "fields": Array [], "query": Object { "bool": Object { "filter": Array [], @@ -68,7 +68,9 @@ describe('getSharingData', () => { }, }, ], - "stored_fields": undefined, + "stored_fields": Array [ + "*", + ], }, "index": "the-index-pattern-title", }, diff --git a/src/plugins/discover/public/application/helpers/persist_saved_search.ts b/src/plugins/discover/public/application/helpers/persist_saved_search.ts index 8e956eff598f3c..8ec2012b5843e7 100644 --- a/src/plugins/discover/public/application/helpers/persist_saved_search.ts +++ b/src/plugins/discover/public/application/helpers/persist_saved_search.ts @@ -53,6 +53,9 @@ export async function persistSavedSearch( savedSearch.columns = state.columns || []; savedSearch.sort = (state.sort as SortOrder[]) || []; + if (state.grid) { + savedSearch.grid = state.grid; + } try { const id = await savedSearch.save(saveOptions); diff --git a/src/plugins/discover/public/get_inner_angular.ts b/src/plugins/discover/public/get_inner_angular.ts index 651a26cad755db..2ace65c31cc036 100644 --- a/src/plugins/discover/public/get_inner_angular.ts +++ b/src/plugins/discover/public/get_inner_angular.ts @@ -41,6 +41,7 @@ import { createTableRowDirective } from './application/angular/doc_table/compone import { createPagerFactory } from './application/angular/doc_table/lib/pager/pager_factory'; import { createInfiniteScrollDirective } from './application/angular/doc_table/infinite_scroll'; import { createDocViewerDirective } from './application/angular/doc_viewer'; +import { createDiscoverGridDirective } from './application/components/create_discover_grid_directive'; import { createRenderCompleteDirective } from './application/angular/directives/render_complete'; import { initAngularBootstrap, @@ -55,6 +56,8 @@ import { import { DiscoverStartPlugins } from './plugin'; import { getScopedHistory } from './kibana_services'; import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive'; +import { createDiscoverDirective } from './application/components/create_discover_directive'; + /** * returns the main inner angular module, it contains all the parts of Angular Discover * needs to render, so in the end the current 'kibana' angular module is no longer necessary @@ -136,7 +139,8 @@ export function initializeInnerAngularModule( .config(watchMultiDecorator) .run(registerListenEventListener) .directive('renderComplete', createRenderCompleteDirective) - .directive('discoverLegacy', createDiscoverLegacyDirective); + .directive('discoverLegacy', createDiscoverLegacyDirective) + .directive('discover', createDiscoverDirective); } function createLocalPromiseModule() { @@ -188,6 +192,7 @@ function createDocTableModule() { .directive('kbnTableRow', createTableRowDirective) .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) .directive('kbnInfiniteScroll', createInfiniteScrollDirective) + .directive('discoverGrid', createDiscoverGridDirective) .directive('docViewer', createDocViewerDirective) .directive('contextAppLegacy', createContextAppLegacy); } diff --git a/src/plugins/discover/public/saved_searches/_saved_search.ts b/src/plugins/discover/public/saved_searches/_saved_search.ts index 1ec4549f05d491..8a0ec128b4eb2b 100644 --- a/src/plugins/discover/public/saved_searches/_saved_search.ts +++ b/src/plugins/discover/public/saved_searches/_saved_search.ts @@ -26,6 +26,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { description: 'text', hits: 'integer', columns: 'keyword', + grid: 'object', sort: 'keyword', version: 'integer', }; @@ -45,6 +46,7 @@ export function createSavedSearchClass(savedObjects: SavedObjectsStart) { description: 'text', hits: 'integer', columns: 'keyword', + grid: 'object', sort: 'keyword', version: 'integer', }, diff --git a/src/plugins/discover/public/saved_searches/types.ts b/src/plugins/discover/public/saved_searches/types.ts index d5e5dd765a364d..7f6f1a2553d5ec 100644 --- a/src/plugins/discover/public/saved_searches/types.ts +++ b/src/plugins/discover/public/saved_searches/types.ts @@ -19,6 +19,7 @@ import { SearchSource } from '../../../data/public'; import { SavedObjectSaveOpts } from '../../../saved_objects/public'; +import { DiscoverGridSettings } from '../application/components/discover_grid/types'; export type SortOrder = [string, string]; export interface SavedSearch { @@ -28,6 +29,7 @@ export interface SavedSearch { description?: string; columns: string[]; sort: SortOrder[]; + grid: DiscoverGridSettings; destroy: () => void; save: (saveOptions: SavedObjectSaveOpts) => Promise; lastSavedTitle?: string; diff --git a/src/plugins/discover/server/saved_objects/search.ts b/src/plugins/discover/server/saved_objects/search.ts index a6e42f956a025f..d124a24b120fdc 100644 --- a/src/plugins/discover/server/saved_objects/search.ts +++ b/src/plugins/discover/server/saved_objects/search.ts @@ -53,6 +53,7 @@ export const searchSavedObjectType: SavedObjectsType = { }, sort: { type: 'keyword', index: false, doc_values: false }, title: { type: 'text' }, + grid: { type: 'object', enabled: false }, version: { type: 'integer' }, }, }, diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index f45281ee62202c..425928385e64a6 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -33,6 +33,7 @@ import { CONTEXT_DEFAULT_SIZE_SETTING, CONTEXT_STEP_SETTING, CONTEXT_TIE_BREAKER_FIELDS_SETTING, + DOC_TABLE_LEGACY, MODIFY_COLUMNS_ON_SWITCH, } from '../common'; @@ -165,6 +166,23 @@ export const uiSettings: Record = { category: ['discover'], schema: schema.arrayOf(schema.string()), }, + [DOC_TABLE_LEGACY]: { + name: i18n.translate('discover.advancedSettings.docTableVersionName', { + defaultMessage: 'Use legacy table', + }), + value: true, + description: i18n.translate('discover.advancedSettings.docTableVersionDescription', { + defaultMessage: + 'Discover uses a new table layout that includes better data sorting, drag-and-drop columns, and a full screen ' + + 'view. Enable this option if you prefer to fall back to the legacy table.', + }), + category: ['discover'], + schema: schema.boolean(), + metric: { + type: METRIC_TYPE.CLICK, + name: 'discover:useLegacyDataGrid', + }, + }, [MODIFY_COLUMNS_ON_SWITCH]: { name: i18n.translate('discover.advancedSettings.discover.modifyColumnsOnSwitchTitle', { defaultMessage: 'Modify columns when changing index patterns', diff --git a/test/functional/apps/dashboard/embeddable_data_grid.ts b/test/functional/apps/dashboard/embeddable_data_grid.ts new file mode 100644 index 00000000000000..067536ab7aa932 --- /dev/null +++ b/test/functional/apps/dashboard/embeddable_data_grid.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dashboardAddPanel = getService('dashboardAddPanel'); + const filterBar = getService('filterBar'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const find = getService('find'); + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'timePicker', 'discover']); + + describe('dashboard embeddable data grid', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('dashboard/current/data'); + await esArchiver.loadIfNeeded('dashboard/current/kibana'); + await kibanaServer.uiSettings.replace({ + defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c', + 'doc_table:legacy': false, + }); + await PageObjects.common.navigateToApp('dashboard'); + await filterBar.ensureFieldEditorModalIsClosed(); + await PageObjects.dashboard.gotoDashboardLandingPage(); + await PageObjects.dashboard.clickNewDashboard(); + await PageObjects.timePicker.setDefaultDataRange(); + }); + + describe('saved search filters', function () { + it('are added when a cell filter is clicked', async function () { + await dashboardAddPanel.addSavedSearch('Rendering-Test:-saved-search'); + await find.clickByCssSelector(`[role="gridcell"]:nth-child(2)`); + await find.clickByCssSelector(`[data-test-subj="filterOutButton"]`); + await PageObjects.header.waitUntilLoadingHasFinished(); + await find.clickByCssSelector(`[role="gridcell"]:nth-child(2)`); + await find.clickByCssSelector(`[data-test-subj="filterForButton"]`); + const filterCount = await filterBar.getFilterCount(); + expect(filterCount).to.equal(2); + }); + }); + }); +} diff --git a/test/functional/apps/dashboard/index.ts b/test/functional/apps/dashboard/index.ts index 6fb5f874022a03..43ad1aad5de00f 100644 --- a/test/functional/apps/dashboard/index.ts +++ b/test/functional/apps/dashboard/index.ts @@ -54,6 +54,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./empty_dashboard')); loadTestFile(require.resolve('./url_field_formatter')); loadTestFile(require.resolve('./embeddable_rendering')); + loadTestFile(require.resolve('./embeddable_data_grid')); loadTestFile(require.resolve('./create_and_add_embeddables')); loadTestFile(require.resolve('./edit_embeddable_redirects')); loadTestFile(require.resolve('./edit_visualizations')); diff --git a/test/functional/apps/discover/_data_grid.ts b/test/functional/apps/discover/_data_grid.ts new file mode 100644 index 00000000000000..8f62e03518253b --- /dev/null +++ b/test/functional/apps/discover/_data_grid.ts @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +export default function ({ + getService, + getPageObjects, +}: { + getService: (service: string) => any; + getPageObjects: (pageObjects: string[]) => any; +}) { + describe('discover data grid tests', function describeDiscoverDataGrid() { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + const kibanaServer = getService('kibanaServer'); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const testSubjects = getService('testSubjects'); + + before(async function () { + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + after(async function () { + await kibanaServer.uiSettings.replace({ 'doc_table:legacy': true }); + }); + + it('can add fields to the table', async function () { + const getTitles = async () => + (await testSubjects.getVisibleText('dataGridHeader')).replace(/\s|\r?\n|\r/g, ' '); + + expect(await getTitles()).to.be('Time (@timestamp) _source'); + + await PageObjects.discover.clickFieldListItemAdd('bytes'); + expect(await getTitles()).to.be('Time (@timestamp) bytes'); + + await PageObjects.discover.clickFieldListItemAdd('agent'); + expect(await getTitles()).to.be('Time (@timestamp) bytes agent'); + + await PageObjects.discover.clickFieldListItemAdd('bytes'); + expect(await getTitles()).to.be('Time (@timestamp) agent'); + + await PageObjects.discover.clickFieldListItemAdd('agent'); + expect(await getTitles()).to.be('Time (@timestamp) _source'); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_context.ts b/test/functional/apps/discover/_data_grid_context.ts new file mode 100644 index 00000000000000..6821b9c69cf7ec --- /dev/null +++ b/test/functional/apps/discover/_data_grid_context.ts @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +const TEST_COLUMN_NAMES = ['@message']; +const TEST_FILTER_COLUMN_NAMES = [ + ['extension', 'jpg'], + ['geo.src', 'IN'], +]; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const filterBar = getService('filterBar'); + const dataGrid = getService('dataGrid'); + const docTable = getService('docTable'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'settings']); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('discover data grid context tests', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + + for (const columnName of TEST_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItemAdd(columnName); + } + + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + await PageObjects.discover.clickFieldListItem(columnName); + await PageObjects.discover.clickFieldListPlusFilter(columnName, value); + } + }); + after(async () => { + await PageObjects.timePicker.resetDefaultAbsoluteRangeViaUiSettings(); + }); + + it('should open the context view with the selected document as anchor', async () => { + // check the anchor timestamp in the context view + await retry.waitFor('selected document timestamp matches anchor timestamp ', async () => { + // get the timestamp of the first row + const discoverFields = await dataGrid.getFields(); + const firstTimestamp = discoverFields[0][0]; + + // navigate to the context view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); + await rowActions[1].click(); + // entering the context view (contains the legacy type) + const contextFields = await docTable.getFields(); + const anchorTimestamp = contextFields[0][0]; + return anchorTimestamp === firstTimestamp; + }); + }); + + it('should open the context view with the same columns', async () => { + const columnNames = await docTable.getHeaderFields(); + expect(columnNames).to.eql(['Time', ...TEST_COLUMN_NAMES]); + }); + + it('should open the context view with the filters disabled', async () => { + let disabledFilterCounter = 0; + for (const [columnName, value] of TEST_FILTER_COLUMN_NAMES) { + if (await filterBar.hasFilter(columnName, value, false)) { + disabledFilterCounter++; + } + } + expect(disabledFilterCounter).to.be(TEST_FILTER_COLUMN_NAMES.length); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_doc_navigation.ts b/test/functional/apps/discover/_data_grid_doc_navigation.ts new file mode 100644 index 00000000000000..92d9893cab0b60 --- /dev/null +++ b/test/functional/apps/discover/_data_grid_doc_navigation.ts @@ -0,0 +1,91 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const filterBar = getService('filterBar'); + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker', 'context']); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + const kibanaServer = getService('kibanaServer'); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + + describe('discover data grid doc link', function () { + beforeEach(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + }); + + it('should open the doc view of the selected document', async function () { + // navigate to the doc view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + + // click the open action + await retry.try(async () => { + const rowActions = await dataGrid.getRowActions({ rowIndex: 0 }); + if (!rowActions.length) { + throw new Error('row actions empty, trying again'); + } + await rowActions[0].click(); + }); + + const hasDocHit = await testSubjects.exists('doc-hit'); + expect(hasDocHit).to.be(true); + }); + + it('add filter should create an exists filter if value is null (#7189)', async function () { + await PageObjects.discover.waitUntilSearchingHasFinished(); + // Filter special document + await filterBar.addFilter('agent', 'is', 'Missing/Fields'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await retry.try(async () => { + // navigate to the doc view + await dataGrid.clickRowToggle({ rowIndex: 0 }); + + const details = await dataGrid.getDetailsRow(); + await dataGrid.addInclusiveFilter(details, 'referer'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + const hasInclusiveFilter = await filterBar.hasFilter( + 'referer', + 'exists', + true, + false, + true + ); + expect(hasInclusiveFilter).to.be(true); + + await dataGrid.clickRowToggle({ rowIndex: 0 }); + const detailsExcluding = await dataGrid.getDetailsRow(); + await dataGrid.removeInclusiveFilter(detailsExcluding, 'referer'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + const hasExcludeFilter = await filterBar.hasFilter('referer', 'exists', true, false, false); + expect(hasExcludeFilter).to.be(true); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_doc_table.ts b/test/functional/apps/discover/_data_grid_doc_table.ts new file mode 100644 index 00000000000000..1224823abf048d --- /dev/null +++ b/test/functional/apps/discover/_data_grid_doc_table.ts @@ -0,0 +1,132 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const log = getService('log'); + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + const defaultSettings = { + defaultIndex: 'logstash-*', + 'doc_table:legacy': false, + }; + + describe('discover data grid doc table', function describeIndexTests() { + const defaultRowsLimit = 25; + + before(async function () { + log.debug('load kibana index with default index pattern'); + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace(defaultSettings); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + }); + + it('should show the first 50 rows by default', async function () { + // with the default range the number of hits is ~14000 + const rows = await dataGrid.getDocTableRows(); + expect(rows.length).to.be(defaultRowsLimit); + }); + + it('should refresh the table content when changing time window', async function () { + const initialRows = await dataGrid.getDocTableRows(); + + const fromTime = 'Sep 20, 2015 @ 23:00:00.000'; + const toTime = 'Sep 20, 2015 @ 23:14:00.000'; + + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + const finalRows = await PageObjects.discover.getDocTableRows(); + expect(finalRows.length).to.be.below(initialRows.length); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + describe('expand a document row', function () { + const rowToInspect = 1; + + it('should expand the detail row when the toggle arrow is clicked', async function () { + await retry.try(async function () { + await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); + const detailsEl = await dataGrid.getDetailsRows(); + const defaultMessageEl = await detailsEl[0].findByTestSubject('docTableRowDetailsTitle'); + expect(defaultMessageEl).to.be.ok(); + await dataGrid.closeFlyout(); + }); + }); + + it('should show the detail panel actions', async function () { + await retry.try(async function () { + await dataGrid.clickRowToggle({ isAnchorRow: false, rowIndex: rowToInspect - 1 }); + const [surroundingActionEl, singleActionEl] = await dataGrid.getRowActions({ + isAnchorRow: false, + rowIndex: rowToInspect - 1, + }); + expect(surroundingActionEl).to.be.ok(); + expect(singleActionEl).to.be.ok(); + await dataGrid.closeFlyout(); + }); + }); + }); + + describe('add and remove columns', function () { + const extraColumns = ['phpmemory', 'ip']; + + afterEach(async function () { + for (const column of extraColumns) { + await PageObjects.discover.clickFieldListItemRemove(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + }); + + it('should add more columns to the table', async function () { + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test the header now + const header = await dataGrid.getHeaderFields(); + expect(header.join(' ')).to.have.string(column); + } + }); + + it('should remove columns from the table', async function () { + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + } + // remove the second column + await PageObjects.discover.clickFieldListItemAdd(extraColumns[1]); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test that the second column is no longer there + const header = await dataGrid.getHeaderFields(); + expect(header.join(' ')).to.not.have.string(extraColumns[1]); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_data_grid_field_data.ts b/test/functional/apps/discover/_data_grid_field_data.ts new file mode 100644 index 00000000000000..8224f59f7fabf8 --- /dev/null +++ b/test/functional/apps/discover/_data_grid_field_data.ts @@ -0,0 +1,99 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const toasts = getService('toasts'); + const queryBar = getService('queryBar'); + const PageObjects = getPageObjects(['common', 'header', 'discover', 'visualize', 'timePicker']); + const defaultSettings = { defaultIndex: 'logstash-*', 'doc_table:legacy': false }; + const dataGrid = getService('dataGrid'); + + describe('discover data grid field data tests', function describeIndexTests() { + this.tags('includeFirefox'); + before(async function () { + await esArchiver.load('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.navigateToApp('discover'); + }); + describe('field data', function () { + it('search php should show the correct hit count', async function () { + const expectedHitCount = '445'; + await retry.try(async function () { + await queryBar.setQuery('php'); + await queryBar.submitQuery(); + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('the search term should be highlighted in the field data', async function () { + // marks is the style that highlights the text in yellow + const marks = await PageObjects.discover.getMarks(); + expect(marks.length).to.be(25); + expect(marks.indexOf('php')).to.be(0); + }); + + it('search type:apache should show the correct hit count', async function () { + const expectedHitCount = '11,156'; + await queryBar.setQuery('type:apache'); + await queryBar.submitQuery(); + await retry.try(async function tryingForTime() { + const hitCount = await PageObjects.discover.getHitCount(); + expect(hitCount).to.be(expectedHitCount); + }); + }); + + it('doc view should show Time and _source columns', async function () { + const expectedHeader = 'Time (@timestamp) _source'; + const DocHeader = await dataGrid.getHeaderFields(); + expect(DocHeader.join(' ')).to.be(expectedHeader); + }); + + it('doc view should sort ascending', async function () { + const expectedTimeStamp = 'Sep 20, 2015 @ 00:00:00.000'; + await dataGrid.clickDocSortAsc(); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await retry.try(async function tryingForTime() { + const rowData = await dataGrid.getFields(); + expect(rowData[0][0].startsWith(expectedTimeStamp)).to.be.ok(); + }); + }); + + it('a bad syntax query should show an error message', async function () { + const expectedError = + 'Expected ":", "<", "<=", ">", ">=", AND, OR, end of input, ' + + 'whitespace but "(" found.'; + await queryBar.setQuery('xxx(yyy))'); + await queryBar.submitQuery(); + const { message } = await toasts.getErrorToast(); + expect(message).to.contain(expectedError); + await toasts.dismissToast(); + }); + }); + }); +} diff --git a/test/functional/apps/discover/_doc_table.ts b/test/functional/apps/discover/_doc_table.ts index 20fda144b338e9..40a6ab31f7d4c6 100644 --- a/test/functional/apps/discover/_doc_table.ts +++ b/test/functional/apps/discover/_doc_table.ts @@ -131,13 +131,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('should add more columns to the table', async function () { - const [column] = extraColumns; - await PageObjects.discover.findFieldByName(column); - log.debug(`add a ${column} column`); - await PageObjects.discover.clickFieldListItemAdd(column); - await PageObjects.header.waitUntilLoadingHasFinished(); - // test the header now - expect(await PageObjects.discover.getDocHeader()).to.have.string(column); + for (const column of extraColumns) { + await PageObjects.discover.clearFieldSearchInput(); + await PageObjects.discover.findFieldByName(column); + await PageObjects.discover.clickFieldListItemAdd(column); + await PageObjects.header.waitUntilLoadingHasFinished(); + // test the header now + expect(await PageObjects.discover.getDocHeader()).to.have.string(column); + } }); it('should remove columns from the table', async function () { diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index c13529b7d1b43c..450049af66abfa 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -51,5 +51,10 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_mixed')); loadTestFile(require.resolve('./_indexpattern_without_timefield')); + loadTestFile(require.resolve('./_data_grid')); + loadTestFile(require.resolve('./_data_grid_context')); + loadTestFile(require.resolve('./_data_grid_field_data')); + loadTestFile(require.resolve('./_data_grid_doc_navigation')); + loadTestFile(require.resolve('./_data_grid_doc_table')); }); } diff --git a/test/functional/services/data_grid.ts b/test/functional/services/data_grid.ts index 209e30d23ca3cd..c538d8156103cc 100644 --- a/test/functional/services/data_grid.ts +++ b/test/functional/services/data_grid.ts @@ -24,10 +24,15 @@ interface TabbedGridData { columns: string[]; rows: string[][]; } +interface SelectOptions { + isAnchorRow?: boolean; + rowIndex: number; +} -export function DataGridProvider({ getService }: FtrProviderContext) { +export function DataGridProvider({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'header']); class DataGrid { async getDataGridTableData(): Promise { @@ -103,6 +108,137 @@ export function DataGridProvider({ getService }: FtrProviderContext) { [data-test-subj="dataGridRowCell"]:nth-of-type(${columnIndex})` ); } + public async getFields() { + const rows = await find.allByCssSelector('.euiDataGridRow'); + + const result = []; + for (const row of rows) { + const cells = await row.findAllByClassName('euiDataGridRowCell__truncate'); + const cellsText = []; + let cellIdx = 0; + for (const cell of cells) { + if (cellIdx > 0) { + cellsText.push(await cell.getVisibleText()); + } + cellIdx++; + } + result.push(cellsText); + } + return result; + } + + public async getTable(selector: string = 'docTable') { + return await testSubjects.find(selector); + } + + public async getBodyRows(): Promise { + const table = await this.getTable(); + return await table.findAllByTestSubject('dataGridRow'); + } + + public async getDocTableRows() { + const table = await this.getTable(); + return await table.findAllByTestSubject('dataGridRow'); + } + + public async getAnchorRow(): Promise { + const table = await this.getTable(); + return await table.findByTestSubject('~docTableAnchorRow'); + } + + public async getRow(options: SelectOptions): Promise { + return options.isAnchorRow + ? await this.getAnchorRow() + : (await this.getBodyRows())[options.rowIndex]; + } + + public async clickRowToggle( + options: SelectOptions = { isAnchorRow: false, rowIndex: 0 } + ): Promise { + const row = await this.getRow(options); + const toggle = await row.findByTestSubject('~docTableExpandToggleColumn'); + await toggle.click(); + } + + public async getDetailsRows(): Promise { + return await testSubjects.findAll('docTableDetailsFlyout'); + } + + public async closeFlyout() { + await testSubjects.click('euiFlyoutCloseButton'); + } + + public async getHeaderFields(): Promise { + const result = await find.allByCssSelector('.euiDataGridHeaderCell__content'); + const textArr = []; + let idx = 0; + for (const cell of result) { + if (idx > 0) { + textArr.push(await cell.getVisibleText()); + } + idx++; + } + return Promise.resolve(textArr); + } + + public async getRowActions( + options: SelectOptions = { isAnchorRow: false, rowIndex: 0 } + ): Promise { + const detailsRow = (await this.getDetailsRows())[options.rowIndex]; + return await detailsRow.findAllByTestSubject('~docTableRowAction'); + } + + public async clickDocSortAsc() { + await find.clickByCssSelector('.euiDataGridHeaderCell__button'); + await find.clickByButtonText('Sort New-Old'); + } + + public async clickDocSortDesc() { + await find.clickByCssSelector('.euiDataGridHeaderCell__button'); + await find.clickByButtonText('Sort Old-New'); + } + public async getDetailsRow(): Promise { + const detailRows = await this.getDetailsRows(); + return detailRows[0]; + } + public async addInclusiveFilter( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); + const addInclusiveFilterButton = await this.getAddInclusiveFilterButton(tableDocViewRow); + await addInclusiveFilterButton.click(); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + } + + public async getAddInclusiveFilterButton( + tableDocViewRow: WebElementWrapper + ): Promise { + return await tableDocViewRow.findByTestSubject(`~addInclusiveFilterButton`); + } + + public async getTableDocViewRow( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + return await detailsRow.findByTestSubject(`~tableDocViewRow-${fieldName}`); + } + + public async getRemoveInclusiveFilterButton( + tableDocViewRow: WebElementWrapper + ): Promise { + return await tableDocViewRow.findByTestSubject(`~removeInclusiveFilterButton`); + } + + public async removeInclusiveFilter( + detailsRow: WebElementWrapper, + fieldName: string + ): Promise { + const tableDocViewRow = await this.getTableDocViewRow(detailsRow, fieldName); + const addInclusiveFilterButton = await this.getRemoveInclusiveFilterButton(tableDocViewRow); + await addInclusiveFilterButton.click(); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + } } return new DataGrid(); From 15b80dd771511c8a953740b7a32d83946a4f07e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Wed, 23 Dec 2020 09:31:01 +0100 Subject: [PATCH 073/100] [APM] Filter out service nodes if there are no metrics (#86639) * filtering out metrics without service.node.name * filtering out metrics without service.node.name * addressing pr comments * fix TS issue --- .../apm/server/lib/service_nodes/index.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index d5e29532e3d7b9..ca58a1b0e71263 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Setup, SetupTimeRange } from '../helpers/setup_request'; -import { getServiceNodesProjection } from '../../projections/service_nodes'; -import { mergeProjection } from '../../projections/util/merge_projection'; -import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; import { - METRIC_PROCESS_CPU_PERCENT, - METRIC_JAVA_THREAD_COUNT, METRIC_JAVA_HEAP_MEMORY_USED, METRIC_JAVA_NON_HEAP_MEMORY_USED, + METRIC_JAVA_THREAD_COUNT, + METRIC_PROCESS_CPU_PERCENT, } from '../../../common/elasticsearch_fieldnames'; +import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; +import { getServiceNodesProjection } from '../../projections/service_nodes'; +import { mergeProjection } from '../../projections/util/merge_projection'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; const getServiceNodes = async ({ setup, @@ -68,15 +68,21 @@ const getServiceNodes = async ({ return []; } - return response.aggregations.nodes.buckets.map((bucket) => { - return { + return response.aggregations.nodes.buckets + .map((bucket) => ({ name: bucket.key as string, cpu: bucket.cpu.value, heapMemory: bucket.heapMemory.value, nonHeapMemory: bucket.nonHeapMemory.value, threadCount: bucket.threadCount.value, - }; - }); + })) + .filter( + (item) => + item.cpu !== null || + item.heapMemory !== null || + item.nonHeapMemory !== null || + item.threadCount != null + ); }; export { getServiceNodes }; From 0d3daa564f0ced3629fe0d3bbd202f7bc67f633f Mon Sep 17 00:00:00 2001 From: Katrin Freihofner Date: Wed, 23 Dec 2020 09:38:35 +0100 Subject: [PATCH 074/100] [Logs UI] removes unnecessary panel in categories tabL (#86769) --- .../page_results_content.tsx | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx index 98367335d9c2d2..6fc9ce3d8983eb 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/page_results_content.tsx @@ -178,21 +178,19 @@ export const LogEntryCategoriesResultsContent: React.FunctionComponent - - - - - - - - + + + + + + Date: Wed, 23 Dec 2020 13:00:52 +0100 Subject: [PATCH 075/100] [APM] "View job" link from latency charts leads to a malfunctioning page (#86788) * fixing ML links * fixing ML links --- .../Settings/anomaly_detection/jobs_list.tsx | 24 +++---- .../MLExplorerLink.test.tsx | 44 +++++++++++++ .../MachineLearningLinks/MLExplorerLink.tsx | 63 +++++++++++++++++++ .../charts/transaction_charts/ml_header.tsx | 4 +- 4 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx create mode 100644 x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx index acc2550930b8e6..b185685f0720a2 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/jobs_list.tsx @@ -4,26 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { - EuiPanel, - EuiTitle, - EuiText, - EuiSpacer, EuiButton, EuiFlexGroup, EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; -import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; -import { MLSingleMetricLink } from '../../../shared/Links/MachineLearningLinks/MLSingleMetricLink'; +import { MLExplorerLink } from '../../../shared/Links/MachineLearningLinks/MLExplorerLink'; import { MLManageJobsLink } from '../../../shared/Links/MachineLearningLinks/MLManageJobsLink'; -import { getEnvironmentLabel } from '../../../../../common/environment_filter_values'; -import { LegacyJobsCallout } from './legacy_jobs_callout'; +import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; +import { ITableColumn, ManagedTable } from '../../../shared/ManagedTable'; import { AnomalyDetectionApiResponse } from './index'; +import { LegacyJobsCallout } from './legacy_jobs_callout'; type Jobs = AnomalyDetectionApiResponse['jobs']; @@ -44,14 +44,14 @@ const columns: Array> = [ { defaultMessage: 'Action' } ), render: (jobId: string) => ( - + {i18n.translate( 'xpack.apm.settings.anomalyDetection.jobList.mlJobLinkText', { defaultMessage: 'View job in ML', } )} - + ), }, ]; diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx new file mode 100644 index 00000000000000..3f02ed082f564d --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Location } from 'history'; +import React from 'react'; +import { getRenderedHref } from '../../../../utils/testHelpers'; +import { MLExplorerLink } from './MLExplorerLink'; + +describe('MLExplorerLink', () => { + it('should produce the correct URL with jobId', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=now/w&rangeTo=now-4h&refreshPaused=true&refreshInterval=0', + } as Location + ); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/explorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now%2Fw,to:now-4h))&_a=(explorer:(mlExplorerFilter:(),mlExplorerSwimlane:()))"` + ); + }); + + it('correctly encodes time range values', async () => { + const href = await getRenderedHref( + () => ( + + ), + { + search: + '?rangeFrom=2020-07-29T17:27:29.000Z&rangeTo=2020-07-29T18:45:00.000Z&refreshInterval=10000&refreshPaused=true', + } as Location + ); + + expect(href).toMatchInlineSnapshot( + `"/app/ml/explorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(explorer:(mlExplorerFilter:(),mlExplorerSwimlane:()))"` + ); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx new file mode 100644 index 00000000000000..ca9eb063bd0900 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLExplorerLink.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ReactNode } from 'react'; +import { EuiLink } from '@elastic/eui'; +import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { useMlHref, ML_PAGES } from '../../../../../../ml/public'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { TimePickerRefreshInterval } from '../../DatePicker/typings'; + +interface Props { + children?: ReactNode; + jobId: string; + external?: boolean; +} + +export function MLExplorerLink({ jobId, external, children }: Props) { + const href = useExplorerHref({ jobId }); + + return ( + + ); +} + +export function useExplorerHref({ jobId }: { jobId: string }) { + const { + core, + plugins: { ml }, + } = useApmPluginContext(); + const { urlParams } = useUrlParams(); + + const timePickerRefreshIntervalDefaults = core.uiSettings.get( + UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS + ); + + const { + // hardcoding a custom default of 1 hour since the default kibana timerange of 15 minutes is shorter than the ML interval + rangeFrom = 'now-1h', + rangeTo = 'now', + refreshInterval = timePickerRefreshIntervalDefaults.value, + refreshPaused = timePickerRefreshIntervalDefaults.pause, + } = urlParams; + + const href = useMlHref(ml, core.http.basePath.get(), { + page: ML_PAGES.ANOMALY_EXPLORER, + pageState: { + jobIds: [jobId], + timeRange: { from: rangeFrom, to: rangeTo }, + refreshInterval: { pause: refreshPaused, value: refreshInterval }, + }, + }); + + return href; +} diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx index d125af70268cb7..33dcbf02ccda7c 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/ml_header.tsx @@ -10,6 +10,7 @@ import { isEmpty } from 'lodash'; import React from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { MLSingleMetricLink } from '../../Links/MachineLearningLinks/MLSingleMetricLink'; @@ -33,12 +34,13 @@ const ShiftedEuiText = styled(EuiText)` export function MLHeader({ hasValidMlLicense, mlJobId }: Props) { const { serviceName } = useParams<{ serviceName?: string }>(); const { urlParams } = useUrlParams(); + const { transactionType } = useApmServiceContext(); if (!hasValidMlLicense || !mlJobId) { return null; } - const { kuery, transactionType } = urlParams; + const { kuery } = urlParams; const hasKuery = !isEmpty(kuery); const icon = hasKuery ? ( From d4d70f22cf76bf7414c357d4bd145ebf917fabf3 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Wed, 23 Dec 2020 14:00:59 +0100 Subject: [PATCH 076/100] [ML] Enforce pause when it's set to false with 0 refresh interval (#86805) * [ML] Enforce pause when it's set to false with 0 refresh interval * [ML] add mocks, fix unit tests --- .../select_interval/select_interval.tsx | 2 +- .../date_picker_wrapper.test.tsx | 93 +++++++------------ .../date_picker_wrapper.tsx | 43 ++++----- .../routes/timeseriesexplorer.test.tsx | 57 +++++++++--- .../routing/routes/timeseriesexplorer.tsx | 10 +- .../__mocks__/index.ts | 7 ++ .../use_timeseriesexplorer_url_state.ts | 9 ++ .../timeseriesexplorer.d.ts | 9 +- .../validate_job_selection.ts | 7 +- .../application/util/__mocks__/url_state.tsx | 27 ++++++ .../ml/public/application/util/url_state.tsx | 4 +- 11 files changed, 162 insertions(+), 106 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/services/toast_notification_service/__mocks__/index.ts create mode 100644 x-pack/plugins/ml/public/application/timeseriesexplorer/hooks/__mocks__/use_timeseriesexplorer_url_state.ts create mode 100644 x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx diff --git a/x-pack/plugins/ml/public/application/components/controls/select_interval/select_interval.tsx b/x-pack/plugins/ml/public/application/components/controls/select_interval/select_interval.tsx index 059ab48daa27ef..0fc1d458399dd2 100644 --- a/x-pack/plugins/ml/public/application/components/controls/select_interval/select_interval.tsx +++ b/x-pack/plugins/ml/public/application/components/controls/select_interval/select_interval.tsx @@ -51,7 +51,7 @@ function optionValueToInterval(value: string) { return interval; } -const TABLE_INTERVAL_DEFAULT = optionValueToInterval('auto'); +export const TABLE_INTERVAL_DEFAULT = optionValueToInterval('auto'); export const useTableInterval = (): [TableInterval, (v: TableInterval) => void] => { return usePageUrlState('mlSelectInterval', TABLE_INTERVAL_DEFAULT); diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx index e2aa13f6019ed2..b882531eb75b0a 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.test.tsx @@ -5,15 +5,31 @@ */ import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; import { EuiSuperDatePicker } from '@elastic/eui'; +import { useUrlState } from '../../../util/url_state'; import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service'; import { DatePickerWrapper } from './date_picker_wrapper'; +jest.mock('@elastic/eui', () => { + const EuiSuperDatePickerMock = jest.fn(() => { + return null; + }); + return { EuiSuperDatePicker: EuiSuperDatePickerMock }; +}); + +jest.mock('../../../util/url_state', () => { + return { + useUrlState: jest.fn(() => { + return [{ refreshInterval: { value: 0, pause: true } }, jest.fn()]; + }), + }; +}); + jest.mock('../../../contexts/kibana', () => ({ useMlKibana: () => { return { @@ -25,9 +41,11 @@ jest.mock('../../../contexts/kibana', () => ({ timefilter: { getRefreshInterval: jest.fn(), setRefreshInterval: jest.fn(), - getTime: jest.fn(), - isAutoRefreshSelectorEnabled: jest.fn(), - isTimeRangeSelectorEnabled: jest.fn(), + getTime: jest.fn(() => { + return { from: '', to: '' }; + }), + isAutoRefreshSelectorEnabled: jest.fn(() => true), + isTimeRangeSelectorEnabled: jest.fn(() => true), getRefreshIntervalUpdate$: jest.fn(), getTimeUpdate$: jest.fn(), getEnabledUpdated$: jest.fn(), @@ -41,11 +59,12 @@ jest.mock('../../../contexts/kibana', () => ({ }, })); -const noop = () => {}; +const MockedEuiSuperDatePicker = EuiSuperDatePicker as jest.MockedClass; describe('Navigation Menu: ', () => { beforeEach(() => { jest.useFakeTimers(); + MockedEuiSuperDatePicker.mockClear(); }); afterEach(() => { @@ -56,66 +75,22 @@ describe('Navigation Menu: ', () => { const refreshListener = jest.fn(); const refreshSubscription = mlTimefilterRefresh$.subscribe(refreshListener); - const wrapper = mount( - - - - ); + const wrapper = mount(); expect(wrapper.find(DatePickerWrapper)).toHaveLength(1); expect(refreshListener).toBeCalledTimes(0); refreshSubscription.unsubscribe(); }); - // The following tests are written against EuiSuperDatePicker - // instead of DatePickerWrapper. DatePickerWrapper uses hooks and we cannot write tests - // with async hook updates yet until React 16.9 is available. - test('Listen for consecutive super date picker refreshs.', async () => { - const onRefresh = jest.fn(); - - const componentRefresh = mount( - - ); - - const instanceRefresh = componentRefresh.instance(); - - jest.advanceTimersByTime(10); - // @ts-ignore - await instanceRefresh.asyncInterval.__pendingFn; - jest.advanceTimersByTime(10); - // @ts-ignore - await instanceRefresh.asyncInterval.__pendingFn; - - expect(onRefresh).toBeCalledTimes(2); - }); + test('should not allow disabled pause with 0 refresh interval', () => { + // arrange + (useUrlState as jest.Mock).mockReturnValue([{ refreshInterval: { pause: false, value: 0 } }]); + + // act + render(); - test('Switching refresh interval to pause should stop onRefresh being called.', async () => { - const onRefresh = jest.fn(); - - const componentRefresh = mount( - - ); - - const instanceRefresh = componentRefresh.instance(); - - jest.advanceTimersByTime(10); - // @ts-ignore - await instanceRefresh.asyncInterval.__pendingFn; - componentRefresh.setProps({ isPaused: true, refreshInterval: 0 }); - jest.advanceTimersByTime(10); - // @ts-ignore - await instanceRefresh.asyncInterval.__pendingFn; - - expect(onRefresh).toBeCalledTimes(1); + // assert + const calledWith = MockedEuiSuperDatePicker.mock.calls[0][0]; + expect(calledWith.isPaused).toBe(true); }); }); diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx index a4dc78ea53a77e..dc046241f82b9e 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, Fragment, useCallback, useEffect, useState } from 'react'; +import React, { FC, useCallback, useEffect, useState } from 'react'; import { Subscription } from 'rxjs'; import { debounce } from 'lodash'; @@ -122,24 +122,25 @@ export const DatePickerWrapper: FC = () => { setRefreshInterval({ pause, value }); } - return ( - - {(isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled) && ( -
    - -
    - )} -
    - ); + /** + * Enforce pause when it's set to false with 0 refresh interval. + */ + const isPaused = refreshInterval.pause || (!refreshInterval.pause && !refreshInterval.value); + + return isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled ? ( +
    + +
    + ) : null; }; diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx index b60a2655604551..97ea27c5fe40aa 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx @@ -5,12 +5,44 @@ */ import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; import { render } from '@testing-library/react'; - import { I18nProvider } from '@kbn/i18n/react'; - import { TimeSeriesExplorerUrlStateManager } from './timeseriesexplorer'; +import { TimeSeriesExplorer } from '../../timeseriesexplorer'; +import { TimeSeriesExplorerPage } from '../../timeseriesexplorer/timeseriesexplorer_page'; +import { TimeseriesexplorerNoJobsFound } from '../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found'; + +jest.mock('../../services/toast_notification_service'); + +jest.mock('../../timeseriesexplorer', () => ({ + TimeSeriesExplorer: jest.fn(() => { + return null; + }), +})); + +jest.mock('../../timeseriesexplorer/timeseriesexplorer_page', () => ({ + TimeSeriesExplorerPage: jest.fn(({ children }) => { + return <>{children}; + }), +})); + +jest.mock('../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found', () => ({ + TimeseriesexplorerNoJobsFound: jest.fn(() => { + return null; + }), +})); + +const MockedTimeSeriesExplorer = TimeSeriesExplorer as jest.MockedClass; +const MockedTimeSeriesExplorerPage = TimeSeriesExplorerPage as jest.MockedFunction< + typeof TimeSeriesExplorerPage +>; +const MockedTimeseriesexplorerNoJobsFound = TimeseriesexplorerNoJobsFound as jest.MockedFunction< + typeof TimeseriesexplorerNoJobsFound +>; + +jest.mock('../../util/url_state'); + +jest.mock('../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state'); jest.mock('../../contexts/kibana/kibana_context', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -59,27 +91,22 @@ jest.mock('../../contexts/kibana/kibana_context', () => { }; }); -jest.mock('../../util/dependency_cache', () => ({ - getToastNotifications: () => ({ addSuccess: jest.fn(), addDanger: jest.fn() }), -})); - -jest.mock('../../../../shared_imports'); - describe('TimeSeriesExplorerUrlStateManager', () => { - test('Initial render shows "No single metric jobs found"', () => { + test('should render TimeseriesexplorerNoJobsFound when no jobs provided', () => { const props = { config: { get: () => 'Browser' }, jobsWithTimeRange: [], }; - const { container } = render( + render( - - - + ); - expect(container.textContent).toContain('No single metric jobs found'); + // assert + expect(MockedTimeSeriesExplorer).not.toHaveBeenCalled(); + expect(MockedTimeSeriesExplorerPage).toHaveBeenCalled(); + expect(MockedTimeseriesexplorerNoJobsFound).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx index 7de59cba495af8..857e894d404ae8 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx @@ -11,7 +11,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { NavigateToPath } from '../../contexts/kibana'; +import { NavigateToPath, useNotifications } from '../../contexts/kibana'; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; @@ -93,6 +93,7 @@ export const TimeSeriesExplorerUrlStateManager: FC { + const { toasts } = useNotifications(); const toastNotificationService = useToastNotificationService(); const [ timeSeriesExplorerUrlState, @@ -249,7 +250,12 @@ export const TimeSeriesExplorerUrlStateManager: FC { + return [{}, jest.fn()]; +}); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts index 8159dbb8ade064..26525505420dee 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.d.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FC } from 'react'; +import React from 'react'; import { TimeRangeBounds } from '../explorer/explorer_utils'; -declare const TimeSeriesExplorer: FC<{ +interface Props { appStateHandler: (action: string, payload: any) => void; autoZoomDuration: number; bounds: TimeRangeBounds; @@ -21,4 +21,7 @@ declare const TimeSeriesExplorer: FC<{ tableInterval: string; tableSeverity: number; zoom?: { from?: string; to?: string }; -}>; +} + +// eslint-disable-next-line react/prefer-stateless-function +declare class TimeSeriesExplorer extends React.Component {} diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts index cd8a10a9e1f99f..1781d0ee6369bd 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer_utils/validate_job_selection.ts @@ -8,8 +8,7 @@ import { difference, without } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getToastNotifications } from '../../util/dependency_cache'; - +import { ToastsStart } from 'kibana/public'; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; import { getTimeRangeFromSelection } from '../../components/job_selector/job_select_service_utils'; @@ -24,9 +23,9 @@ import { createTimeSeriesJobData } from './timeseriesexplorer_utils'; export function validateJobSelection( jobsWithTimeRange: MlJobWithTimeRange[], selectedJobIds: string[], - setGlobalState: (...args: any) => void + setGlobalState: (...args: any) => void, + toastNotifications: ToastsStart ) { - const toastNotifications = getToastNotifications(); const jobs = createTimeSeriesJobData(mlJobService.jobs); const timeSeriesJobIds: string[] = jobs.map((j: any) => j.id); diff --git a/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx b/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx new file mode 100644 index 00000000000000..cb237b951d8ddc --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/__mocks__/url_state.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AppStateKey } from '../url_state'; +import { TABLE_INTERVAL_DEFAULT } from '../../components/controls/select_interval/select_interval'; + +export const useUrlState = jest.fn((accessor: '_a' | '_g') => { + if (accessor === '_g') { + return [{ refreshInterval: { value: 0, pause: true } }, jest.fn()]; + } +}); + +export const usePageUrlState = jest.fn((pageKey: AppStateKey) => { + let state: unknown; + switch (pageKey) { + case 'timeseriesexplorer': + state = {}; + break; + case 'mlSelectInterval': + state = TABLE_INTERVAL_DEFAULT; + break; + } + return [state, jest.fn()]; +}); diff --git a/x-pack/plugins/ml/public/application/util/url_state.tsx b/x-pack/plugins/ml/public/application/util/url_state.tsx index 569e7bcc7b7e1a..b565a0f7b7a73e 100644 --- a/x-pack/plugins/ml/public/application/util/url_state.tsx +++ b/x-pack/plugins/ml/public/application/util/url_state.tsx @@ -73,7 +73,9 @@ export const urlStateStore = createContext({ searchString: '', setUrlState: () => {}, }); + const { Provider } = urlStateStore; + export const UrlStateProvider: FC = ({ children }) => { const history = useHistory(); const { search: searchString } = useLocation(); @@ -164,7 +166,7 @@ export const useUrlState = (accessor: Accessor) => { type LegacyUrlKeys = 'mlExplorerSwimlane'; -type AppStateKey = +export type AppStateKey = | 'mlSelectSeverity' | 'mlSelectInterval' | 'mlAnomaliesTable' From 0f838bfea125b92901110b2597a73c92df05c3ee Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Wed, 23 Dec 2020 09:41:49 -0500 Subject: [PATCH 077/100] [Fleet] Consistent display of agent counts on lists (#86827) * move LinkedAgentCount component to top-level components and adjust output * refactor integration details Policies list to use LinkedAgentCount component * test cases for agent counts on integrations --- .../fleet/components/linked_agent_count.tsx | 36 +++++ .../sections/agent_policy/components/index.ts | 2 +- .../components/linked_agent_count.tsx | 35 ----- .../epm/screens/detail/index.test.tsx | 115 ++++++++++++++- .../screens/detail/package_policies_panel.tsx | 31 +--- .../translations/translations/ja-JP.json | 139 +++++++++--------- .../translations/translations/zh-CN.json | 139 +++++++++--------- 7 files changed, 295 insertions(+), 202 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx new file mode 100644 index 00000000000000..bbe7f1254a140a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/components/linked_agent_count.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { memo } from 'react'; +import { EuiLink, EuiLinkAnchorProps } from '@elastic/eui'; +import { useLink } from '../hooks'; +import { AGENT_SAVED_OBJECT_TYPE } from '../constants'; + +/** + * Displays the provided `count` number as a link to the Agents list if it is greater than zero + */ +export const LinkedAgentCount = memo< + Omit & { count: number; agentPolicyId: string } +>(({ count, agentPolicyId, ...otherEuiLinkProps }) => { + const { getHref } = useLink(); + return count > 0 ? ( + + {count} + + ) : ( + + {count} + + ); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts index 1ec43f4df8c8ea..ca76b65518ebea 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/index.ts @@ -8,7 +8,7 @@ export { AgentPolicyCopyProvider } from './agent_policy_copy_provider'; export { AgentPolicyDeleteProvider } from './agent_policy_delete_provider'; export { PackagePolicyDeleteProvider } from './package_policy_delete_provider'; export { AgentPolicyYamlFlyout } from './agent_policy_yaml_flyout'; -export { LinkedAgentCount } from './linked_agent_count'; +export { LinkedAgentCount } from '../../../components/linked_agent_count'; export { ConfirmDeployAgentPolicyModal } from './confirm_deploy_modal'; export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item'; export { AgentPolicyActionMenu } from './actions_menu'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx deleted file mode 100644 index c602f492f74c65..00000000000000 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/linked_agent_count.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { memo } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiLink } from '@elastic/eui'; -import { useLink } from '../../../hooks'; -import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; - -export const LinkedAgentCount = memo<{ count: number; agentPolicyId: string }>( - ({ count, agentPolicyId }) => { - const { getHref } = useLink(); - const displayValue = ( - - ); - return count > 0 ? ( - - {displayValue} - - ) : ( - displayValue - ); - } -); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.test.tsx index 3d43725f2dc714..2e4c65955e0dae 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.test.tsx @@ -166,11 +166,27 @@ describe('when on integration detail', () => { it('should link to integration policy detail when an integration policy is clicked', async () => { await mockedApi.waitForApi(); - const firstPolicy = renderResult.getByTestId('integrationNameLink') as HTMLAnchorElement; + const firstPolicy = renderResult.getAllByTestId( + 'integrationNameLink' + )[0] as HTMLAnchorElement; expect(firstPolicy.href).toEqual( 'http://localhost/mock/app/fleet#/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc' ); }); + + it('should NOT show link for agent count if it is zero', async () => { + await mockedApi.waitForApi(); + const firstRowAgentCount = renderResult.getAllByTestId('rowAgentCount')[0]; + expect(firstRowAgentCount.textContent).toEqual('0'); + expect(firstRowAgentCount.tagName).not.toEqual('A'); + }); + + it('should show link for agent count if greater than zero', async () => { + await mockedApi.waitForApi(); + const secondRowAgentCount = renderResult.getAllByTestId('rowAgentCount')[1]; + expect(secondRowAgentCount.textContent).toEqual('100'); + expect(secondRowAgentCount.tagName).toEqual('A'); + }); }); }); @@ -522,8 +538,87 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos updated_at: '2020-12-09T13:46:31.013Z', updated_by: 'elastic', }, + { + id: 'e3t37031-2907-44f6-89d2-5555555555', + version: 'WrrrMiwxXQ==', + name: 'nginx-2', + description: '', + namespace: 'default', + policy_id: '125c1b70-3976-11eb-ad1c-3baa423085y6', + enabled: true, + output_id: '', + inputs: [ + { + type: 'logfile', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'logs', dataset: 'nginx.access' }, + vars: { paths: { value: ['/var/log/nginx/access.log*'], type: 'text' } }, + id: 'logfile-nginx.access-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + paths: ['/var/log/nginx/access.log*'], + exclude_files: ['.gz$'], + processors: [{ add_locale: null }], + }, + }, + { + enabled: true, + data_stream: { type: 'logs', dataset: 'nginx.error' }, + vars: { paths: { value: ['/var/log/nginx/error.log*'], type: 'text' } }, + id: 'logfile-nginx.error-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + paths: ['/var/log/nginx/error.log*'], + exclude_files: ['.gz$'], + multiline: { + pattern: '^\\d{4}\\/\\d{2}\\/\\d{2} ', + negate: true, + match: 'after', + }, + processors: [{ add_locale: null }], + }, + }, + { + enabled: false, + data_stream: { type: 'logs', dataset: 'nginx.ingress_controller' }, + vars: { paths: { value: ['/var/log/nginx/ingress.log*'], type: 'text' } }, + id: 'logfile-nginx.ingress_controller-e8a37031-2907-44f6-89d2-98bd493f60dc', + }, + ], + }, + { + type: 'nginx/metrics', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { type: 'metrics', dataset: 'nginx.stubstatus' }, + vars: { + period: { value: '10s', type: 'text' }, + server_status_path: { value: '/nginx_status', type: 'text' }, + }, + id: 'nginx/metrics-nginx.stubstatus-e8a37031-2907-44f6-89d2-98bd493f60dc', + compiled_stream: { + metricsets: ['stubstatus'], + hosts: ['http://127.0.0.1:80'], + period: '10s', + server_status_path: '/nginx_status', + }, + }, + ], + vars: { hosts: { value: ['http://127.0.0.1:80'], type: 'text' } }, + }, + ], + package: { name: 'nginx', title: 'Nginx', version: '0.3.7' }, + revision: 3, + created_at: '2020-12-09T13:46:31.013Z', + created_by: 'elastic', + updated_at: '2020-12-09T13:46:31.013Z', + updated_by: 'elastic', + }, ], - total: 1, + total: 2, page: 1, perPage: 20, }; @@ -548,8 +643,22 @@ On Windows, the module was tested with Nginx installed from the Chocolatey repos updated_by: 'elastic', agents: 0, }, + { + id: '125c1b70-3976-11eb-ad1c-3baa423085y6', + name: 'EU Healthy agents', + namespace: 'default', + description: 'Protect EU from COVID', + status: 'active', + package_policies: ['e8a37031-2907-44f6-89d2-98bd493f60cd'], + is_default: false, + monitoring_enabled: ['logs', 'metrics'], + revision: 2, + updated_at: '2020-12-09T13:46:31.840Z', + updated_by: 'elastic', + agents: 100, + }, ], - total: 1, + total: 2, page: 1, perPage: 100, }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx index 4d8cb5a16034f7..c740adc4201dea 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/package_policies_panel.tsx @@ -17,10 +17,7 @@ import { FormattedRelative, FormattedMessage } from '@kbn/i18n/react'; import { useGetPackageInstallStatus } from '../../hooks'; import { InstallStatus } from '../../../../types'; import { useLink } from '../../../../hooks'; -import { - AGENT_SAVED_OBJECT_TYPE, - PACKAGE_POLICY_SAVED_OBJECT_TYPE, -} from '../../../../../../../common/constants'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../../../../common/constants'; import { useUrlPagination } from '../../../../hooks'; import { PackagePolicyAndAgentPolicy, @@ -28,6 +25,7 @@ import { } from './use_package_policies_with_agent_policy'; import { LinkAndRevision, LinkAndRevisionProps } from '../../../../components'; import { Persona } from './persona'; +import { LinkedAgentCount } from '../../../../components/linked_agent_count'; const IntegrationDetailsLink = memo<{ packagePolicy: PackagePolicyAndAgentPolicy['packagePolicy']; @@ -66,22 +64,6 @@ const AgentPolicyDetailLink = memo<{ ); }); -const PolicyAgentListLink = memo<{ agentPolicyId: string; children: ReactNode }>( - ({ agentPolicyId, children }) => { - const { getHref } = useLink(); - return ( - - {children} - - ); - } -); - interface PackagePoliciesPanelProps { name: string; version: string; @@ -156,9 +138,12 @@ export const PackagePoliciesPanel = ({ name, version }: PackagePoliciesPanelProp width: '8ch', render({ packagePolicy, agentPolicy }: PackagePolicyAndAgentPolicy) { return ( - - {agentPolicy?.agents ?? 0} - + ); }, }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b61532ec88c3cf..fda6b81c4af030 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -248,6 +248,7 @@ "charts.colormaps.redsText": "赤", "charts.colormaps.yellowToRedText": "黄色から赤", "charts.colorPicker.setColor.screenReaderDescription": "値 {legendDataLabel} の色を設定", + "charts.countText": "カウント", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", "console.consoleMenu.copyAsCurlMessage": "リクエストが URL としてコピーされました", @@ -1349,6 +1350,8 @@ "data.search.functions.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。", "data.search.functions.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します", "data.search.functions.kibana.help": "Kibana グローバルコンテキストを取得します", + "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。", + "data.triggers.applyFilterTitle": "フィルターを適用", "devTools.badge.readOnly.text": "読み込み専用", "devTools.badge.readOnly.tooltip": "を保存できませんでした", "devTools.devToolsTitle": "開発ツール", @@ -1574,6 +1577,10 @@ "embeddableApi.samples.contactCard.displayName": "連絡先カード", "embeddableApi.samples.filterableContainer.displayName": "フィルター可能なダッシュボード", "embeddableApi.samples.filterableEmbeddable.displayName": "フィルター可能", + "embeddableApi.selectRangeTrigger.description": "ビジュアライゼーションでの値の範囲", + "embeddableApi.selectRangeTrigger.title": "範囲選択", + "embeddableApi.valueClickTrigger.description": "ビジュアライゼーションでデータポイントをクリック", + "embeddableApi.valueClickTrigger.title": "シングルクリック", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "に", "esUi.cronEditor.cronDaily.fieldTimeLabel": "時間", "esUi.cronEditor.cronDaily.hourSelectLabel": "時間", @@ -3482,12 +3489,6 @@ "uiActions.actionPanel.more": "詳細", "uiActions.actionPanel.title": "オプション", "uiActions.errors.incompatibleAction": "操作に互換性がありません", - "data.triggers.applyFilterDescription": "Kibanaフィルターが適用されるとき。単一の値または範囲フィルターにすることができます。", - "data.triggers.applyFilterTitle": "フィルターを適用", - "embeddableApi.selectRangeTrigger.description": "ビジュアライゼーションでの値の範囲", - "embeddableApi.selectRangeTrigger.title": "範囲選択", - "embeddableApi.valueClickTrigger.description": "ビジュアライゼーションでデータポイントをクリック", - "embeddableApi.valueClickTrigger.title": "シングルクリック", "usageCollection.stats.notReadyMessage": "まだ統計が準備できていません。しばらくたってから再試行してください。", "visDefaultEditor.advancedToggle.advancedLinkLabel": "高度な設定", "visDefaultEditor.agg.toggleEditorButtonAriaLabel": "{schema} エディターを切り替える", @@ -4305,27 +4306,6 @@ "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "1つのデータソースが返せるバケットの最大数です。値が大きいとブラウザのレンダリング速度が下がる可能性があります。", "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "ヒートマップの最大バケット数", "visTypeVislib.aggResponse.allDocsTitle": "すべてのドキュメント", - "visTypeXy.area.areaTitle": "エリア", - "charts.countText": "カウント", - "visTypeXy.area.groupTitle": "系列を分割", - "visTypeXy.area.metricsTitle": "Y 軸", - "visTypeXy.area.radiusTitle": "点のサイズ", - "visTypeXy.area.segmentTitle": "X 軸", - "visTypeXy.area.splitTitle": "チャートを分割", - "visTypeXy.area.tabs.metricsAxesTitle": "メトリックと軸", - "visTypeXy.area.tabs.panelSettingsTitle": "パネル設定", - "visTypeXy.axisModes.normalText": "標準", - "visTypeXy.axisModes.percentageText": "割合 (%)", - "visTypeXy.axisModes.silhouetteText": "シルエット", - "visTypeXy.axisModes.wiggleText": "振動", - "visTypeXy.categoryAxis.rotate.angledText": "傾斜", - "visTypeXy.categoryAxis.rotate.horizontalText": "横", - "visTypeXy.categoryAxis.rotate.verticalText": "縦", - "visTypeXy.chartModes.normalText": "標準", - "visTypeXy.chartModes.stackedText": "スタック", - "visTypeXy.chartTypes.areaText": "エリア", - "visTypeXy.chartTypes.barText": "バー", - "visTypeXy.chartTypes.lineText": "折れ線", "visTypeVislib.controls.gaugeOptions.alignmentLabel": "アラインメント", "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "範囲を自動拡張", "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "警告を表示", @@ -4351,6 +4331,68 @@ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "ラベルを表示", "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "カスタム範囲を使用", + "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本設定", + "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", + "visTypeVislib.editors.heatmap.highlightLabel": "ハイライト範囲", + "visTypeVislib.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", + "visTypeVislib.editors.pie.donutLabel": "ドーナッツ", + "visTypeVislib.editors.pie.labelsSettingsTitle": "ラベル設定", + "visTypeVislib.editors.pie.pieSettingsTitle": "パイ設定", + "visTypeVislib.editors.pie.showLabelsLabel": "ラベルを表示", + "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示", + "visTypeVislib.editors.pie.showValuesLabel": "値を表示", + "visTypeVislib.functions.pie.help": "パイビジュアライゼーション", + "visTypeVislib.functions.vislib.help": "Vislib ビジュアライゼーション", + "visTypeVislib.gauge.alignmentAutomaticTitle": "自動", + "visTypeVislib.gauge.alignmentHorizontalTitle": "横", + "visTypeVislib.gauge.alignmentVerticalTitle": "縦", + "visTypeVislib.gauge.gaugeTitle": "ゲージ", + "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", + "visTypeVislib.gauge.gaugeTypes.circleText": "円", + "visTypeVislib.gauge.groupTitle": "グループを分割", + "visTypeVislib.gauge.metricTitle": "メトリック", + "visTypeVislib.goal.goalTitle": "ゴール", + "visTypeVislib.goal.groupTitle": "グループを分割", + "visTypeVislib.goal.metricTitle": "メトリック", + "visTypeVislib.heatmap.groupTitle": "Y 軸", + "visTypeVislib.heatmap.metricTitle": "値", + "visTypeVislib.heatmap.segmentTitle": "X 軸", + "visTypeVislib.heatmap.splitTitle": "チャートを分割", + "visTypeVislib.pie.metricTitle": "サイズのスライス", + "visTypeVislib.pie.pieTitle": "パイ", + "visTypeVislib.pie.segmentTitle": "スライスの分割", + "visTypeVislib.pie.splitTitle": "チャートを分割", + "visTypeVislib.vislib.errors.noResultsFoundTitle": "結果が見つかりませんでした", + "visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr})。構成されている最大値は {max} です。", + "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング", + "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", + "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外", + "visTypeVislib.vislib.legend.loadingLabel": "読み込み中…", + "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", + "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える", + "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", + "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド", + "visTypeVislib.vislib.tooltip.valueLabel": "値", + "visTypeXy.area.areaTitle": "エリア", + "visTypeXy.area.groupTitle": "系列を分割", + "visTypeXy.area.metricsTitle": "Y 軸", + "visTypeXy.area.radiusTitle": "点のサイズ", + "visTypeXy.area.segmentTitle": "X 軸", + "visTypeXy.area.splitTitle": "チャートを分割", + "visTypeXy.area.tabs.metricsAxesTitle": "メトリックと軸", + "visTypeXy.area.tabs.panelSettingsTitle": "パネル設定", + "visTypeXy.axisModes.normalText": "標準", + "visTypeXy.axisModes.percentageText": "割合 (%)", + "visTypeXy.axisModes.silhouetteText": "シルエット", + "visTypeXy.axisModes.wiggleText": "振動", + "visTypeXy.categoryAxis.rotate.angledText": "傾斜", + "visTypeXy.categoryAxis.rotate.horizontalText": "横", + "visTypeXy.categoryAxis.rotate.verticalText": "縦", + "visTypeXy.chartModes.normalText": "標準", + "visTypeXy.chartModes.stackedText": "スタック", + "visTypeXy.chartTypes.areaText": "エリア", + "visTypeXy.chartTypes.barText": "バー", + "visTypeXy.chartTypes.lineText": "折れ線", "visTypeXy.controls.pointSeries.categoryAxis.alignLabel": "配置", "visTypeXy.controls.pointSeries.categoryAxis.filterLabelsLabel": "フィルターラベル", "visTypeXy.controls.pointSeries.categoryAxis.labelsTitle": "ラベル", @@ -4393,16 +4435,6 @@ "visTypeXy.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "{axisName} オプションを切り替える", "visTypeXy.controls.pointSeries.valueAxes.yAxisTitle": "Y 軸", "visTypeXy.controls.truncateLabel": "切り捨て", - "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本設定", - "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", - "visTypeVislib.editors.heatmap.highlightLabel": "ハイライト範囲", - "visTypeVislib.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", - "visTypeVislib.editors.pie.donutLabel": "ドーナッツ", - "visTypeVislib.editors.pie.labelsSettingsTitle": "ラベル設定", - "visTypeVislib.editors.pie.pieSettingsTitle": "パイ設定", - "visTypeVislib.editors.pie.showLabelsLabel": "ラベルを表示", - "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示", - "visTypeVislib.editors.pie.showValuesLabel": "値を表示", "visTypeXy.editors.pointSeries.currentTimeMarkerLabel": "現在時刻マーカー", "visTypeXy.editors.pointSeries.orderBucketsBySumLabel": "バケットを合計で並べ替え", "visTypeXy.editors.pointSeries.settingsTitle": "設定", @@ -4413,23 +4445,6 @@ "visTypeXy.editors.pointSeries.thresholdLine.valueLabel": "しきい値", "visTypeXy.editors.pointSeries.thresholdLine.widthLabel": "線の幅", "visTypeXy.editors.pointSeries.thresholdLineSettingsTitle": "しきい線", - "visTypeVislib.functions.pie.help": "パイビジュアライゼーション", - "visTypeVislib.functions.vislib.help": "Vislib ビジュアライゼーション", - "visTypeVislib.gauge.alignmentAutomaticTitle": "自動", - "visTypeVislib.gauge.alignmentHorizontalTitle": "横", - "visTypeVislib.gauge.alignmentVerticalTitle": "縦", - "visTypeVislib.gauge.gaugeTitle": "ゲージ", - "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", - "visTypeVislib.gauge.gaugeTypes.circleText": "円", - "visTypeVislib.gauge.groupTitle": "グループを分割", - "visTypeVislib.gauge.metricTitle": "メトリック", - "visTypeVislib.goal.goalTitle": "ゴール", - "visTypeVislib.goal.groupTitle": "グループを分割", - "visTypeVislib.goal.metricTitle": "メトリック", - "visTypeVislib.heatmap.groupTitle": "Y 軸", - "visTypeVislib.heatmap.metricTitle": "値", - "visTypeVislib.heatmap.segmentTitle": "X 軸", - "visTypeVislib.heatmap.splitTitle": "チャートを分割", "visTypeXy.histogram.groupTitle": "系列を分割", "visTypeXy.histogram.metricTitle": "Y 軸", "visTypeXy.histogram.radiusTitle": "点のサイズ", @@ -4453,27 +4468,12 @@ "visTypeXy.line.radiusTitle": "点のサイズ", "visTypeXy.line.segmentTitle": "X 軸", "visTypeXy.line.splitTitle": "チャートを分割", - "visTypeVislib.pie.metricTitle": "サイズのスライス", - "visTypeVislib.pie.pieTitle": "パイ", - "visTypeVislib.pie.segmentTitle": "スライスの分割", - "visTypeVislib.pie.splitTitle": "チャートを分割", "visTypeXy.scaleTypes.linearText": "線形", "visTypeXy.scaleTypes.logText": "ログ", "visTypeXy.scaleTypes.squareRootText": "平方根", "visTypeXy.thresholdLine.style.dashedText": "鎖線", "visTypeXy.thresholdLine.style.dotdashedText": "点線", "visTypeXy.thresholdLine.style.fullText": "完全", - "visTypeVislib.vislib.errors.noResultsFoundTitle": "結果が見つかりませんでした", - "visTypeVislib.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr})。構成されている最大値は {max} です。", - "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "値 {legendDataLabel} でフィルタリング", - "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}、フィルターオプション", - "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "値 {legendDataLabel} を除外", - "visTypeVislib.vislib.legend.loadingLabel": "読み込み中…", - "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "凡例を切り替える", - "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "凡例を切り替える", - "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}、トグルオプション", - "visTypeVislib.vislib.tooltip.fieldLabel": "フィールド", - "visTypeVislib.vislib.tooltip.valueLabel": "値", "visualizations.advancedSettings.visualizeEnableLabsText": "ユーザーが実験的なビジュアライゼーションを作成、表示、編集できるようになります。無効の場合、\n ユーザーは本番準備が整ったビジュアライゼーションのみを利用できます。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "実験的なビジュアライゼーションを有効にする", "visualizations.disabledLabVisualizationMessage": "ラボビジュアライゼーションを表示するには、高度な設定でラボモードをオンにしてください。", @@ -7153,7 +7153,6 @@ "xpack.fleet.agentPolicy.confirmModalConfirmButtonLabel": "変更を保存してデプロイ", "xpack.fleet.agentPolicy.confirmModalDescription": "このアクションは元に戻せません。続行していいですか?", "xpack.fleet.agentPolicy.confirmModalTitle": "変更を保存してデプロイ", - "xpack.fleet.agentPolicy.linkedAgentCountText": "{count, plural, one {#件のエージェント} other {#件のエージェント}}", "xpack.fleet.agentPolicyActionMenu.buttonText": "アクション", "xpack.fleet.agentPolicyActionMenu.copyPolicyActionText": "ポリシーをコピー", "xpack.fleet.agentPolicyActionMenu.enrollAgentActionText": "エージェントの追加", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5377ae790c6014..609e09d0197af2 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -248,6 +248,7 @@ "charts.colormaps.redsText": "红色", "charts.colormaps.yellowToRedText": "黄到红", "charts.colorPicker.setColor.screenReaderDescription": "为值 {legendDataLabel} 设置颜色", + "charts.countText": "计数", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "控制台", "console.consoleMenu.copyAsCurlMessage": "请求已复制为 cURL", @@ -1350,6 +1351,8 @@ "data.search.functions.kibana_context.savedSearchId.help": "指定要用于查询和筛选的已保存搜索 ID", "data.search.functions.kibana_context.timeRange.help": "指定 Kibana 时间范围筛选", "data.search.functions.kibana.help": "获取 kibana 全局上下文", + "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。", + "data.triggers.applyFilterTitle": "应用筛选", "devTools.badge.readOnly.text": "只读", "devTools.badge.readOnly.tooltip": "无法保存", "devTools.devToolsTitle": "开发工具", @@ -1575,6 +1578,10 @@ "embeddableApi.samples.contactCard.displayName": "联系卡片", "embeddableApi.samples.filterableContainer.displayName": "可筛选仪表板", "embeddableApi.samples.filterableEmbeddable.displayName": "可筛选", + "embeddableApi.selectRangeTrigger.description": "可视化上的一组值", + "embeddableApi.selectRangeTrigger.title": "范围选择", + "embeddableApi.valueClickTrigger.description": "可视化上的数据点单击", + "embeddableApi.valueClickTrigger.title": "单击", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "在", "esUi.cronEditor.cronDaily.fieldTimeLabel": "时间", "esUi.cronEditor.cronDaily.hourSelectLabel": "小时", @@ -3483,12 +3490,6 @@ "uiActions.actionPanel.more": "更多", "uiActions.actionPanel.title": "选项", "uiActions.errors.incompatibleAction": "操作不兼容", - "data.triggers.applyFilterDescription": "应用 kibana 筛选时。可能是单个值或范围筛选。", - "data.triggers.applyFilterTitle": "应用筛选", - "embeddableApi.selectRangeTrigger.description": "可视化上的一组值", - "embeddableApi.selectRangeTrigger.title": "范围选择", - "embeddableApi.valueClickTrigger.description": "可视化上的数据点单击", - "embeddableApi.valueClickTrigger.title": "单击", "usageCollection.stats.notReadyMessage": "统计信息尚未准备就绪。请稍后重试。", "visDefaultEditor.advancedToggle.advancedLinkLabel": "高级", "visDefaultEditor.agg.toggleEditorButtonAriaLabel": "切换 {schema} 编辑器", @@ -4307,27 +4308,6 @@ "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsText": "单个数据源可以返回的最大存储桶数目。较高的数目可能对浏览器呈现性能有负面影响", "visTypeVislib.advancedSettings.visualization.heatmap.maxBucketsTitle": "热图最大存储桶数", "visTypeVislib.aggResponse.allDocsTitle": "所有文档", - "visTypeXy.area.areaTitle": "面积图", - "charts.countText": "计数", - "visTypeXy.area.groupTitle": "拆分序列", - "visTypeXy.area.metricsTitle": "Y 轴", - "visTypeXy.area.radiusTitle": "点大小", - "visTypeXy.area.segmentTitle": "X 轴", - "visTypeXy.area.splitTitle": "拆分图表", - "visTypeXy.area.tabs.metricsAxesTitle": "指标和轴", - "visTypeXy.area.tabs.panelSettingsTitle": "面板设置", - "visTypeXy.axisModes.normalText": "正常", - "visTypeXy.axisModes.percentageText": "百分比", - "visTypeXy.axisModes.silhouetteText": "剪影", - "visTypeXy.axisModes.wiggleText": "扭动", - "visTypeXy.categoryAxis.rotate.angledText": "带角度", - "visTypeXy.categoryAxis.rotate.horizontalText": "水平", - "visTypeXy.categoryAxis.rotate.verticalText": "垂直", - "visTypeXy.chartModes.normalText": "正常", - "visTypeXy.chartModes.stackedText": "堆叠", - "visTypeXy.chartTypes.areaText": "面积图", - "visTypeXy.chartTypes.barText": "条形图", - "visTypeXy.chartTypes.lineText": "折线图", "visTypeVislib.controls.gaugeOptions.alignmentLabel": "对齐方式", "visTypeVislib.controls.gaugeOptions.autoExtendRangeLabel": "自动扩展范围", "visTypeVislib.controls.gaugeOptions.displayWarningsLabel": "显示警告", @@ -4353,6 +4333,68 @@ "visTypeVislib.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界", "visTypeVislib.controls.heatmapOptions.showLabelsTitle": "显示标签", "visTypeVislib.controls.heatmapOptions.useCustomRangesLabel": "使用定制范围", + "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本设置", + "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "热图设置", + "visTypeVislib.editors.heatmap.highlightLabel": "高亮范围", + "visTypeVislib.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", + "visTypeVislib.editors.pie.donutLabel": "圆环图", + "visTypeVislib.editors.pie.labelsSettingsTitle": "标签设置", + "visTypeVislib.editors.pie.pieSettingsTitle": "饼图设置", + "visTypeVislib.editors.pie.showLabelsLabel": "显示标签", + "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "仅显示顶级", + "visTypeVislib.editors.pie.showValuesLabel": "显示值", + "visTypeVislib.functions.pie.help": "饼图可视化", + "visTypeVislib.functions.vislib.help": "Vislib 可视化", + "visTypeVislib.gauge.alignmentAutomaticTitle": "自动", + "visTypeVislib.gauge.alignmentHorizontalTitle": "水平", + "visTypeVislib.gauge.alignmentVerticalTitle": "垂直", + "visTypeVislib.gauge.gaugeTitle": "仪表盘图", + "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", + "visTypeVislib.gauge.gaugeTypes.circleText": "圆形", + "visTypeVislib.gauge.groupTitle": "拆分组", + "visTypeVislib.gauge.metricTitle": "指标", + "visTypeVislib.goal.goalTitle": "目标图", + "visTypeVislib.goal.groupTitle": "拆分组", + "visTypeVislib.goal.metricTitle": "指标", + "visTypeVislib.heatmap.groupTitle": "Y 轴", + "visTypeVislib.heatmap.metricTitle": "值", + "visTypeVislib.heatmap.segmentTitle": "X 轴", + "visTypeVislib.heatmap.splitTitle": "拆分图表", + "visTypeVislib.pie.metricTitle": "切片大小", + "visTypeVislib.pie.pieTitle": "饼图", + "visTypeVislib.pie.segmentTitle": "拆分切片", + "visTypeVislib.pie.splitTitle": "拆分图表", + "visTypeVislib.vislib.errors.noResultsFoundTitle": "找不到结果", + "visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", + "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}", + "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", + "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}", + "visTypeVislib.vislib.legend.loadingLabel": "正在加载……", + "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例", + "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例", + "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项", + "visTypeVislib.vislib.tooltip.fieldLabel": "字段", + "visTypeVislib.vislib.tooltip.valueLabel": "值", + "visTypeXy.area.areaTitle": "面积图", + "visTypeXy.area.groupTitle": "拆分序列", + "visTypeXy.area.metricsTitle": "Y 轴", + "visTypeXy.area.radiusTitle": "点大小", + "visTypeXy.area.segmentTitle": "X 轴", + "visTypeXy.area.splitTitle": "拆分图表", + "visTypeXy.area.tabs.metricsAxesTitle": "指标和轴", + "visTypeXy.area.tabs.panelSettingsTitle": "面板设置", + "visTypeXy.axisModes.normalText": "正常", + "visTypeXy.axisModes.percentageText": "百分比", + "visTypeXy.axisModes.silhouetteText": "剪影", + "visTypeXy.axisModes.wiggleText": "扭动", + "visTypeXy.categoryAxis.rotate.angledText": "带角度", + "visTypeXy.categoryAxis.rotate.horizontalText": "水平", + "visTypeXy.categoryAxis.rotate.verticalText": "垂直", + "visTypeXy.chartModes.normalText": "正常", + "visTypeXy.chartModes.stackedText": "堆叠", + "visTypeXy.chartTypes.areaText": "面积图", + "visTypeXy.chartTypes.barText": "条形图", + "visTypeXy.chartTypes.lineText": "折线图", "visTypeXy.controls.pointSeries.categoryAxis.alignLabel": "对齐", "visTypeXy.controls.pointSeries.categoryAxis.filterLabelsLabel": "筛选标签", "visTypeXy.controls.pointSeries.categoryAxis.labelsTitle": "标签", @@ -4395,16 +4437,6 @@ "visTypeXy.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "切换 {axisName} 选项", "visTypeXy.controls.pointSeries.valueAxes.yAxisTitle": "Y 轴", "visTypeXy.controls.truncateLabel": "截断", - "visTypeVislib.editors.heatmap.basicSettingsTitle": "基本设置", - "visTypeVislib.editors.heatmap.heatmapSettingsTitle": "热图设置", - "visTypeVislib.editors.heatmap.highlightLabel": "高亮范围", - "visTypeVislib.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", - "visTypeVislib.editors.pie.donutLabel": "圆环图", - "visTypeVislib.editors.pie.labelsSettingsTitle": "标签设置", - "visTypeVislib.editors.pie.pieSettingsTitle": "饼图设置", - "visTypeVislib.editors.pie.showLabelsLabel": "显示标签", - "visTypeVislib.editors.pie.showTopLevelOnlyLabel": "仅显示顶级", - "visTypeVislib.editors.pie.showValuesLabel": "显示值", "visTypeXy.editors.pointSeries.currentTimeMarkerLabel": "当前时间标记", "visTypeXy.editors.pointSeries.orderBucketsBySumLabel": "按总计值排序存储桶", "visTypeXy.editors.pointSeries.settingsTitle": "设置", @@ -4415,23 +4447,6 @@ "visTypeXy.editors.pointSeries.thresholdLine.valueLabel": "阈值", "visTypeXy.editors.pointSeries.thresholdLine.widthLabel": "线条宽度", "visTypeXy.editors.pointSeries.thresholdLineSettingsTitle": "阈值线条", - "visTypeVislib.functions.pie.help": "饼图可视化", - "visTypeVislib.functions.vislib.help": "Vislib 可视化", - "visTypeVislib.gauge.alignmentAutomaticTitle": "自动", - "visTypeVislib.gauge.alignmentHorizontalTitle": "水平", - "visTypeVislib.gauge.alignmentVerticalTitle": "垂直", - "visTypeVislib.gauge.gaugeTitle": "仪表盘图", - "visTypeVislib.gauge.gaugeTypes.arcText": "弧形", - "visTypeVislib.gauge.gaugeTypes.circleText": "圆形", - "visTypeVislib.gauge.groupTitle": "拆分组", - "visTypeVislib.gauge.metricTitle": "指标", - "visTypeVislib.goal.goalTitle": "目标图", - "visTypeVislib.goal.groupTitle": "拆分组", - "visTypeVislib.goal.metricTitle": "指标", - "visTypeVislib.heatmap.groupTitle": "Y 轴", - "visTypeVislib.heatmap.metricTitle": "值", - "visTypeVislib.heatmap.segmentTitle": "X 轴", - "visTypeVislib.heatmap.splitTitle": "拆分图表", "visTypeXy.histogram.groupTitle": "拆分序列", "visTypeXy.histogram.metricTitle": "Y 轴", "visTypeXy.histogram.radiusTitle": "点大小", @@ -4455,27 +4470,12 @@ "visTypeXy.line.radiusTitle": "点大小", "visTypeXy.line.segmentTitle": "X 轴", "visTypeXy.line.splitTitle": "拆分图表", - "visTypeVislib.pie.metricTitle": "切片大小", - "visTypeVislib.pie.pieTitle": "饼图", - "visTypeVislib.pie.segmentTitle": "拆分切片", - "visTypeVislib.pie.splitTitle": "拆分图表", "visTypeXy.scaleTypes.linearText": "线性", "visTypeXy.scaleTypes.logText": "对数", "visTypeXy.scaleTypes.squareRootText": "平方根", "visTypeXy.thresholdLine.style.dashedText": "虚线", "visTypeXy.thresholdLine.style.dotdashedText": "点虚线", "visTypeXy.thresholdLine.style.fullText": "实线", - "visTypeVislib.vislib.errors.noResultsFoundTitle": "找不到结果", - "visTypeVislib.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", - "visTypeVislib.vislib.legend.filterForValueButtonAriaLabel": "筛留值 {legendDataLabel}", - "visTypeVislib.vislib.legend.filterOptionsLegend": "{legendDataLabel}, 筛选选项", - "visTypeVislib.vislib.legend.filterOutValueButtonAriaLabel": "筛除值 {legendDataLabel}", - "visTypeVislib.vislib.legend.loadingLabel": "正在加载……", - "visTypeVislib.vislib.legend.toggleLegendButtonAriaLabel": "切换图例", - "visTypeVislib.vislib.legend.toggleLegendButtonTitle": "切换图例", - "visTypeVislib.vislib.legend.toggleOptionsButtonAriaLabel": "{legendDataLabel}, 切换选项", - "visTypeVislib.vislib.tooltip.fieldLabel": "字段", - "visTypeVislib.vislib.tooltip.valueLabel": "值", "visualizations.advancedSettings.visualizeEnableLabsText": "允许用户创建、查看和编辑实验性可视化。如果禁用,\n 仅被视为生产就绪的可视化可供用户使用。", "visualizations.advancedSettings.visualizeEnableLabsTitle": "启用实验性可视化", "visualizations.disabledLabVisualizationMessage": "请在高级设置中打开实验室模式,以查看实验室可视化。", @@ -7160,7 +7160,6 @@ "xpack.fleet.agentPolicy.confirmModalConfirmButtonLabel": "保存并部署更改", "xpack.fleet.agentPolicy.confirmModalDescription": "此操作无法撤消。是否确定要继续?", "xpack.fleet.agentPolicy.confirmModalTitle": "保存并部署更改", - "xpack.fleet.agentPolicy.linkedAgentCountText": "{count, plural, one {# 个代理} other {# 个代理}}", "xpack.fleet.agentPolicyActionMenu.buttonText": "操作", "xpack.fleet.agentPolicyActionMenu.copyPolicyActionText": "复制策略", "xpack.fleet.agentPolicyActionMenu.enrollAgentActionText": "添加代理", From e575af338669eaa9c4e2286e9b675b20ea722723 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 23 Dec 2020 15:53:44 +0100 Subject: [PATCH 078/100] [Expression Renderer] Fix Expression Renderer className composition (#86094) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/react_expression_renderer.test.tsx | 18 ++++++++++++++++++ .../public/react_expression_renderer.tsx | 3 +-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index 4ebd626e70fc3e..ac6fcab33acbff 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -304,4 +304,22 @@ describe('ExpressionRenderer', () => { expect(onEvent).toHaveBeenCalledTimes(1); expect(onEvent.mock.calls[0][0]).toBe(event); }); + + it('should correctly assign classes to the wrapper node', () => { + (ExpressionLoader as jest.Mock).mockImplementation(() => { + return { + render$: new Subject(), + data$: new Subject(), + loading$: new Subject(), + update: jest.fn(), + destroy: jest.fn(), + }; + }); + + const instance = mount(); + // Counte is 2 because the class is applied to ReactExpressionRenderer + internal component + expect(instance.find('.myClassName').length).toBe(2); + + instance.unmount(); + }); }); diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index d19f4821078458..3227b34dcc1ff0 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -211,10 +211,9 @@ export const ReactExpressionRenderer = ({ } }, [state.error]); - const classes = classNames('expExpressionRenderer', { + const classes = classNames('expExpressionRenderer', className, { 'expExpressionRenderer-isEmpty': state.isEmpty, 'expExpressionRenderer-hasError': !!state.error, - className, }); const expressionStyles: React.CSSProperties = {}; From ca685f01fcd081c4aaa27f0da79bbccee3ec89a2 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 23 Dec 2020 18:22:56 +0300 Subject: [PATCH 079/100] Removed unneeded dependency from hook (#86888) --- src/plugins/vis_type_vislib/public/vis_wrapper.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx index b8dbd0f945c325..e2e8a98a9a8b6c 100644 --- a/src/plugins/vis_type_vislib/public/vis_wrapper.tsx +++ b/src/plugins/vis_type_vislib/public/vis_wrapper.tsx @@ -61,7 +61,7 @@ const VislibWrapper = ({ core, charts, visData, visConfig, handlers }: VislibWra visController.current?.destroy(); visController.current = null; }; - }, [core, charts, handlers]); + }, [core, charts]); useEffect(updateChart, [updateChart]); From 2cc2312f6d7a98f91ae8229c7a6509d3110e74a9 Mon Sep 17 00:00:00 2001 From: Constance Date: Wed, 23 Dec 2020 10:39:21 -0800 Subject: [PATCH 080/100] [App Search] Result component - a11y enhancements (#86841) * Refactor Result card layout - Move toggle action to the bottom of the card content - [TODO] Action button to the right will be used for new link button (separate for accessibility/screen readers) - Use grid to get the layout we want without extra div wrappers * Add action button link to document detail + remove tag on article content - should have onClick only - this allows screenreaders to granularly navigate through the card content while allowing mouse users the entire card to click - the new actionButton details link is accessible to both keyboard & screen reader users * [Polish] Hover effects to help guide mouse users * [i18n] Add pluralization to fields copy * Update tests * [Cleanup] Remove unneeded wrapper * [??] More specific title for result group - since the aria-label for the new detail button link is basically that --- .../app_search/components/result/result.scss | 57 +++++++++-- .../components/result/result.test.tsx | 69 +++++++------ .../app_search/components/result/result.tsx | 97 +++++++++++-------- 3 files changed, 142 insertions(+), 81 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss index 8342061ee00c3b..f69acbdaba150f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.scss @@ -1,17 +1,43 @@ .appSearchResult { - display: flex; + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: 1fr auto; + grid-template-areas: + 'content actions' + 'toggle actions'; + overflow: hidden; // Prevents child background-colors from clipping outside of panel border-radius &__content { + grid-area: content; width: 100%; padding: $euiSize; overflow: hidden; color: $euiTextColor; } - &__hiddenFieldsIndicator { + &__hiddenFieldsToggle { + grid-area: toggle; + display: flex; + justify-content: center; + padding: $euiSizeS; + border-top: $euiBorderThin; font-size: $euiFontSizeXS; - color: $euiColorDarkShade; - margin-top: $euiSizeS; + color: $euiColorPrimary; + + &:hover, + &:focus { + background-color: $euiPageBackgroundColor; + } + + .euiIcon { + margin-left: $euiSizeXS; + } + } + + &__actionButtons { + grid-area: actions; + display: flex; + flex-wrap: no-wrap; } &__actionButton { @@ -22,10 +48,27 @@ border-left: $euiBorderThin; &:hover, - &:focus, - &:active { + &:focus { background-color: $euiPageBackgroundColor; - cursor: pointer; } } } + +/** + * CSS for hover specific logic + * It's mildly horrific, so I pulled it out to its own section here + */ + +.appSearchResult--link { + &:hover, + &:focus { + @include euiSlightShadowHover; + } +} +.appSearchResult__content--link:hover { + cursor: pointer; + + & ~ .appSearchResult__actionButtons .appSearchResult__actionButton--link { + background-color: $euiPageBackgroundColor; + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx index c4de24e78eae5e..973fc6226910a8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.test.tsx @@ -49,6 +49,7 @@ describe('Result', () => { it('renders', () => { const wrapper = shallow(); expect(wrapper.find(EuiPanel).exists()).toBe(true); + expect(wrapper.find(EuiPanel).prop('title')).toEqual('Document 1'); }); it('should render a ResultField for each field except id and _meta', () => { @@ -76,16 +77,20 @@ describe('Result', () => { describe('document detail link', () => { it('will render a link if shouldLinkToDetailPage is true', () => { const wrapper = shallow(); - expect(wrapper.find(ReactRouterHelper).prop('to')).toEqual('/engines/my-engine/documents/1'); - expect(wrapper.find('article.appSearchResult__content').exists()).toBe(false); - expect(wrapper.find('a.appSearchResult__content').exists()).toBe(true); + wrapper.find(ReactRouterHelper).forEach((link) => { + expect(link.prop('to')).toEqual('/engines/my-engine/documents/1'); + }); + expect(wrapper.hasClass('appSearchResult--link')).toBe(true); + expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(true); + expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(true); }); it('will not render a link if shouldLinkToDetailPage is not set', () => { const wrapper = shallow(); expect(wrapper.find(ReactRouterHelper).exists()).toBe(false); - expect(wrapper.find('article.appSearchResult__content').exists()).toBe(true); - expect(wrapper.find('a.appSearchResult__content').exists()).toBe(false); + expect(wrapper.hasClass('appSearchResult--link')).toBe(false); + expect(wrapper.find('.appSearchResult__content--link').exists()).toBe(false); + expect(wrapper.find('.appSearchResult__actionButton--link').exists()).toBe(false); }); }); @@ -140,18 +145,16 @@ describe('Result', () => { wrapper = shallow(); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); + it('renders a hidden fields toggle button', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); }); - it('renders a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').text()).toEqual( - '1 more fields' - ); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); }); it('shows no more than 5 fields', () => { @@ -164,20 +167,22 @@ describe('Result', () => { beforeAll(() => { wrapper = shallow(); - expect(wrapper.find('.appSearchResult__actionButton').exists()).toBe(true); - wrapper.find('.appSearchResult__actionButton').simulate('click'); + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(true); + it('renders correct toggle text', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').text()).toEqual( + 'Hide additional fields' + ); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(false); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(true); }); - it('does not render a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').exists()).toBe(false); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(false); }); it('shows all fields', () => { @@ -190,23 +195,23 @@ describe('Result', () => { beforeAll(() => { wrapper = shallow(); - expect(wrapper.find('.appSearchResult__actionButton').exists()).toBe(true); - wrapper.find('.appSearchResult__actionButton').simulate('click'); - wrapper.find('.appSearchResult__actionButton').simulate('click'); + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').exists()).toBe(true); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); + wrapper.find('.appSearchResult__hiddenFieldsToggle').simulate('click'); }); - it('renders a collapse button', () => { - expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); + it('renders correct toggle text', () => { + expect(wrapper.find('.appSearchResult__hiddenFieldsToggle').text()).toEqual( + 'Show 1 additional field' + ); }); - it('does not render an expand button', () => { - expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); + it('renders a collapse icon', () => { + expect(wrapper.find('[data-test-subj="CollapseResult"]').exists()).toBe(false); }); - it('renders a hidden fields indicator', () => { - expect(wrapper.find('.appSearchResult__hiddenFieldsIndicator').text()).toEqual( - '1 more fields' - ); + it('does not render an expand icon', () => { + expect(wrapper.find('[data-test-subj="ExpandResult"]').exists()).toBe(true); }); it('shows no more than 5 fields', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx index 76b06754d6ce68..f25eb2a4ba09ee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx @@ -5,6 +5,7 @@ */ import React, { useState, useMemo } from 'react'; +import classNames from 'classnames'; import './result.scss'; @@ -49,23 +50,31 @@ export const Result: React.FC = ({ if (schemaForTypeHighlights) return schemaForTypeHighlights[fieldName]; }; + const documentLink = getDocumentDetailRoute(resultMeta.engine, resultMeta.id); const conditionallyLinkedArticle = (children: React.ReactNode) => { return shouldLinkToDetailPage ? ( - - {children} + +
    + {children} +
    ) : (
    {children}
    ); }; + const classes = classNames('appSearchResult', { + 'appSearchResult--link': shouldLinkToDetailPage, + }); + return ( {conditionallyLinkedArticle( @@ -75,53 +84,57 @@ export const Result: React.FC = ({ showScore={!!showScore} isMetaEngine={isMetaEngine} /> -
    - {resultFields - .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF) - .map(([field, value]: [string, FieldValue]) => ( - - ))} -
    - {numResults > RESULT_CUTOFF && !isOpen && ( -
    - {i18n.translate('xpack.enterpriseSearch.appSearch.result.numberOfAdditionalFields', { - defaultMessage: '{numberOfAdditionalFields} more fields', - values: { - numberOfAdditionalFields: numResults - RESULT_CUTOFF, - }, - })} -
    - )} + {resultFields + .slice(0, isOpen ? resultFields.length : RESULT_CUTOFF) + .map(([field, value]: [string, FieldValue]) => ( + + ))} )} {numResults > RESULT_CUTOFF && ( )} +
    + {shouldLinkToDetailPage && ( + + + + + + )} +
    ); }; From f7961998d9bea29c4c89f9be49f7d2363987525d Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 23 Dec 2020 12:18:13 -0700 Subject: [PATCH 081/100] Migrates spaces usage collector es client from legacy to new (#86900) --- .../spaces_usage_collector.test.ts | 66 ++++++++++++------- .../spaces_usage_collector.ts | 18 ++--- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts index ea8770b7843cf3..747e37e7db32b8 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.test.ts @@ -12,7 +12,10 @@ import { ILicense, LicensingPluginSetup } from '../../../licensing/server'; import { UsageStats } from '../usage_stats'; import { usageStatsClientMock } from '../usage_stats/usage_stats_client.mock'; import { usageStatsServiceMock } from '../usage_stats/usage_stats_service.mock'; -import { pluginInitializerContextConfigMock } from 'src/core/server/mocks'; +import { + elasticsearchServiceMock, + pluginInitializerContextConfigMock, +} from 'src/core/server/mocks'; import { createCollectorFetchContextMock } from 'src/plugins/usage_collection/server/mocks'; interface SetupOpts { @@ -74,31 +77,39 @@ function setup({ }; } -const defaultCallClusterMock = jest.fn().mockResolvedValue({ - hits: { - total: { - value: 2, +const defaultEsClientSearchMock = jest.fn().mockResolvedValue({ + body: { + hits: { + total: { + value: 2, + }, }, - }, - aggregations: { - disabledFeatures: { - buckets: [ - { - key: 'feature1', - doc_count: 1, - }, - ], + aggregations: { + disabledFeatures: { + buckets: [ + { + key: 'feature1', + doc_count: 1, + }, + ], + }, }, }, }); -const getMockFetchContext = (mockedCallCluster: jest.Mock) => { +const getMockFetchContext = (mockedEsClient: any) => { return { ...createCollectorFetchContextMock(), - callCluster: mockedCallCluster, + esClient: mockedEsClient, }; }; +const getMockedEsClient = (esClientMock: jest.Mock) => { + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.search = esClientMock; + return esClient; +}; + describe('error handling', () => { it('handles a 404 when searching for space usage', async () => { const { features, licensing, usageCollection, usageStatsService } = setup({ @@ -110,8 +121,10 @@ describe('error handling', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + esClient.search.mockRejectedValue({ status: 404 }); - await collector.fetch(getMockFetchContext(jest.fn().mockRejectedValue({ status: 404 }))); + await collector.fetch(getMockFetchContext(esClient)); }); it('throws error for a non-404', async () => { @@ -124,13 +137,13 @@ describe('error handling', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const statusCodes = [401, 402, 403, 500]; for (const statusCode of statusCodes) { const error = { status: statusCode }; - await expect( - collector.fetch(getMockFetchContext(jest.fn().mockRejectedValue(error))) - ).rejects.toBe(error); + esClient.search.mockRejectedValue(error); + await expect(collector.fetch(getMockFetchContext(esClient))).rejects.toBe(error); } }); }); @@ -148,9 +161,10 @@ describe('with a basic license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + usageData = await collector.fetch(getMockFetchContext(esClient)); - expect(defaultCallClusterMock).toHaveBeenCalledWith('search', { + expect(defaultEsClientSearchMock).toHaveBeenCalledWith({ body: { aggs: { disabledFeatures: { @@ -206,7 +220,9 @@ describe('with no license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + + usageData = await collector.fetch(getMockFetchContext(esClient)); }); test('sets enabled to false', () => { @@ -245,7 +261,9 @@ describe('with platinum license', () => { licensing, usageStatsServicePromise: Promise.resolve(usageStatsService), }); - usageData = await collector.fetch(getMockFetchContext(defaultCallClusterMock)); + const esClient = getMockedEsClient(defaultEsClientSearchMock); + + usageData = await collector.fetch(getMockFetchContext(esClient)); }); test('sets enabled to true', () => { diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 44388453d0707c..269490bddd8dc0 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -4,19 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyCallAPIOptions } from 'src/core/server'; +import { ElasticsearchClient } from 'src/core/server'; import { take } from 'rxjs/operators'; import { CollectorFetchContext, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { Observable } from 'rxjs'; import { PluginsSetup } from '../plugin'; import { UsageStats, UsageStatsServiceSetup } from '../usage_stats'; -type CallCluster = ( - endpoint: string, - clientParams: Record, - options?: LegacyCallAPIOptions -) => Promise; - interface SpacesAggregationResponse { hits: { total: { value: number }; @@ -37,7 +31,7 @@ interface SpacesAggregationResponse { * @return {UsageData} */ async function getSpacesUsage( - callCluster: CallCluster, + esClient: ElasticsearchClient, kibanaIndex: string, features: PluginsSetup['features'], spacesAvailable: boolean @@ -50,7 +44,7 @@ async function getSpacesUsage( let resp: SpacesAggregationResponse | undefined; try { - resp = await callCluster('search', { + ({ body: resp } = await esClient.search({ index: kibanaIndex, body: { track_total_hits: true, @@ -72,7 +66,7 @@ async function getSpacesUsage( }, size: 0, }, - }); + })); } catch (err) { if (err.status === 404) { return null; @@ -208,14 +202,14 @@ export function getSpacesUsageCollector( 'apiCalls.resolveCopySavedObjectsErrors.createNewCopiesEnabled.yes': { type: 'long' }, 'apiCalls.resolveCopySavedObjectsErrors.createNewCopiesEnabled.no': { type: 'long' }, }, - fetch: async ({ callCluster }: CollectorFetchContext) => { + fetch: async ({ esClient }: CollectorFetchContext) => { const { licensing, kibanaIndexConfig$, features, usageStatsServicePromise } = deps; const license = await licensing.license$.pipe(take(1)).toPromise(); const available = license.isAvailable; // some form of spaces is available for all valid licenses const kibanaIndex = (await kibanaIndexConfig$.pipe(take(1)).toPromise()).kibana.index; - const usageData = await getSpacesUsage(callCluster, kibanaIndex, features, available); + const usageData = await getSpacesUsage(esClient, kibanaIndex, features, available); const usageStats = await getUsageStats(usageStatsServicePromise, available); return { From cf6afe04adc0a52ce5b89e01004512870f0aa7d3 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Wed, 23 Dec 2020 14:25:34 -0500 Subject: [PATCH 082/100] [Uptime] Fix/85236 user experience display low values (#86026) * add hasVitals prop to CoreVitalItem * pass hasVitals prop to CoreVitalsItem based on coreVitalPages * adjust criteria for displaying no core vital item data * add stories for CoreVitalItem edge cases * remove comment from core web vitals index page * update test comment in CoreVitalItem * adjust APM get_web_core_vitals endpoint to return a number for cls value, and adjust corresponding observability components * remove hasVitals from CoreVitalItem props and adjust storybook stories * add comment to EuiStat aria-label in CoreVitalItem * adjust CoreVitalItem tests * adjust APM KeyUXMetrics test * adjust APM get_web_core_vitals endpoint to return null for cls when cls is undefined * adjust unit and integration tests that rely on apm get_web_core_vitals * add comment in get_web_core_vitals * update CLS value in Observability core_web_vitals index * add withKibanaIntl to CoreVitalItem test to wrap in Intl Provider and KibanaReact provider * update CoreVitalItem test to use testing-library/react test_helper Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../UXMetrics/__tests__/KeyUXMetrics.test.tsx | 2 +- .../lib/rum_client/get_web_core_vitals.ts | 4 +- .../components/app/section/ux/index.test.tsx | 2 +- .../app/section/ux/mock_data/ux.mock.ts | 2 +- .../__stories__/core_vitals.stories.tsx | 15 ++++- .../core_web_vitals/core_vital_item.test.tsx | 67 +++++++++++++++++++ .../core_web_vitals/core_vital_item.tsx | 4 +- .../shared/core_web_vitals/index.tsx | 22 +++--- .../observability/public/data_handler.test.ts | 4 +- .../trial/tests/csm/web_core_vitals.ts | 2 +- 10 files changed, 104 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx index baa9cb7dd74f9f..5d73cbc4cd3c80 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/__tests__/KeyUXMetrics.test.tsx @@ -23,7 +23,7 @@ describe('KeyUXMetrics', () => { { expect(getByText('Largest contentful paint')).toBeInTheDocument(); expect(getByText('1.94 s')).toBeInTheDocument(); expect(getByText('14 ms')).toBeInTheDocument(); - expect(getByText('0.01')).toBeInTheDocument(); + expect(getByText('0.010')).toBeInTheDocument(); // LCP Rank Values expect(getByText('Good (65%)')).toBeInTheDocument(); diff --git a/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts b/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts index 017f385d36735b..bbe81699e999d6 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts +++ b/x-pack/plugins/observability/public/components/app/section/ux/mock_data/ux.mock.ts @@ -9,7 +9,7 @@ import { UxFetchDataResponse } from '../../../../../typings'; export const response: UxFetchDataResponse = { appLink: '/app/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 13.5, lcp: 1942.6666666666667, tbt: 281.55833333333334, diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx index 26cf9c144b4a17..208c840b403e95 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/__stories__/core_vitals.stories.tsx @@ -33,13 +33,26 @@ export default { ], }; -export function Basic() { +export function NoDataAvailable() { + return ( + + ); +} + +export function OneHundredPercentGood() { return ( ); diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx new file mode 100644 index 00000000000000..346355e11c6ef2 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.test.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { render } from '../../../utils/test_helper'; +import { CoreVitalItem } from './core_vital_item'; +import { NO_DATA } from './translations'; + +describe('CoreVitalItem', () => { + const value = '0.005'; + const title = 'Cumulative Layout Shift'; + const thresholds = { bad: '0.25', good: '0.1' }; + const loading = false; + const helpLabel = 'sample help label'; + + it('renders if value is truthy', () => { + const { getByText } = render( + + ); + + expect(getByText(title)).toBeInTheDocument(); + expect(getByText(value)).toBeInTheDocument(); + expect(getByText('Good (85%)')).toBeInTheDocument(); + expect(getByText('Needs improvement (10%)')).toBeInTheDocument(); + expect(getByText('Poor (5%)')).toBeInTheDocument(); + }); + + it('renders loading state when loading is truthy', () => { + const { queryByText, getByText } = render( + + ); + + expect(queryByText(value)).not.toBeInTheDocument(); + expect(getByText('--')).toBeInTheDocument(); + }); + + it('renders no data UI if value is falsey and loading is falsey', () => { + const { getByText } = render( + + ); + + expect(getByText(NO_DATA)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx index 18831565b8784b..23dd0b86a235b5 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx @@ -88,12 +88,14 @@ export function CoreVitalItem({ const biggestValIndex = ranks.indexOf(Math.max(...ranks)); - if ((value === null || value !== undefined) && ranks[0] === 100 && !loading) { + if (!value && !loading) { return ; } + return ( <> { title: 'User Experience', appLink: '/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 5, lcp: 1464.3333333333333, tbt: 232.92166666666665, @@ -298,7 +298,7 @@ describe('registerDataHandler', () => { title: 'User Experience', appLink: '/ux', coreWebVitals: { - cls: '0.01', + cls: 0.01, fid: 5, lcp: 1464.3333333333333, tbt: 232.92166666666665, diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts index 7e970493eb611d..50c261d2d37ad6 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts @@ -49,7 +49,7 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) expectSnapshot(response.body).toMatchInline(` Object { - "cls": "0.000", + "cls": 0, "clsRanks": Array [ 100, 0, From a5cfc7fb4a7ce0363b73a118299e228e925814df Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 23 Dec 2020 21:13:03 +0100 Subject: [PATCH 083/100] [Lens] Add percentile function (#86490) --- .../dimension_panel/dimension_panel.test.tsx | 2 + .../indexpattern.test.ts | 47 ++++ .../calculations/moving_average.tsx | 17 +- .../definitions/date_histogram.test.tsx | 6 +- .../definitions/filters/filters.test.tsx | 3 +- .../operations/definitions/helpers.tsx | 28 ++- .../operations/definitions/index.ts | 25 +- .../definitions/last_value.test.tsx | 3 +- .../operations/definitions/metrics.tsx | 29 ++- .../definitions/percentile.test.tsx | 237 ++++++++++++++++++ .../operations/definitions/percentile.tsx | 189 ++++++++++++++ .../definitions/ranges/advanced_editor.tsx | 4 +- .../definitions/ranges/ranges.test.tsx | 12 +- .../operations/definitions/ranges/ranges.tsx | 10 +- .../operations/definitions/terms/index.tsx | 15 +- .../definitions/terms/terms.test.tsx | 45 +++- .../operations/operations.test.ts | 5 + .../indexpattern_datasource/to_expression.ts | 10 +- 18 files changed, 631 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 95a6c351e1fc23..5d477d98d042d5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -1329,8 +1329,10 @@ describe('IndexPatternDimensionEditorPanel', () => { 'Median', 'Minimum', 'Moving average', + 'Percentile', 'Sum', 'Unique count', + '\u00a0', ]); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts index 7e67d863346c79..1f23fd38304771 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.test.ts @@ -474,6 +474,53 @@ describe('IndexPattern Data Source', () => { expect(ast.chain[0].arguments.timeFields).toEqual(['timestamp', 'another_datefield']); }); + it('should add the suffix to the remap column id if provided by the operation', async () => { + const queryBaseState: IndexPatternBaseState = { + currentIndexPatternId: '1', + layers: { + first: { + indexPatternId: '1', + columnOrder: ['def', 'abc'], + columns: { + abc: { + label: '23rd percentile', + dataType: 'number', + isBucketed: false, + sourceField: 'bytes', + operationType: 'percentile', + params: { + percentile: 23, + }, + }, + def: { + label: 'Terms', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + sourceField: 'source', + params: { + size: 5, + orderBy: { + type: 'alphabetical', + }, + orderDirection: 'asc', + }, + }, + }, + }, + }, + }; + + const state = enrichBaseState(queryBaseState); + + const ast = indexPatternDatasource.toExpression(state, 'first') as Ast; + expect(Object.keys(JSON.parse(ast.chain[1].arguments.idMap[0] as string))).toEqual([ + 'col-0-def', + // col-1 is the auto naming of esasggs, abc is the specified column id, .23 is the generated suffix + 'col-1-abc.23', + ]); + }); + it('should add time_scale and format function if time scale is set and supported', async () => { const queryBaseState: IndexPatternBaseState = { currentIndexPatternId: '1', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index e1bc378635f1d6..d9805b337c000f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -19,7 +19,7 @@ import { hasDateField, } from './utils'; import { updateColumnParam } from '../../layer_helpers'; -import { useDebounceWithOptions } from '../helpers'; +import { isValidNumber, useDebounceWithOptions } from '../helpers'; import { adjustTimeScaleOnOtherColumnChange } from '../../time_scale_utils'; import type { OperationDefinition, ParamEditorProps } from '..'; @@ -122,19 +122,6 @@ export const movingAverageOperation: OperationDefinition< timeScalingMode: 'optional', }; -function isValidNumber(input: string) { - if (input === '') return false; - try { - const val = parseFloat(input); - if (isNaN(val)) return false; - if (val < 1) return false; - if (val.toString().includes('.')) return false; - } catch (e) { - return false; - } - return true; -} - function MovingAverageParamEditor({ layer, updateLayer, @@ -145,7 +132,7 @@ function MovingAverageParamEditor({ useDebounceWithOptions( () => { - if (!isValidNumber(inputValue)) return; + if (!isValidNumber(inputValue, true, undefined, 1)) return; const inputNumber = parseInt(inputValue, 10); updateLayer( updateColumnParam({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx index 8c2fa43b541d4f..abd033c0db4cfc 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.test.tsx @@ -199,7 +199,8 @@ describe('date_histogram', () => { const esAggsFn = dateHistogramOperation.toEsAggsFn( layer.columns.col1 as DateHistogramIndexPatternColumn, 'col1', - indexPattern1 + indexPattern1, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -250,7 +251,8 @@ describe('date_histogram', () => { }, }, ]), - } + }, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx index cf57c35f6f68bf..86767fbc8b4691 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.test.tsx @@ -83,7 +83,8 @@ describe('filters', () => { const esAggsFn = filtersOperation.toEsAggsFn( layer.columns.col1 as FiltersIndexPatternColumn, 'col1', - createMockedIndexPattern() + createMockedIndexPattern(), + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx index 7b96bcf4f20698..29148052cee8e3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/helpers.tsx @@ -7,7 +7,7 @@ import { useRef } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; import { i18n } from '@kbn/i18n'; -import { operationDefinitionMap } from '.'; +import { IndexPatternColumn, operationDefinitionMap } from '.'; import { FieldBasedIndexPatternColumn } from './column_types'; import { IndexPattern } from '../../types'; @@ -63,6 +63,13 @@ export function getInvalidFieldMessage( : undefined; } +export function getEsAggsSuffix(column: IndexPatternColumn) { + const operationDefinition = operationDefinitionMap[column.operationType]; + return operationDefinition.input === 'field' && operationDefinition.getEsAggsSuffix + ? operationDefinition.getEsAggsSuffix(column) + : ''; +} + export function getSafeName(name: string, indexPattern: IndexPattern): string { const field = indexPattern.getFieldByName(name); return field @@ -71,3 +78,22 @@ export function getSafeName(name: string, indexPattern: IndexPattern): string { defaultMessage: 'Missing field', }); } + +export function isValidNumber( + inputValue: string | number | null | undefined, + integer?: boolean, + upperBound?: number, + lowerBound?: number +) { + const inputValueAsNumber = Number(inputValue); + return ( + inputValue !== '' && + inputValue !== null && + inputValue !== undefined && + !Number.isNaN(inputValueAsNumber) && + Number.isFinite(inputValueAsNumber) && + (!integer || Number.isInteger(inputValueAsNumber)) && + (upperBound === undefined || inputValueAsNumber <= upperBound) && + (lowerBound === undefined || inputValueAsNumber >= lowerBound) + ); +} 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 6231460347de26..36c9cf75d2b6cf 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 @@ -9,6 +9,7 @@ import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { termsOperation, TermsIndexPatternColumn } from './terms'; import { filtersOperation, FiltersIndexPatternColumn } from './filters'; import { cardinalityOperation, CardinalityIndexPatternColumn } from './cardinality'; +import { percentileOperation, PercentileIndexPatternColumn } from './percentile'; import { minOperation, MinIndexPatternColumn, @@ -58,6 +59,7 @@ export type IndexPatternColumn = | CardinalityIndexPatternColumn | SumIndexPatternColumn | MedianIndexPatternColumn + | PercentileIndexPatternColumn | CountIndexPatternColumn | LastValueIndexPatternColumn | CumulativeSumIndexPatternColumn @@ -82,6 +84,7 @@ const internalOperationDefinitions = [ cardinalityOperation, sumOperation, medianOperation, + percentileOperation, lastValueOperation, countOperation, rangeOperation, @@ -96,6 +99,7 @@ export { rangeOperation } from './ranges'; export { filtersOperation } from './filters'; export { dateHistogramOperation } from './date_histogram'; export { minOperation, averageOperation, sumOperation, maxOperation } from './metrics'; +export { percentileOperation } from './percentile'; export { countOperation } from './count'; export { lastValueOperation } from './last_value'; export { @@ -223,7 +227,12 @@ interface FieldlessOperationDefinition { * Function turning a column into an agg config passed to the `esaggs` function * together with the agg configs returned from other columns. */ - toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction; + toEsAggsFn: ( + column: C, + columnId: string, + indexPattern: IndexPattern, + layer: IndexPatternLayer + ) => ExpressionAstFunction; } interface FieldBasedOperationDefinition { @@ -262,7 +271,19 @@ interface FieldBasedOperationDefinition { * Function turning a column into an agg config passed to the `esaggs` function * together with the agg configs returned from other columns. */ - toEsAggsFn: (column: C, columnId: string, indexPattern: IndexPattern) => ExpressionAstFunction; + toEsAggsFn: ( + column: C, + columnId: string, + indexPattern: IndexPattern, + layer: IndexPatternLayer + ) => ExpressionAstFunction; + /** + * Optional function to return the suffix used for ES bucket paths and esaggs column id. + * This is relevant for multi metrics to pick the right value. + * + * @param column The current column + */ + getEsAggsSuffix?: (column: C) => string; /** * Validate that the operation has the right preconditions in the state. For example: * diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index bde8cd0e424277..96b12a714e613d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -69,7 +69,8 @@ describe('last_value', () => { const esAggsFn = lastValueOperation.toEsAggsFn( { ...lastValueColumn, params: { ...lastValueColumn.params } }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx index eb25b5d932b1f8..470a5407b2589a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx @@ -83,21 +83,24 @@ function buildMetricOperation>({ }, onOtherColumnChanged: (layer, thisColumnId, changedColumnId) => optionalTimeScaling - ? adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) - : layer.columns[thisColumnId], + ? (adjustTimeScaleOnOtherColumnChange(layer, thisColumnId, changedColumnId) as T) + : (layer.columns[thisColumnId] as T), getDefaultLabel: (column, indexPattern, columns) => labelLookup(getSafeName(column.sourceField, indexPattern), column), - buildColumn: ({ field, previousColumn }) => ({ - label: labelLookup(field.displayName, previousColumn), - dataType: 'number', - operationType: type, - sourceField: field.name, - isBucketed: false, - scale: 'ratio', - timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined, - params: - previousColumn && previousColumn.dataType === 'number' ? previousColumn.params : undefined, - }), + buildColumn: ({ field, previousColumn }) => + ({ + label: labelLookup(field.displayName, previousColumn), + dataType: 'number', + operationType: type, + sourceField: field.name, + isBucketed: false, + scale: 'ratio', + timeScale: optionalTimeScaling ? previousColumn?.timeScale : undefined, + params: + previousColumn && previousColumn.dataType === 'number' + ? previousColumn.params + : undefined, + } as T), onFieldChange: (oldColumn, field) => { return { ...oldColumn, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx new file mode 100644 index 00000000000000..c22eec62ea1ab9 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.test.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; +import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; +import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; +import { createMockedIndexPattern } from '../../mocks'; +import { percentileOperation } from './index'; +import { IndexPattern, IndexPatternLayer } from '../../types'; +import { PercentileIndexPatternColumn } from './percentile'; +import { EuiFieldNumber } from '@elastic/eui'; +import { act } from 'react-dom/test-utils'; +import { EuiFormRow } from '@elastic/eui'; + +const defaultProps = { + storage: {} as IStorageWrapper, + uiSettings: {} as IUiSettingsClient, + savedObjectsClient: {} as SavedObjectsClientContract, + dateRange: { fromDate: 'now-1d', toDate: 'now' }, + data: dataPluginMock.createStartContract(), + http: {} as HttpSetup, + indexPattern: { + ...createMockedIndexPattern(), + hasRestrictions: false, + } as IndexPattern, +}; + +describe('percentile', () => { + let layer: IndexPatternLayer; + const InlineOptions = percentileOperation.paramEditor!; + + beforeEach(() => { + layer = { + indexPatternId: '1', + columnOrder: ['col1', 'col2'], + columns: { + col1: { + label: 'Top value of category', + dataType: 'string', + isBucketed: true, + operationType: 'terms', + params: { + orderBy: { type: 'alphabetical' }, + size: 3, + orderDirection: 'asc', + }, + sourceField: 'category', + }, + col2: { + label: '23rd percentile of a', + dataType: 'number', + isBucketed: false, + sourceField: 'a', + operationType: 'percentile', + params: { + percentile: 23, + }, + }, + }, + }; + }); + + describe('toEsAggsFn', () => { + it('should reflect params correctly', () => { + const percentileColumn = layer.columns.col2 as PercentileIndexPatternColumn; + const esAggsFn = percentileOperation.toEsAggsFn( + percentileColumn, + 'col1', + {} as IndexPattern, + layer + ); + expect(esAggsFn).toEqual( + expect.objectContaining({ + arguments: expect.objectContaining({ + percents: [23], + field: ['a'], + }), + }) + ); + }); + }); + + describe('onFieldChange', () => { + it('should change correctly to new field', () => { + const oldColumn: PercentileIndexPatternColumn = { + operationType: 'percentile', + sourceField: 'bytes', + label: '23rd percentile of bytes', + isBucketed: true, + dataType: 'number', + params: { + percentile: 23, + }, + }; + const indexPattern = createMockedIndexPattern(); + const newNumberField = indexPattern.getFieldByName('memory')!; + const column = percentileOperation.onFieldChange(oldColumn, newNumberField); + + expect(column).toEqual( + expect.objectContaining({ + dataType: 'number', + sourceField: 'memory', + params: expect.objectContaining({ + percentile: 23, + }), + }) + ); + expect(column.label).toContain('memory'); + }); + }); + + describe('buildColumn', () => { + it('should set default percentile', () => { + const indexPattern = createMockedIndexPattern(); + const bytesField = indexPattern.fields.find(({ name }) => name === 'bytes')!; + bytesField.displayName = 'test'; + const percentileColumn = percentileOperation.buildColumn({ + indexPattern, + field: bytesField, + layer: { columns: {}, columnOrder: [], indexPatternId: '' }, + }); + expect(percentileColumn.dataType).toEqual('number'); + expect(percentileColumn.params.percentile).toEqual(95); + expect(percentileColumn.label).toEqual('95th percentile of test'); + }); + }); + + describe('param editor', () => { + it('should render current percentile', () => { + const updateLayerSpy = jest.fn(); + const instance = shallow( + + ); + + const input = instance.find('[data-test-subj="lns-indexPattern-percentile-input"]'); + + expect(input.prop('value')).toEqual('23'); + }); + + it('should update state on change', async () => { + jest.useFakeTimers(); + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + jest.runAllTimers(); + + const input = instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ target: { value: '27' } } as React.ChangeEvent); + }); + + instance.update(); + + jest.runAllTimers(); + + expect(updateLayerSpy).toHaveBeenCalledWith({ + ...layer, + columns: { + ...layer.columns, + col2: { + ...layer.columns.col2, + params: { + percentile: 27, + }, + label: '27th percentile of a', + }, + }, + }); + }); + + it('should not update on invalid input, but show invalid value locally', async () => { + const updateLayerSpy = jest.fn(); + const instance = mount( + + ); + + jest.runAllTimers(); + + const input = instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber); + + await act(async () => { + input.prop('onChange')!({ + target: { value: '12.12' }, + } as React.ChangeEvent); + }); + + instance.update(); + + jest.runAllTimers(); + + expect(updateLayerSpy).not.toHaveBeenCalled(); + + expect( + instance + .find('[data-test-subj="lns-indexPattern-percentile-form"]') + .find(EuiFormRow) + .prop('isInvalid') + ).toEqual(true); + expect( + instance + .find('[data-test-subj="lns-indexPattern-percentile-input"]') + .find(EuiFieldNumber) + .prop('value') + ).toEqual('12.12'); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx new file mode 100644 index 00000000000000..b381a0ecb664a4 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/percentile.tsx @@ -0,0 +1,189 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; +import React, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { AggFunctionsMapping } from 'src/plugins/data/public'; +import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; +import { OperationDefinition } from './index'; +import { + getInvalidFieldMessage, + getSafeName, + isValidNumber, + useDebounceWithOptions, +} from './helpers'; +import { FieldBasedIndexPatternColumn } from './column_types'; + +export interface PercentileIndexPatternColumn extends FieldBasedIndexPatternColumn { + operationType: 'percentile'; + params: { + percentile: number; + format?: { + id: string; + params?: { + decimals: number; + }; + }; + }; +} + +function ofName(name: string, percentile: number) { + return i18n.translate('xpack.lens.indexPattern.percentileOf', { + defaultMessage: + '{percentile, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} percentile of {name}', + values: { name, percentile }, + }); +} + +const DEFAULT_PERCENTILE_VALUE = 95; + +export const percentileOperation: OperationDefinition = { + type: 'percentile', + displayName: i18n.translate('xpack.lens.indexPattern.percentile', { + defaultMessage: 'Percentile', + }), + input: 'field', + getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type: fieldType }) => { + if (fieldType === 'number' && aggregatable && !aggregationRestrictions) { + return { + dataType: 'number', + isBucketed: false, + scale: 'ratio', + }; + } + }, + isTransferable: (column, newIndexPattern) => { + const newField = newIndexPattern.getFieldByName(column.sourceField); + + return Boolean( + newField && + newField.type === 'number' && + newField.aggregatable && + !newField.aggregationRestrictions + ); + }, + getDefaultLabel: (column, indexPattern, columns) => + ofName(getSafeName(column.sourceField, indexPattern), column.params.percentile), + buildColumn: ({ field, previousColumn, indexPattern }) => { + const existingFormat = + previousColumn?.params && 'format' in previousColumn?.params + ? previousColumn?.params?.format + : undefined; + const existingPercentileParam = + previousColumn?.operationType === 'percentile' && previousColumn?.params.percentile; + const newPercentileParam = existingPercentileParam || DEFAULT_PERCENTILE_VALUE; + return { + label: ofName(getSafeName(field.name, indexPattern), newPercentileParam), + dataType: 'number', + operationType: 'percentile', + sourceField: field.name, + isBucketed: false, + scale: 'ratio', + params: { + format: existingFormat, + percentile: newPercentileParam, + }, + }; + }, + onFieldChange: (oldColumn, field) => { + return { + ...oldColumn, + label: ofName(field.displayName, oldColumn.params.percentile), + sourceField: field.name, + }; + }, + toEsAggsFn: (column, columnId, _indexPattern) => { + return buildExpressionFunction('aggPercentiles', { + id: columnId, + enabled: true, + schema: 'metric', + field: column.sourceField, + percents: [column.params.percentile], + }).toAst(); + }, + getEsAggsSuffix: (column) => { + const value = column.params.percentile; + return `.${value}`; + }, + getErrorMessage: (layer, columnId, indexPattern) => + getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), + paramEditor: function PercentileParamEditor({ + layer, + updateLayer, + currentColumn, + columnId, + indexPattern, + }) { + const [inputValue, setInputValue] = useState(String(currentColumn.params.percentile)); + + const inputValueAsNumber = Number(inputValue); + // an input is value if it's not an empty string, parses to a valid number, is between 0 and 100 (exclusive) + // and is an integer + const inputValueIsValid = isValidNumber(inputValue, true, 99, 1); + + useDebounceWithOptions( + () => { + if (!inputValueIsValid) return; + updateLayer({ + ...layer, + columns: { + ...layer.columns, + [columnId]: { + ...currentColumn, + label: currentColumn.customLabel + ? currentColumn.label + : ofName( + indexPattern.getFieldByName(currentColumn.sourceField)?.displayName || + currentColumn.sourceField, + inputValueAsNumber + ), + params: { + ...currentColumn.params, + percentile: inputValueAsNumber, + }, + }, + }, + }); + }, + { skipFirstRender: true }, + 256, + [inputValue] + ); + + const handleInputChange = useCallback((e: React.ChangeEvent) => { + const val = String(e.target.value); + setInputValue(val); + }, []); + return ( + + + + ); + }, +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 9ab677bf68f62e..420846f7fc8018 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -22,7 +22,7 @@ import { keys, } from '@elastic/eui'; import { IFieldFormat } from '../../../../../../../../src/plugins/data/common'; -import { RangeTypeLens, isValidRange, isValidNumber } from './ranges'; +import { RangeTypeLens, isValidRange } from './ranges'; import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; import { NewBucketButton, @@ -30,7 +30,7 @@ import { DraggableBucketContainer, LabelInput, } from '../shared_components'; -import { useDebounceWithOptions } from '../helpers'; +import { isValidNumber, useDebounceWithOptions } from '../helpers'; const generateId = htmlIdGenerator(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index c2c52985c6cd27..987c8971aa3109 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -142,7 +142,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toMatchInlineSnapshot(` Object { @@ -184,7 +185,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( @@ -203,7 +205,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( @@ -222,7 +225,8 @@ describe('ranges', () => { const esAggsFn = rangeOperation.toEsAggsFn( layer.columns.col1 as RangeIndexPatternColumn, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect((esAggsFn as { arguments: unknown }).arguments).toEqual( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index b687e6fe3da504..aa5cc8255a5841 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -19,7 +19,7 @@ import { updateColumnParam } from '../../layer_helpers'; import { supportedFormats } from '../../../format_column'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; -import { getInvalidFieldMessage } from '../helpers'; +import { getInvalidFieldMessage, isValidNumber } from '../helpers'; type RangeType = Omit; // Try to cover all possible serialized states for ranges @@ -52,10 +52,6 @@ export type UpdateParamsFnType = ( value: RangeColumnParams[K] ) => void; -// on initialization values can be null (from the Infinity serialization), so handle it correctly -// or they will be casted to 0 by the editor ( see #78867 ) -export const isValidNumber = (value: number | '' | null): value is number => - value != null && value !== '' && !isNaN(value) && isFinite(value); export const isRangeWithin = (range: RangeType): boolean => range.from <= range.to; const isFullRange = (range: RangeTypeLens): range is FullRangeTypeLens => isValidNumber(range.from) && isValidNumber(range.to); @@ -152,10 +148,10 @@ export const rangeOperation: OperationDefinition = { label: range.label }; // be careful with the fields to set on partial ranges if (isValidNumber(range.from)) { - partialRange.from = range.from; + partialRange.from = Number(range.from); } if (isValidNumber(range.to)) { - partialRange.to = range.to; + partialRange.to = Number(range.to); } return partialRange; }) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index 8462d374c6e6b8..625084000fa939 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -23,7 +23,7 @@ import { DataType } from '../../../../types'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { ValuesRangeInput } from './values_range_input'; -import { getInvalidFieldMessage } from '../helpers'; +import { getEsAggsSuffix, getInvalidFieldMessage } from '../helpers'; import type { IndexPatternLayer } from '../../../types'; function ofName(name?: string) { @@ -119,7 +119,10 @@ export const termsOperation: OperationDefinition { + toEsAggsFn: (column, columnId, _indexPattern, layer) => { return buildExpressionFunction('aggTerms', { id: columnId, enabled: true, schema: 'segment', field: column.sourceField, orderBy: - column.params.orderBy.type === 'alphabetical' ? '_key' : column.params.orderBy.columnId, + column.params.orderBy.type === 'alphabetical' + ? '_key' + : `${column.params.orderBy.columnId}${getEsAggsSuffix( + layer.columns[column.params.orderBy.columnId] + )}`, order: column.params.orderDirection, size: column.params.size, otherBucket: Boolean(column.params.otherBucket), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx index 0209c0b9a448b7..d60992bda2e2a7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/terms.test.tsx @@ -65,7 +65,8 @@ describe('terms', () => { const esAggsFn = termsOperation.toEsAggsFn( { ...termsColumn, params: { ...termsColumn.params, otherBucket: true } }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -87,7 +88,8 @@ describe('terms', () => { params: { ...termsColumn.params, otherBucket: false, missingBucket: true }, }, 'col1', - {} as IndexPattern + {} as IndexPattern, + layer ); expect(esAggsFn).toEqual( expect.objectContaining({ @@ -98,6 +100,45 @@ describe('terms', () => { }) ); }); + + it('should include esaggs suffix from other columns in orderby argument', () => { + const termsColumn = layer.columns.col1 as TermsIndexPatternColumn; + const esAggsFn = termsOperation.toEsAggsFn( + { + ...termsColumn, + params: { + ...termsColumn.params, + otherBucket: true, + orderBy: { type: 'column', columnId: 'abcde' }, + }, + }, + 'col1', + {} as IndexPattern, + { + ...layer, + columns: { + ...layer.columns, + abcde: { + dataType: 'number', + isBucketed: false, + operationType: 'percentile', + sourceField: 'abc', + label: '', + params: { + percentile: 12, + }, + }, + }, + } + ); + expect(esAggsFn).toEqual( + expect.objectContaining({ + arguments: expect.objectContaining({ + orderBy: ['abcde.12'], + }), + }) + ); + }); }); describe('onFieldChange', () => { 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 9f2b8eab4e09ba..882252132c5b35 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 @@ -293,6 +293,11 @@ describe('getOperationTypesForField', () => { "operationType": "median", "type": "field", }, + Object { + "field": "bytes", + "operationType": "percentile", + "type": "field", + }, Object { "field": "bytes", "operationType": "last_value", diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index a5ce4dfbea371f..38f51f24aae7dd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -20,6 +20,7 @@ import { operationDefinitionMap } from './operations'; import { IndexPattern, IndexPatternPrivateState, IndexPatternLayer } from './types'; import { OriginalColumn } from './rename_columns'; import { dateHistogramOperation } from './operations/definitions'; +import { getEsAggsSuffix } from './operations/definitions/helpers'; function getExpressionForLayer( layer: IndexPatternLayer, @@ -41,15 +42,20 @@ function getExpressionForLayer( expressions.push(...def.toExpression(layer, colId, indexPattern)); } else { aggs.push( - buildExpression({ type: 'expression', chain: [def.toEsAggsFn(col, colId, indexPattern)] }) + buildExpression({ + type: 'expression', + chain: [def.toEsAggsFn(col, colId, indexPattern, layer)], + }) ); } }); const idMap = columnEntries.reduce((currentIdMap, [colId, column], index) => { + const esAggsId = `col-${columnEntries.length === 1 ? 0 : index}-${colId}`; + const suffix = getEsAggsSuffix(column); return { ...currentIdMap, - [`col-${columnEntries.length === 1 ? 0 : index}-${colId}`]: { + [`${esAggsId}${suffix}`]: { ...column, id: colId, }, From 0ffb9e72edfcfa71fe045a639ad7660f49e0dac9 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Wed, 23 Dec 2020 19:25:21 -0500 Subject: [PATCH 084/100] [Security Solution][Exceptions][Tech Debt] - Refactor exceptions api file to follow value lists pattern (#86903) ## Summary Currently working on issues related to exceptions and it was noted on a separate PR that the request payload validation being done in the client side API calls was unnecessary. It was helpful in development, but not of any added value in production. Not only that, but the extra validations also add to the performance hit. Removed the payload validation and formatted the code to follow the same pattern as that in the value lists api file. Tested that exceptions flows not affected by testing out exceptions CRUD flows. --- .../lists/public/exceptions/api.test.ts | 166 +---- x-pack/plugins/lists/public/exceptions/api.ts | 600 +++++++++--------- 2 files changed, 312 insertions(+), 454 deletions(-) diff --git a/x-pack/plugins/lists/public/exceptions/api.test.ts b/x-pack/plugins/lists/public/exceptions/api.test.ts index 7570e1f050abba..96d5fbf010f42e 100644 --- a/x-pack/plugins/lists/public/exceptions/api.test.ts +++ b/x-pack/plugins/lists/public/exceptions/api.test.ts @@ -11,12 +11,6 @@ import { getCreateExceptionListItemSchemaMock } from '../../common/schemas/reque import { getFoundExceptionListItemSchemaMock } from '../../common/schemas/response/found_exception_list_item_schema.mock'; import { getUpdateExceptionListItemSchemaMock } from '../../common/schemas/request/update_exception_list_item_schema.mock'; import { getUpdateExceptionListSchemaMock } from '../../common/schemas/request/update_exception_list_schema.mock'; -import { - CreateExceptionListItemSchema, - CreateExceptionListSchema, - ExceptionListItemSchema, - ExceptionListSchema, -} from '../../common/schemas'; import { getFoundExceptionListSchemaMock } from '../../common/schemas/response/found_exception_list_schema.mock'; import { @@ -33,7 +27,6 @@ import { updateExceptionList, updateExceptionListItem, } from './api'; -import { ApiCallByIdProps, ApiCallByListIdProps, ApiCallFetchExceptionListsProps } from './types'; const abortCtrl = new AbortController(); @@ -75,20 +68,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload: Omit & { - description?: string[]; - } = { ...getCreateExceptionListSchemaMock(), description: ['123'] }; - - await expect( - addExceptionList({ - http: httpMock, - list: (payload as unknown) as ExceptionListSchema, - signal: abortCtrl.signal, - }) - ).rejects.toEqual('Invalid value "["123"]" supplied to "description"'); - }); - test('it returns error if response payload fails decode', async () => { const payload = getCreateExceptionListSchemaMock(); const badPayload = getExceptionListSchemaMock(); @@ -102,7 +81,7 @@ describe('Exceptions Lists API', () => { list: payload, signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -137,20 +116,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload: Omit & { - description?: string[]; - } = { ...getCreateExceptionListItemSchemaMock(), description: ['123'] }; - - await expect( - addExceptionListItem({ - http: httpMock, - listItem: (payload as unknown) as ExceptionListItemSchema, - signal: abortCtrl.signal, - }) - ).rejects.toEqual('Invalid value "["123"]" supplied to "description"'); - }); - test('it returns error if response payload fails decode', async () => { const payload = getCreateExceptionListItemSchemaMock(); const badPayload = getExceptionListItemSchemaMock(); @@ -164,7 +129,7 @@ describe('Exceptions Lists API', () => { listItem: payload, signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -199,20 +164,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = getUpdateExceptionListSchemaMock(); - // @ts-expect-error - delete payload.description; - - await expect( - updateExceptionList({ - http: httpMock, - list: payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual('Invalid value "undefined" supplied to "description"'); - }); - test('it returns error if response payload fails decode', async () => { const payload = getUpdateExceptionListSchemaMock(); const badPayload = getExceptionListSchemaMock(); @@ -226,7 +177,7 @@ describe('Exceptions Lists API', () => { list: payload, signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -261,20 +212,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = getUpdateExceptionListItemSchemaMock(); - // @ts-expect-error - delete payload.description; - - await expect( - updateExceptionListItem({ - http: httpMock, - listItem: payload, - signal: abortCtrl.signal, - }) - ).rejects.toEqual('Invalid value "undefined" supplied to "description"'); - }); - test('it returns error if response payload fails decode', async () => { const payload = getUpdateExceptionListItemSchemaMock(); const badPayload = getExceptionListItemSchemaMock(); @@ -288,7 +225,7 @@ describe('Exceptions Lists API', () => { listItem: payload, signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -336,22 +273,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse.data).toEqual([getExceptionListSchemaMock()]); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - filters: 'exception-list.attributes.name: Sample Endpoint', - http: httpMock, - namespaceTypes: 'notANamespaceType', - pagination: { - page: 1, - perPage: 20, - }, - signal: abortCtrl.signal, - } as unknown) as ApiCallFetchExceptionListsProps & { namespaceTypes: string[] }; - await expect(fetchExceptionLists(payload)).rejects.toEqual( - 'Invalid value "notANamespaceType" supplied to "namespace_type"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListSchemaMock(); // @ts-expect-error @@ -369,7 +290,7 @@ describe('Exceptions Lists API', () => { }, signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "data,id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "data,id"')); }); }); @@ -405,18 +326,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - http: httpMock, - id: 1, - namespaceType: 'single', - signal: abortCtrl.signal, - } as unknown) as ApiCallByIdProps & { id: number }; - await expect(fetchExceptionListById(payload)).rejects.toEqual( - 'Invalid value "1" supplied to "id"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListSchemaMock(); // @ts-expect-error @@ -430,7 +339,7 @@ describe('Exceptions Lists API', () => { namespaceType: 'single', signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -614,23 +523,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getFoundExceptionListItemSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - filterOptions: [], - http: httpMock, - listIds: ['myList'], - namespaceTypes: ['not a namespace type'], - pagination: { - page: 1, - perPage: 20, - }, - signal: abortCtrl.signal, - } as unknown) as ApiCallByListIdProps & { listId: number }; - await expect(fetchExceptionListsItemsByListIds(payload)).rejects.toEqual( - 'Invalid value "not a namespace type" supplied to "namespace_type"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListItemSchemaMock(); // @ts-expect-error @@ -650,7 +542,9 @@ describe('Exceptions Lists API', () => { signal: abortCtrl.signal, }) ).rejects.toEqual( - 'Invalid value "undefined" supplied to "data",Invalid value "undefined" supplied to "page",Invalid value "undefined" supplied to "per_page",Invalid value "undefined" supplied to "total"' + new Error( + 'Invalid value "undefined" supplied to "data",Invalid value "undefined" supplied to "page",Invalid value "undefined" supplied to "per_page",Invalid value "undefined" supplied to "total"' + ) ); }); }); @@ -687,18 +581,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - http: httpMock, - id: '1', - namespaceType: 'not a namespace type', - signal: abortCtrl.signal, - } as unknown) as ApiCallByIdProps & { namespaceType: string }; - await expect(fetchExceptionListItemById(payload)).rejects.toEqual( - 'Invalid value "not a namespace type" supplied to "namespace_type"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListItemSchemaMock(); // @ts-expect-error @@ -712,7 +594,7 @@ describe('Exceptions Lists API', () => { namespaceType: 'single', signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -748,18 +630,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - http: httpMock, - id: 1, - namespaceType: 'single', - signal: abortCtrl.signal, - } as unknown) as ApiCallByIdProps & { id: number }; - await expect(deleteExceptionListById(payload)).rejects.toEqual( - 'Invalid value "1" supplied to "id"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListSchemaMock(); // @ts-expect-error @@ -773,7 +643,7 @@ describe('Exceptions Lists API', () => { namespaceType: 'single', signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); @@ -809,18 +679,6 @@ describe('Exceptions Lists API', () => { expect(exceptionResponse).toEqual(getExceptionListItemSchemaMock()); }); - test('it returns error and does not make request if request payload fails decode', async () => { - const payload = ({ - http: httpMock, - id: 1, - namespaceType: 'single', - signal: abortCtrl.signal, - } as unknown) as ApiCallByIdProps & { id: number }; - await expect(deleteExceptionListItemById(payload)).rejects.toEqual( - 'Invalid value "1" supplied to "id"' - ); - }); - test('it returns error if response payload fails decode', async () => { const badPayload = getExceptionListItemSchemaMock(); // @ts-expect-error @@ -834,7 +692,7 @@ describe('Exceptions Lists API', () => { namespaceType: 'single', signal: abortCtrl.signal, }) - ).rejects.toEqual('Invalid value "undefined" supplied to "id"'); + ).rejects.toEqual(new Error('Invalid value "undefined" supplied to "id"')); }); }); diff --git a/x-pack/plugins/lists/public/exceptions/api.ts b/x-pack/plugins/lists/public/exceptions/api.ts index f7032c22cb6c26..8fcd1af524f6dd 100644 --- a/x-pack/plugins/lists/public/exceptions/api.ts +++ b/x-pack/plugins/lists/public/exceptions/api.ts @@ -3,6 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { chain, fromEither, tryCatch } from 'fp-ts/lib/TaskEither'; +import { flow } from 'fp-ts/lib/function'; + +import { validateEither } from '../../common/shared_imports'; +import { toError, toPromise } from '../common/fp_utils'; import { ENDPOINT_LIST_URL, EXCEPTION_LIST_ITEM_URL, @@ -17,22 +22,11 @@ import { FoundExceptionListItemSchema, FoundExceptionListSchema, createEndpointListSchema, - createExceptionListItemSchema, - createExceptionListSchema, - deleteExceptionListItemSchema, - deleteExceptionListSchema, exceptionListItemSchema, exceptionListSchema, - findExceptionListItemSchema, - findExceptionListSchema, foundExceptionListItemSchema, foundExceptionListSchema, - readExceptionListItemSchema, - readExceptionListSchema, - updateExceptionListItemSchema, - updateExceptionListSchema, } from '../../common/schemas'; -import { validate } from '../../common/shared_imports'; import { AddEndpointExceptionListProps, @@ -56,35 +50,38 @@ import { * @throws An error if response is not OK * */ -export const addExceptionList = async ({ +const addExceptionList = async ({ http, list, signal, -}: AddExceptionListProps): Promise => { - const [validatedRequest, errorsRequest] = validate(list, createExceptionListSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_URL, { - body: JSON.stringify(list), - method: 'POST', - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: AddExceptionListProps): Promise => + http.fetch(EXCEPTION_LIST_URL, { + body: JSON.stringify(list), + method: 'POST', + signal, + }); + +const addExceptionListWithValidation = async ({ + http, + list, + signal, +}: AddExceptionListProps): Promise => + flow( + () => + tryCatch( + () => + addExceptionList({ + http, + list, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { addExceptionListWithValidation as addExceptionList }; /** * Add new ExceptionListItem @@ -96,35 +93,38 @@ export const addExceptionList = async ({ * @throws An error if response is not OK * */ -export const addExceptionListItem = async ({ +const addExceptionListItem = async ({ http, listItem, signal, -}: AddExceptionListItemProps): Promise => { - const [validatedRequest, errorsRequest] = validate(listItem, createExceptionListItemSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify(listItem), - method: 'POST', - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListItemSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: AddExceptionListItemProps): Promise => + http.fetch(EXCEPTION_LIST_ITEM_URL, { + body: JSON.stringify(listItem), + method: 'POST', + signal, + }); + +const addExceptionListItemWithValidation = async ({ + http, + listItem, + signal, +}: AddExceptionListItemProps): Promise => + flow( + () => + tryCatch( + () => + addExceptionListItem({ + http, + listItem, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { addExceptionListItemWithValidation as addExceptionListItem }; /** * Update existing ExceptionList @@ -136,35 +136,38 @@ export const addExceptionListItem = async ({ * @throws An error if response is not OK * */ -export const updateExceptionList = async ({ +const updateExceptionList = async ({ http, list, signal, -}: UpdateExceptionListProps): Promise => { - const [validatedRequest, errorsRequest] = validate(list, updateExceptionListSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_URL, { - body: JSON.stringify(list), - method: 'PUT', - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: UpdateExceptionListProps): Promise => + http.fetch(EXCEPTION_LIST_URL, { + body: JSON.stringify(list), + method: 'PUT', + signal, + }); + +const updateExceptionListWithValidation = async ({ + http, + list, + signal, +}: UpdateExceptionListProps): Promise => + flow( + () => + tryCatch( + () => + updateExceptionList({ + http, + list, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { updateExceptionListWithValidation as updateExceptionList }; /** * Update existing ExceptionListItem @@ -176,35 +179,38 @@ export const updateExceptionList = async ({ * @throws An error if response is not OK * */ -export const updateExceptionListItem = async ({ +const updateExceptionListItem = async ({ http, listItem, signal, -}: UpdateExceptionListItemProps): Promise => { - const [validatedRequest, errorsRequest] = validate(listItem, updateExceptionListItemSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_ITEM_URL, { - body: JSON.stringify(listItem), - method: 'PUT', - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListItemSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: UpdateExceptionListItemProps): Promise => + http.fetch(EXCEPTION_LIST_ITEM_URL, { + body: JSON.stringify(listItem), + method: 'PUT', + signal, + }); + +const updateExceptionListItemWithValidation = async ({ + http, + listItem, + signal, +}: UpdateExceptionListItemProps): Promise => + flow( + () => + tryCatch( + () => + updateExceptionListItem({ + http, + listItem, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { updateExceptionListItemWithValidation as updateExceptionListItem }; /** * Fetch all ExceptionLists (optionally by namespaceType) @@ -217,7 +223,7 @@ export const updateExceptionListItem = async ({ * * @throws An error if request params or response is not OK */ -export const fetchExceptionLists = async ({ +const fetchExceptionLists = async ({ http, filters, namespaceTypes, @@ -233,31 +239,39 @@ export const fetchExceptionLists = async ({ sort_order: 'desc', }; - const [validatedRequest, errorsRequest] = validate(query, findExceptionListSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch(`${EXCEPTION_LIST_URL}/_find`, { - method: 'GET', - query, - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, foundExceptionListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } + return http.fetch(`${EXCEPTION_LIST_URL}/_find`, { + method: 'GET', + query, + signal, + }); }; +const fetchExceptionListsWithValidation = async ({ + filters, + http, + namespaceTypes, + pagination, + signal, +}: ApiCallFetchExceptionListsProps): Promise => + flow( + () => + tryCatch( + () => + fetchExceptionLists({ + filters, + http, + namespaceTypes, + pagination, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(foundExceptionListSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListsWithValidation as fetchExceptionLists }; + /** * Fetch an ExceptionList by providing a ExceptionList ID * @@ -268,39 +282,41 @@ export const fetchExceptionLists = async ({ * * @throws An error if response is not OK */ -export const fetchExceptionListById = async ({ +const fetchExceptionListById = async ({ http, id, namespaceType, signal, -}: ApiCallByIdProps): Promise => { - const [validatedRequest, errorsRequest] = validate( - { id, namespace_type: namespaceType }, - readExceptionListSchema - ); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_URL, { - method: 'GET', - query: { id, namespace_type: namespaceType }, - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: ApiCallByIdProps): Promise => + http.fetch(EXCEPTION_LIST_URL, { + method: 'GET', + query: { id, namespace_type: namespaceType }, + signal, + }); + +const fetchExceptionListByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise => + flow( + () => + tryCatch( + () => + fetchExceptionListById({ + http, + id, + namespaceType, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListByIdWithValidation as fetchExceptionListById }; /** * Fetch an ExceptionList's ExceptionItems by providing a ExceptionList list_id @@ -314,7 +330,7 @@ export const fetchExceptionListById = async ({ * * @throws An error if response is not OK */ -export const fetchExceptionListsItemsByListIds = async ({ +const fetchExceptionListsItemsByListIds = async ({ http, listIds, namespaceTypes, @@ -349,34 +365,42 @@ export const fetchExceptionListsItemsByListIds = async ({ sort_order: 'desc', ...(filters.trim() !== '' ? { filter: filters } : {}), }; - const [validatedRequest, errorsRequest] = validate(query, findExceptionListItemSchema); - - if (validatedRequest != null) { - try { - const response = await http.fetch( - `${EXCEPTION_LIST_ITEM_URL}/_find`, - { - method: 'GET', - query, - signal, - } - ); - - const [validatedResponse, errorsResponse] = validate(response, foundExceptionListItemSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } + + return http.fetch(`${EXCEPTION_LIST_ITEM_URL}/_find`, { + method: 'GET', + query, + signal, + }); }; +const fetchExceptionListsItemsByListIdsWithValidation = async ({ + filterOptions, + http, + listIds, + namespaceTypes, + pagination, + signal, +}: ApiCallByListIdProps): Promise => + flow( + () => + tryCatch( + () => + fetchExceptionListsItemsByListIds({ + filterOptions, + http, + listIds, + namespaceTypes, + pagination, + signal, + }), + toError + ), + chain((response) => fromEither(validateEither(foundExceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListsItemsByListIdsWithValidation as fetchExceptionListsItemsByListIds }; + /** * Fetch an ExceptionListItem by providing a ExceptionListItem ID * @@ -387,38 +411,31 @@ export const fetchExceptionListsItemsByListIds = async ({ * * @throws An error if response is not OK */ -export const fetchExceptionListItemById = async ({ +const fetchExceptionListItemById = async ({ http, id, namespaceType, signal, -}: ApiCallByIdProps): Promise => { - const [validatedRequest, errorsRequest] = validate( - { id, namespace_type: namespaceType }, - readExceptionListItemSchema - ); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_ITEM_URL, { - method: 'GET', - query: { id, namespace_type: namespaceType }, - signal, - }); - const [validatedResponse, errorsResponse] = validate(response, exceptionListItemSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: ApiCallByIdProps): Promise => + http.fetch(EXCEPTION_LIST_ITEM_URL, { + method: 'GET', + query: { id, namespace_type: namespaceType }, + signal, + }); + +const fetchExceptionListItemByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise => + flow( + () => tryCatch(() => fetchExceptionListItemById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { fetchExceptionListItemByIdWithValidation as fetchExceptionListItemById }; /** * Delete an ExceptionList by providing a ExceptionList ID @@ -430,39 +447,31 @@ export const fetchExceptionListItemById = async ({ * * @throws An error if response is not OK */ -export const deleteExceptionListById = async ({ +const deleteExceptionListById = async ({ http, id, namespaceType, signal, -}: ApiCallByIdProps): Promise => { - const [validatedRequest, errorsRequest] = validate( - { id, namespace_type: namespaceType }, - deleteExceptionListSchema - ); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_URL, { - method: 'DELETE', - query: { id, namespace_type: namespaceType }, - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: ApiCallByIdProps): Promise => + http.fetch(EXCEPTION_LIST_URL, { + method: 'DELETE', + query: { id, namespace_type: namespaceType }, + signal, + }); + +const deleteExceptionListByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise => + flow( + () => tryCatch(() => deleteExceptionListById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListSchema, response))), + flow(toPromise) + )(); + +export { deleteExceptionListByIdWithValidation as deleteExceptionListById }; /** * Delete an ExceptionListItem by providing a ExceptionListItem ID @@ -474,39 +483,31 @@ export const deleteExceptionListById = async ({ * * @throws An error if response is not OK */ -export const deleteExceptionListItemById = async ({ +const deleteExceptionListItemById = async ({ http, id, namespaceType, signal, -}: ApiCallByIdProps): Promise => { - const [validatedRequest, errorsRequest] = validate( - { id, namespace_type: namespaceType }, - deleteExceptionListItemSchema - ); - - if (validatedRequest != null) { - try { - const response = await http.fetch(EXCEPTION_LIST_ITEM_URL, { - method: 'DELETE', - query: { id, namespace_type: namespaceType }, - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, exceptionListItemSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } - } else { - return Promise.reject(errorsRequest); - } -}; +}: ApiCallByIdProps): Promise => + http.fetch(EXCEPTION_LIST_ITEM_URL, { + method: 'DELETE', + query: { id, namespace_type: namespaceType }, + signal, + }); + +const deleteExceptionListItemByIdWithValidation = async ({ + http, + id, + namespaceType, + signal, +}: ApiCallByIdProps): Promise => + flow( + () => tryCatch(() => deleteExceptionListItemById({ http, id, namespaceType, signal }), toError), + chain((response) => fromEither(validateEither(exceptionListItemSchema, response))), + flow(toPromise) + )(); + +export { deleteExceptionListItemByIdWithValidation as deleteExceptionListItemById }; /** * Add new Endpoint ExceptionList @@ -517,27 +518,26 @@ export const deleteExceptionListItemById = async ({ * @throws An error if response is not OK * */ -export const addEndpointExceptionList = async ({ +const addEndpointExceptionList = async ({ http, signal, -}: AddEndpointExceptionListProps): Promise => { - try { - const response = await http.fetch(ENDPOINT_LIST_URL, { - method: 'POST', - signal, - }); - - const [validatedResponse, errorsResponse] = validate(response, createEndpointListSchema); - - if (errorsResponse != null || validatedResponse == null) { - return Promise.reject(errorsResponse); - } else { - return Promise.resolve(validatedResponse); - } - } catch (error) { - return Promise.reject(error); - } -}; +}: AddEndpointExceptionListProps): Promise => + http.fetch(ENDPOINT_LIST_URL, { + method: 'POST', + signal, + }); + +const addEndpointExceptionListWithValidation = async ({ + http, + signal, +}: AddEndpointExceptionListProps): Promise => + flow( + () => tryCatch(() => addEndpointExceptionList({ http, signal }), toError), + chain((response) => fromEither(validateEither(createEndpointListSchema, response))), + flow(toPromise) + )(); + +export { addEndpointExceptionListWithValidation as addEndpointExceptionList }; /** * Fetch an ExceptionList by providing a ExceptionList ID From deae756756612402665e9f4f3ba14a62104023fb Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 23 Dec 2020 21:13:20 -0500 Subject: [PATCH 085/100] [Security Solution] Fix flow/draggable in details event (#86834) * fix details event * fix types + add unit test * review with angela * fix lint error Co-authored-by: Angela Chuang --- .../common/types/timeline/index.ts | 19 ++- .../__snapshots__/event_details.test.tsx.snap | 2 + .../event_details/event_details.test.tsx | 4 +- .../event_details/event_details.tsx | 6 +- .../event_fields_browser.test.tsx | 11 ++ .../event_details/event_fields_browser.tsx | 7 +- .../events_viewer/event_details_flyout.tsx | 3 +- .../events_viewer/events_viewer.test.tsx | 157 +++++++++++++----- .../events_viewer/events_viewer.tsx | 39 +---- .../common/components/events_viewer/index.tsx | 2 +- .../common/components/events_viewer/mock.ts | 88 ++++++++++ .../navigation/breadcrumbs/index.test.ts | 2 +- .../components/navigation/index.test.tsx | 2 +- .../navigation/tab_navigation/index.test.tsx | 2 +- .../common/components/url_state/helpers.ts | 4 +- .../components/url_state/test_dependencies.ts | 2 +- .../public/common/mock/global_state.ts | 3 +- .../public/common/mock/timeline_results.ts | 9 +- .../components/alerts_table/actions.test.tsx | 8 +- .../flyout/bottom_bar/index.test.tsx | 2 +- .../components/flyout/bottom_bar/index.tsx | 2 +- .../components/flyout/header/index.tsx | 3 +- .../timelines/components/flyout/index.tsx | 3 +- .../timelines/components/flyout/selectors.ts | 3 +- .../components/open_timeline/helpers.test.ts | 8 +- .../components/open_timeline/helpers.ts | 7 +- .../open_timeline/note_previews/index.tsx | 2 + .../body/data_driven_columns/index.tsx | 3 +- .../body/events/event_column_view.test.tsx | 3 +- .../body/events/event_column_view.tsx | 4 +- .../components/timeline/body/events/index.tsx | 3 +- .../timeline/body/events/stateful_event.tsx | 14 +- .../components/timeline/body/helpers.tsx | 3 +- .../components/timeline/body/index.test.tsx | 76 ++++++++- .../components/timeline/body/index.tsx | 4 +- .../components/timeline/event_details.tsx | 6 +- .../timeline/expandable_event/index.tsx | 8 +- .../timelines/components/timeline/index.tsx | 6 +- .../timeline/notes_tab_content/index.tsx | 40 +++-- .../timeline/notes_tab_content/selectors.ts | 20 +++ .../timeline/pinned_tab_content/index.tsx | 8 +- .../timeline/query_tab_content/index.test.tsx | 3 +- .../timeline/query_tab_content/index.tsx | 34 +--- .../timeline/tabs_content/index.tsx | 39 ++++- .../timeline/tabs_content/selectors.ts | 10 +- .../containers/active_timeline_context.ts | 8 +- .../timelines/containers/details/index.tsx | 11 +- .../timelines/store/timeline/actions.ts | 9 +- .../timelines/store/timeline/defaults.ts | 4 +- .../timelines/store/timeline/epic.test.ts | 4 +- .../timeline/epic_local_storage.test.tsx | 3 +- .../public/timelines/store/timeline/model.ts | 9 +- .../timelines/store/timeline/reducer.test.ts | 4 +- .../timelines/store/timeline/reducer.ts | 26 +-- 54 files changed, 535 insertions(+), 227 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index aa114ff0748980..26d13b13f40cba 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -408,12 +408,23 @@ export type ImportTimelineResultSchema = runtimeTypes.TypeOf; -export type TimelineExpandedEvent = TimelineExpandedEventType | EmptyObject; +export type TimelineExpandedEventType = + | { + eventId: string; + indexName: string; + } + | EmptyObject; + +export type TimelineExpandedEvent = { + [tab in TimelineTabs]?: TimelineExpandedEventType; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap index 973d067d9e3794..e9b11d9bcdf71b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/event_details.test.tsx.snap @@ -577,6 +577,7 @@ exports[`EventDetails rendering should match snapshot 1`] = ` } eventId="Y-6TfmcB0WOhS6qyMv3s" timelineId="test" + timelineTabType="query" /> , "id": "table-view", @@ -1157,6 +1158,7 @@ exports[`EventDetails rendering should match snapshot 1`] = ` } eventId="Y-6TfmcB0WOhS6qyMv3s" timelineId="test" + timelineTabType="query" /> , "id": "table-view", diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index 46590060507815..9ab286b120dd3f 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { waitFor } from '@testing-library/dom'; import { ReactWrapper, shallow } from 'enzyme'; import React from 'react'; @@ -16,7 +17,7 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; import { mockAlertDetailsData } from './__mocks__'; import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; -import { waitFor } from '@testing-library/dom'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('../link_to'); describe('EventDetails', () => { @@ -27,6 +28,7 @@ describe('EventDetails', () => { id: mockDetailItemDataId, isAlert: false, onViewSelected: jest.fn(), + timelineTabType: TimelineTabs.query, timelineId: 'test', view: EventsViewType.summaryView, }; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 291893fe682b43..123a3fa7b610b6 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -14,6 +14,7 @@ import { EventFieldsBrowser } from './event_fields_browser'; import { JsonView } from './json_view'; import * as i18n from './translations'; import { SummaryView } from './summary_view'; +import { TimelineTabs } from '../../../../common/types/timeline'; export type View = EventsViewType.tableView | EventsViewType.jsonView | EventsViewType.summaryView; export enum EventsViewType { @@ -29,6 +30,7 @@ interface Props { isAlert: boolean; view: EventsViewType; onViewSelected: (selected: EventsViewType) => void; + timelineTabType: TimelineTabs | 'flyout'; timelineId: string; } @@ -52,6 +54,7 @@ const EventDetailsComponent: React.FC = ({ id, view, onViewSelected, + timelineTabType, timelineId, isAlert, }) => { @@ -91,6 +94,7 @@ const EventDetailsComponent: React.FC = ({ data={data} eventId={id} timelineId={timelineId} + timelineTabType={timelineTabType} /> ), @@ -106,7 +110,7 @@ const EventDetailsComponent: React.FC = ({ ), }, ], - [alerts, browserFields, data, id, isAlert, timelineId] + [alerts, browserFields, data, id, isAlert, timelineId, timelineTabType] ); const selectedTab = useMemo(() => tabs.find((t) => t.id === view) ?? tabs[0], [tabs, view]); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx index cd50eb7880e56a..0fc29e7193d4d0 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx @@ -13,6 +13,7 @@ import { timelineActions } from '../../../timelines/store/timeline'; import { EventFieldsBrowser } from './event_fields_browser'; import { mockBrowserFields } from '../../containers/source/mock'; import { useMountAppended } from '../../utils/use_mount_appended'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); @@ -48,6 +49,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -66,6 +68,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -89,6 +92,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -108,6 +112,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -127,6 +132,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={eventId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -158,6 +164,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -182,6 +189,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -196,6 +204,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -220,6 +229,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); @@ -238,6 +248,7 @@ describe('EventFieldsBrowser', () => { data={mockDetailItemData} eventId={mockDetailItemDataId} timelineId="test" + timelineTabType={TimelineTabs.query} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index cd1579b2990936..9733fafbe1c4d2 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -29,12 +29,14 @@ import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { getColumns } from './columns'; import { EVENT_FIELDS_TABLE_CLASS_NAME, onEventDetailsTabKeyPressed, search } from './helpers'; import { useDeepEqualSelector } from '../../hooks/use_selector'; +import { TimelineTabs } from '../../../../common/types/timeline'; interface Props { browserFields: BrowserFields; data: TimelineEventsDetailsItem[]; eventId: string; timelineId: string; + timelineTabType: TimelineTabs | 'flyout'; } const TableWrapper = styled.div` @@ -87,7 +89,7 @@ const getAriaRowindex = (timelineEventsDetailsItem: TimelineEventsDetailsItem) = /** Renders a table view or JSON view of the `ECS` `data` */ export const EventFieldsBrowser = React.memo( - ({ browserFields, data, eventId, timelineId }) => { + ({ browserFields, data, eventId, timelineTabType, timelineId }) => { const containerElement = useRef(null); const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); @@ -156,7 +158,7 @@ export const EventFieldsBrowser = React.memo( columnHeaders, eventId, onUpdateColumns, - contextId: `event-fields-browser-for-${timelineId}`, + contextId: `event-fields-browser-for-${timelineId}-${timelineTabType}`, timelineId, toggleColumn, getLinkValue, @@ -167,6 +169,7 @@ export const EventFieldsBrowser = React.memo( eventId, onUpdateColumns, timelineId, + timelineTabType, toggleColumn, getLinkValue, ] diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx index 48bdebbc0aa4ff..9c09f2e696104c 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx @@ -39,7 +39,7 @@ const EventDetailsFlyoutComponent: React.FC = ({ const dispatch = useDispatch(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults)?.expandedEvent ?? {} + (state) => (getTimeline(state, timelineId) ?? timelineDefaults)?.expandedEvent?.query ?? {} ); const handleClearSelection = useCallback(() => { @@ -75,6 +75,7 @@ const EventDetailsFlyoutComponent: React.FC = ({ isAlert={isAlert} loading={loading} timelineId={timelineId} + timelineTabType="flyout" /> diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx index 423b3566e4eb5a..6250345579cff2 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.test.tsx @@ -5,12 +5,13 @@ */ import React from 'react'; +import { waitFor, act } from '@testing-library/react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; import { mockIndexNames, mockIndexPattern, TestProviders } from '../../mock'; -import { mockEventViewerResponse } from './mock'; +import { mockEventViewerResponse, mockEventViewerResponseWithEvents } from './mock'; import { StatefulEventsViewer } from '.'; import { EventsViewer } from './events_viewer'; import { defaultHeaders } from './default_headers'; @@ -30,6 +31,15 @@ jest.mock('../../../timelines/components/graph_overlay', () => ({ GraphOverlay: jest.fn(() =>
    ), })); +const mockDispatch = jest.fn(); +jest.mock('react-redux', () => { + const original = jest.requireActual('react-redux'); + return { + ...original, + useDispatch: () => mockDispatch, + }; +}); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -50,6 +60,9 @@ const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); +const mockUseTimelineEvents: jest.Mock = useTimelineEvents as jest.Mock; +jest.mock('../../../timelines/containers'); + const from = '2019-08-26T22:10:56.791Z'; const to = '2019-08-27T22:10:56.794Z'; @@ -108,14 +121,51 @@ describe('EventsViewer', () => { start: from, scopeId: SourcererScopeName.timeline, }; - beforeEach(() => { - (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); + mockUseTimelineEvents.mockReset(); }); beforeAll(() => { mockUseSourcererScope.mockImplementation(() => defaultMocks); }); + + describe('event details', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponseWithEvents]); + }); + + test('call the right reduce action to show event details', async () => { + const wrapper = mount( + + + + ); + + await act(async () => { + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + }); + + await waitFor(() => { + expect(mockDispatch).toBeCalledTimes(2); + expect(mockDispatch.mock.calls[1][0]).toEqual({ + payload: { + event: { + eventId: 'yb8TkHYBRgU82_bJu_rY', + indexName: 'auditbeat-7.10.1-2020.12.18-000001', + }, + tabType: 'query', + timelineId: 'test-stateful-events-viewer', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + }); + }); + describe('rendering', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the "Showing..." subtitle with the expected event count', () => { const wrapper = mount( @@ -160,57 +210,66 @@ describe('EventsViewer', () => { ); }); }); - describe('loading', () => { - beforeAll(() => { - mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); - }); - test('it does NOT render fetch index pattern is loading', () => { - const wrapper = mount( - - - - ); + }); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + describe('loading', () => { + beforeAll(() => { + mockUseSourcererScope.mockImplementation(() => ({ ...defaultMocks, loading: true })); + }); + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); - test('it does NOT render when start is empty', () => { - testProps = { - ...testProps, - start: '', - }; - const wrapper = mount( - - - - ); + test('it does NOT render fetch index pattern is loading', () => { + const wrapper = mount( + + + + ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); - test('it does NOT render when end is empty', () => { - testProps = { - ...testProps, - end: '', - }; - const wrapper = mount( - - - - ); + test('it does NOT render when start is empty', () => { + testProps = { + ...testProps, + start: '', + }; + const wrapper = mount( + + + + ); - expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( - false - ); - }); + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); + }); + + test('it does NOT render when end is empty', () => { + testProps = { + ...testProps, + end: '', + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).first().exists()).toBe( + false + ); }); }); describe('headerFilterGroup', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the provided headerFilterGroup', () => { const wrapper = mount( @@ -284,6 +343,10 @@ describe('EventsViewer', () => { }); describe('utilityBar', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the provided utilityBar when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( @@ -313,6 +376,10 @@ describe('EventsViewer', () => { }); describe('header inspect button', () => { + beforeEach(() => { + mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponse]); + }); + test('it renders the inspect button when Resolver is NOT showing, because graphEventId is undefined', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 7d38e3b732fc09..1d06f07bc774b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -6,21 +6,15 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useEffect, useMemo, useState, useRef } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; -import { useDispatch } from 'react-redux'; import { Direction } from '../../../../common/search_strategy'; import { BrowserFields, DocValueFields } from '../../containers/source'; import { useTimelineEvents } from '../../../timelines/containers'; -import { timelineActions } from '../../../timelines/store/timeline'; import { useKibana } from '../../lib/kibana'; -import { - ColumnHeaderOptions, - KqlMode, - TimelineTabs, -} from '../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions, KqlMode } from '../../../timelines/store/timeline/model'; import { HeaderSection } from '../header_section'; import { defaultHeaders } from '../../../timelines/components/timeline/body/column_headers/default_headers'; import { Sort } from '../../../timelines/components/timeline/body/sort'; @@ -45,7 +39,11 @@ import { inputsModel } from '../../store'; import { useManageTimeline } from '../../../timelines/components/manage_timeline'; import { ExitFullScreen } from '../exit_full_screen'; import { useGlobalFullScreen } from '../../containers/use_full_screen'; -import { TimelineExpandedEvent, TimelineId } from '../../../../common/types/timeline'; +import { + TimelineExpandedEventType, + TimelineId, + TimelineTabs, +} from '../../../../common/types/timeline'; import { GraphOverlay } from '../../../timelines/components/graph_overlay'; import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles'; @@ -114,7 +112,7 @@ interface Props { deletedEventIds: Readonly; docValueFields: DocValueFields[]; end: string; - expandedEvent: TimelineExpandedEvent; + expandedEvent: TimelineExpandedEventType; filters: Filter[]; headerFilterGroup?: React.ReactNode; height?: number; @@ -160,7 +158,6 @@ const EventsViewerComponent: React.FC = ({ utilityBar, graphEventId, }) => { - const dispatch = useDispatch(); const { globalFullScreen } = useGlobalFullScreen(); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); @@ -191,9 +188,6 @@ const EventsViewerComponent: React.FC = ({ [justTitle] ); - const prevCombinedQueries = useRef<{ - filterQuery: string; - } | null>(null); const combinedQueries = combineQueries({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), dataProviders, @@ -220,12 +214,6 @@ const EventsViewerComponent: React.FC = ({ queryFields, ]); - const prevSortField = useRef< - Array<{ - field: string; - direction: Direction; - }> - >([]); const sortField = useMemo( () => sort.map(({ columnId, sortDirection }) => ({ @@ -251,17 +239,6 @@ const EventsViewerComponent: React.FC = ({ skip: !canQueryTimeline, }); - useEffect(() => { - if (!deepEqual(prevCombinedQueries.current, combinedQueries)) { - prevCombinedQueries.current = combinedQueries; - dispatch(timelineActions.toggleExpandedEvent({ timelineId: id })); - } - if (!deepEqual(prevSortField.current, sortField)) { - prevSortField.current = sortField; - dispatch(timelineActions.toggleExpandedEvent({ timelineId: id })); - } - }, [combinedQueries, dispatch, id, sortField]); - const totalCountMinusDeleted = useMemo( () => (totalCount > 0 ? totalCount - deletedEventIds.length : 0), [deletedEventIds.length, totalCount] diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 3272b0306f9c98..d7310ea776659d 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -166,7 +166,7 @@ const makeMapStateToProps = () => { columns, dataProviders, deletedEventIds, - expandedEvent, + expandedEvent: expandedEvent?.query ?? {}, excludedRowRendererIds, filters: getGlobalFiltersQuerySelector(state), id, diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts index d2bd940dcc266b..153992d9f1adb9 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts @@ -12,3 +12,91 @@ export const mockEventViewerResponse = { }, events: [], }; + +export const mockEventViewerResponseWithEvents = { + totalCount: 1, + pageInfo: { + activePage: 0, + fakeTotalCount: 100, + }, + events: [ + { + ecs: { + _id: 'yb8TkHYBRgU82_bJu_rY', + timestamp: '2020-12-23T14:49:39.957Z', + _index: 'auditbeat-7.10.1-2020.12.18-000001', + '@timestamp': ['2020-12-23T14:49:39.957Z'], + event: { + module: ['system'], + action: ['process_started'], + category: ['process'], + dataset: ['process'], + kind: ['event'], + type: ['start'], + }, + host: { + name: ['handsome'], + os: { + family: ['darwin'], + }, + id: ['33'], + ip: ['0.0.0.0'], + }, + user: { + name: ['handsome'], + }, + message: ['Process node (PID: 77895) by user handsome STARTED'], + agent: { + type: ['auditbeat'], + }, + process: { + hash: { + sha1: ['`12345678987654323456Y7U87654`'], + }, + pid: ['77895'], + name: ['node'], + ppid: ['73537'], + args: [ + '/Users/handsome/.nvm/versions/node/v14.15.3/bin/node', + '/Users/handsome/Documents/workspace/kibana/node_modules/jest-worker/build/workers/processChild.js', + ], + entity_id: ['3arNfOyR9NwR2u03'], + executable: ['/Users/handsome/.nvm/versions/node/v14.15.3/bin/node'], + working_directory: ['/Users/handsome/Documents/workspace/kibana/x-pack'], + }, + }, + data: [ + { + field: '@timestamp', + value: ['2020-12-23T14:49:39.957Z'], + }, + { + field: 'event.module', + value: ['system'], + }, + { + field: 'event.action', + value: ['process_started'], + }, + { + field: 'host.name', + value: ['handsome'], + }, + { + field: 'user.name', + value: ['handsome'], + }, + { + field: 'message', + value: ['Process node (PID: 77895) by user handsome STARTED'], + }, + { + field: 'event.dataset', + value: ['process'], + }, + ], + _id: 'yb8TkHYBRgU82_bJu_rY', + _index: 'auditbeat-7.10.1-2020.12.18-000001', + }, + ], +}; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 36cdc807c4c0c9..891e7bfffe8683 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -11,7 +11,7 @@ import { HostsTableType } from '../../../../hosts/store/model'; import { RouteSpyState, SiemRouteType } from '../../../utils/route/types'; import { TabNavigationProps } from '../tab_navigation/types'; import { NetworkRouteType } from '../../../../network/pages/navigation/types'; -import { TimelineTabs } from '../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const setBreadcrumbsMock = jest.fn(); const chromeMock = { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index 158da3be3bbf78..f2fbe48c97c83d 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -14,7 +14,7 @@ import { navTabs } from '../../../app/home/home_navigations'; import { HostsTableType } from '../../../hosts/store/model'; import { RouteSpyState } from '../../utils/route/types'; import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../common/types/timeline'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index f4ffc25146be5a..e5c011cdc14be8 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -6,12 +6,12 @@ import { mount } from 'enzyme'; import React from 'react'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { navTabs } from '../../../../app/home/home_navigations'; import { SecurityPageName } from '../../../../app/types'; import { navTabsHostDetails } from '../../../../hosts/pages/details/nav_tabs'; import { HostsTableType } from '../../../../hosts/store/model'; -import { TimelineTabs } from '../../../../timelines/store/timeline/model'; import { RouteSpyState } from '../../../utils/route/types'; import { CONSTANTS } from '../../url_state/constants'; import { TabNavigationComponent } from './'; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 9932e52b6a1d14..9f51ecf9483b25 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -12,11 +12,11 @@ import * as H from 'history'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { url } from '../../../../../../../src/plugins/kibana_utils/public'; -import { TimelineId } from '../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../common/types/timeline'; import { SecurityPageName } from '../../../app/types'; import { inputsSelectors, State } from '../../store'; import { UrlInputsModel } from '../../store/inputs/model'; -import { TimelineTabs, TimelineUrl } from '../../../timelines/store/timeline/model'; +import { TimelineUrl } from '../../../timelines/store/timeline/model'; import { timelineSelectors } from '../../../timelines/store/timeline'; import { formatDate } from '../super_date_picker'; import { NavTab } from '../navigation/types'; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts index bf5b6b17196050..d835636aa27784 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts @@ -17,7 +17,7 @@ import { Query } from '../../../../../../../src/plugins/data/public'; import { networkModel } from '../../../network/store'; import { hostsModel } from '../../../hosts/store'; import { HostsTableType } from '../../../hosts/store/model'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../common/types/timeline'; type Action = 'PUSH' | 'POP' | 'REPLACE'; const pop: Action = 'POP'; diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index db218479915340..320c3a0736540d 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -24,13 +24,12 @@ import { DEFAULT_INDEX_PATTERN, } from '../../../common/constants'; import { networkModel } from '../../network/store'; -import { TimelineType, TimelineStatus } from '../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../common/types/timeline'; import { mockManagementState } from '../../management/store/reducer'; import { ManagementState } from '../../management/types'; import { initialSourcererState, SourcererScopeName } from '../store/sourcerer/model'; import { mockBrowserFields, mockDocValueFields } from '../containers/source/mock'; import { mockIndexPattern } from './index_pattern'; -import { TimelineTabs } from '../../timelines/store/timeline/model'; export const mockGlobalState: State = { app: { diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index c8d9fc981d8805..03109803eb9d8c 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -5,14 +5,19 @@ */ import { FilterStateStore } from '../../../../../../src/plugins/data/common/es_query/filters/meta_filter'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../common/types/timeline'; import { OpenTimelineResult } from '../../timelines/components/open_timeline/types'; import { GetAllTimeline, SortFieldTimeline, TimelineResult, Direction } from '../../graphql/types'; import { TimelineEventsDetailsItem } from '../../../common/search_strategy'; import { allTimelinesQuery } from '../../timelines/containers/all/index.gql_query'; import { CreateTimelineProps } from '../../detections/components/alerts_table/types'; -import { TimelineModel, TimelineTabs } from '../../timelines/store/timeline/model'; +import { TimelineModel } from '../../timelines/store/timeline/model'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; export interface MockedProvidedQuery { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index d251cce381536a..64e916f87b09d8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -19,10 +19,14 @@ import { } from '../../../common/mock/'; import { CreateTimeline, UpdateTimelineLoading } from './types'; import { Ecs } from '../../../../common/ecs'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../../common/types/timeline'; import { ISearchStart } from '../../../../../../../src/plugins/data/public'; import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; -import { TimelineTabs } from '../../../timelines/store/timeline/model'; jest.mock('apollo-client'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx index c9bd1ee13cd957..f9bb8b58634fa1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.test.tsx @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { TestProviders } from '../../../../common/mock/test_providers'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { FlyoutBottomBar } from '.'; describe('FlyoutBottomBar', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx index e6de34f1bf7a44..edc571528e94a3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/bottom_bar/index.tsx @@ -14,7 +14,7 @@ import { DataProvider } from '../../timeline/data_providers/data_provider'; import { flattenIntoAndGroups } from '../../timeline/data_providers/helpers'; import { DataProviders } from '../../timeline/data_providers'; import { FlyoutHeaderPanel } from '../header'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; export const FLYOUT_BUTTON_CLASS_NAME = 'timeline-flyout-button'; 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 e22a6616ecfc68..73c2eae1402c0d 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 @@ -20,7 +20,7 @@ import styled from 'styled-components'; import { FormattedRelative } from '@kbn/i18n/react'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { TimelineStatus, TimelineType } from '../../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs, TimelineType } from '../../../../../common/types/timeline'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { AddToFavoritesButton } from '../../timeline/properties/helpers'; @@ -33,7 +33,6 @@ import { ActiveTimelines } from './active_timelines'; import * as i18n from './translations'; import * as commonI18n from '../../timeline/properties/translations'; import { getTimelineStatusByIdSelector } from './selectors'; -import { TimelineTabs } from '../../../store/timeline/model'; // to hide side borders const StyledPanel = styled(EuiPanel)` diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx index 622efefc6230a0..6881ad3ee4bc18 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx @@ -11,10 +11,9 @@ import { useDispatch } from 'react-redux'; import styled from 'styled-components'; import { AppLeaveHandler } from '../../../../../../../src/core/public'; -import { TimelineId, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { timelineActions } from '../../store/timeline'; -import { TimelineTabs } from '../../store/timeline/model'; import { FlyoutBottomBar } from './bottom_bar'; import { Pane } from './pane'; import { getTimelineShowStatusByIdSelector } from './selectors'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts index 0ec4fecedfa7fd..e6892c121ed442 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts @@ -6,9 +6,8 @@ import { createSelector } from 'reselect'; -import { TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { timelineSelectors } from '../../store/timeline'; -import { TimelineTabs } from '../../store/timeline/model'; export const getTimelineShowStatusByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 61b0c004dcb9dc..da6eec968d11c7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -39,12 +39,16 @@ import { KueryFilterQueryKind } from '../../../common/store/model'; import { Note } from '../../../common/lib/note'; import moment from 'moment'; import sinon from 'sinon'; -import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { + TimelineId, + TimelineType, + TimelineStatus, + TimelineTabs, +} from '../../../../common/types/timeline'; import { mockTimeline as mockSelectedTimeline, mockTemplate as mockSelectedTemplate, } from './__mocks__'; -import { TimelineTabs } from '../../store/timeline/model'; jest.mock('../../../common/store/inputs/actions'); jest.mock('../../../common/components/url_state/normalize_time_range.ts'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 37de75fd736af7..c7821df3473114 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -27,6 +27,7 @@ import { TimelineId, TimelineStatus, TimelineType, + TimelineTabs, } from '../../../../common/types/timeline'; import { @@ -42,11 +43,7 @@ import { addTimeline as dispatchAddTimeline, addNote as dispatchAddGlobalTimelineNote, } from '../../../timelines/store/timeline/actions'; -import { - ColumnHeaderOptions, - TimelineModel, - TimelineTabs, -} from '../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions, TimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx index fc05e61442e83d..d35a5f487ed8ee 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx @@ -17,6 +17,7 @@ import { MarkdownRenderer } from '../../../../common/components/markdown_editor' import { timelineActions } from '../../../store/timeline'; import { NOTE_CONTENT_CLASS_NAME } from '../../timeline/body/helpers'; import * as i18n from './translations'; +import { TimelineTabs } from '../../../../../common/types/timeline'; export const NotePreviewsContainer = styled.section` padding-top: ${({ theme }) => `${theme.eui.euiSizeS}`}; @@ -37,6 +38,7 @@ const ToggleEventDetailsButtonComponent: React.FC const handleClick = useCallback(() => { dispatch( timelineActions.toggleExpandedEvent({ + tabType: TimelineTabs.notes, timelineId, event: { eventId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx index 0242a2a0ff0914..21ca30658f5306 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/data_driven_columns/index.tsx @@ -11,7 +11,8 @@ import { getOr } from 'lodash/fp'; import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '../../../../../common/components/drag_and_drop/helpers'; import { Ecs } from '../../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../../common/types/timeline'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { ARIA_COLUMN_INDEX_OFFSET } from '../../helpers'; import { EventsTd, EVENTS_TD_CLASS_NAME, EventsTdContent, EventsTdGroupData } from '../../styles'; import { ColumnRenderer } from '../renderers/column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx index 0525767e616bef..cff3d2890d85a1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx @@ -11,7 +11,7 @@ import { DEFAULT_ACTIONS_COLUMN_WIDTH } from '../constants'; import * as i18n from '../translations'; import { EventColumnView } from './event_column_view'; -import { TimelineType } from '../../../../../../common/types/timeline'; +import { TimelineTabs, TimelineType } from '../../../../../../common/types/timeline'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; jest.mock('../../../../../common/hooks/use_selector'); @@ -48,6 +48,7 @@ describe('EventColumnView', () => { selectedEventIds: {}, showCheckboxes: false, showNotes: false, + tabType: TimelineTabs.query, timelineId: 'timeline-test', toggleShowNotes: jest.fn(), updateNote: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx index ae8d2a47c7dc78..4e61fb7346c5cf 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useMemo } from 'react'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; import { Ecs } from '../../../../../../common/ecs'; import { TimelineNonEcsData } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnPinEvent, OnRowSelected, OnUnPinEvent } from '../../events'; import { EventsTrData } from '../../styles'; import { Actions } from '../actions'; @@ -26,7 +26,7 @@ import { InvestigateInTimelineAction } from '../../../../../detections/component import { AddEventNoteAction } from '../actions/add_note_icon_item'; import { PinEventAction } from '../actions/pin_event_action'; import { inputsModel } from '../../../../../common/store'; -import { TimelineId } from '../../../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; import { timelineSelectors } from '../../../../store/timeline'; import { timelineDefaults } from '../../../../store/timeline/defaults'; import { AddToCaseAction } from '../../../../../cases/components/timeline_actions/add_to_case_action'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx index 92ae01b185f7a1..dba08823b87fe2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/index.tsx @@ -12,7 +12,8 @@ import { TimelineItem, TimelineNonEcsData, } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { TimelineTabs } from '../../../../../../common/types/timeline'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnRowSelected } from '../../events'; import { EventsTbody } from '../../styles'; import { ColumnRenderer } from '../renderers/column_renderer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index e3f5a744e8b7d7..f00b86ef96567d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -8,13 +8,13 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; -import { TimelineId } from '../../../../../../common/types/timeline'; +import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; import { BrowserFields } from '../../../../../common/containers/source'; import { TimelineItem, TimelineNonEcsData, } from '../../../../../../common/search_strategy/timeline'; -import { ColumnHeaderOptions, TimelineTabs } from '../../../../../timelines/store/timeline/model'; +import { ColumnHeaderOptions } from '../../../../../timelines/store/timeline/model'; import { OnPinEvent, OnRowSelected } from '../../events'; import { STATEFUL_EVENT_CSS_CLASS_NAME } from '../../helpers'; import { EventsTrGroup, EventsTrSupplement, EventsTrSupplementContainer } from '../../styles'; @@ -92,7 +92,10 @@ const StatefulEventComponent: React.FC = ({ const [showNotes, setShowNotes] = useState<{ [eventId: string]: boolean }>({}); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent + (state) => + (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent[ + tabType ?? TimelineTabs.query + ] ?? {} ); const getNotesByIds = useMemo(() => appSelectors.notesByIdsSelector(), []); const notesById = useDeepEqualSelector(getNotesByIds); @@ -153,6 +156,7 @@ const StatefulEventComponent: React.FC = ({ dispatch( timelineActions.toggleExpandedEvent({ + tabType, timelineId, event: { eventId, @@ -161,10 +165,10 @@ const StatefulEventComponent: React.FC = ({ }) ); - if (timelineId === TimelineId.active) { + if (timelineId === TimelineId.active && tabType === TimelineTabs.query) { activeTimeline.toggleExpandedEvent({ eventId, indexName }); } - }, [dispatch, event._id, event._index, timelineId]); + }, [dispatch, event._id, event._index, tabType, timelineId]); const associateNote = useCallback( (noteId: string) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx index 0295d44b646d77..3a738db981b38a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx @@ -16,12 +16,11 @@ import { TimelineTypeLiteral, TimelineType, TimelineId, + TimelineTabs, } from '../../../../../common/types/timeline'; import { OnPinEvent, OnUnPinEvent } from '../events'; import { ActionIconItem } from './actions/action_icon_item'; - import * as i18n from './translations'; -import { TimelineTabs } from '../../../store/timeline/model'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const omitTypenameAndEmpty = (k: string, v: any): any | undefined => diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index c8e911db85f640..cc04b83382998f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -17,7 +17,7 @@ import { BodyComponent, StatefulBodyProps } from '.'; import { Sort } from './sort'; import { useMountAppended } from '../../../../common/utils/use_mount_appended'; import { timelineActions } from '../../../store/timeline'; -import { TimelineTabs } from '../../../store/timeline/model'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const mockSort: Sort[] = [ { @@ -221,4 +221,78 @@ describe('Body', () => { ); }); }); + + describe('event details', () => { + beforeEach(() => { + mockDispatch.mockReset(); + }); + test('call the right reduce action to show event details for query tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'query', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + + test('call the right reduce action to show event details for pinned tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'pinned', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + + test('call the right reduce action to show event details for notes tab', async () => { + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click'); + wrapper.update(); + expect(mockDispatch).toBeCalledTimes(1); + expect(mockDispatch.mock.calls[0][0]).toEqual({ + payload: { + event: { + eventId: '1', + indexName: undefined, + }, + tabType: 'notes', + timelineId: 'timeline-test', + }, + type: 'x-pack/security_solution/local/timeline/TOGGLE_EXPANDED_EVENT', + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx index 4a33d0d3af33e9..a03f4c07645ad0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx @@ -10,7 +10,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { connect, ConnectedProps } from 'react-redux'; import deepEqual from 'fast-deep-equal'; -import { RowRendererId, TimelineId } from '../../../../../common/types/timeline'; +import { RowRendererId, TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { FIRST_ARIA_INDEX, ARIA_COLINDEX_ATTRIBUTE, @@ -21,7 +21,7 @@ import { BrowserFields } from '../../../../common/containers/source'; import { TimelineItem } from '../../../../../common/search_strategy/timeline'; import { inputsModel, State } from '../../../../common/store'; import { useManageTimeline } from '../../manage_timeline'; -import { ColumnHeaderOptions, TimelineModel, TimelineTabs } from '../../../store/timeline/model'; +import { ColumnHeaderOptions, TimelineModel } from '../../../store/timeline/model'; import { timelineDefaults } from '../../../store/timeline/defaults'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; import { OnRowSelected, OnSelectAll } from '../events'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx index 9895f4eda0e6cc..c75f8a0d1c1708 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx @@ -25,10 +25,12 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { useTimelineEventsDetails } from '../../containers/details'; import { timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; +import { TimelineTabs } from '../../../../common/types/timeline'; interface EventDetailsProps { browserFields: BrowserFields; docValueFields: DocValueFields[]; + tabType: TimelineTabs; timelineId: string; handleOnEventClosed?: HandleOnEventClosed; } @@ -36,12 +38,13 @@ interface EventDetailsProps { const EventDetailsComponent: React.FC = ({ browserFields, docValueFields, + tabType, timelineId, handleOnEventClosed, }) => { const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const expandedEvent = useDeepEqualSelector( - (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent + (state) => (getTimeline(state, timelineId) ?? timelineDefaults).expandedEvent[tabType] ?? {} ); const [loading, detailsData] = useTimelineEventsDetails({ @@ -71,6 +74,7 @@ const EventDetailsComponent: React.FC = ({ isAlert={isAlert} loading={loading} timelineId={timelineId} + timelineTabType={tabType} /> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx index df8e84b4e2a789..a38fde0e3f548e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/expandable_event/index.tsx @@ -20,7 +20,7 @@ import { import React, { useMemo, useState } from 'react'; import styled from 'styled-components'; -import { TimelineExpandedEvent } from '../../../../../common/types/timeline'; +import { TimelineExpandedEventType, TimelineTabs } from '../../../../../common/types/timeline'; import { BrowserFields } from '../../../../common/containers/source'; import { EventDetails, @@ -35,9 +35,10 @@ export type HandleOnEventClosed = () => void; interface Props { browserFields: BrowserFields; detailsData: TimelineEventsDetailsItem[] | null; - event: TimelineExpandedEvent; + event: TimelineExpandedEventType; isAlert: boolean; loading: boolean; + timelineTabType: TimelineTabs | 'flyout'; timelineId: string; } @@ -71,7 +72,7 @@ export const ExpandableEventTitle = React.memo( ExpandableEventTitle.displayName = 'ExpandableEventTitle'; export const ExpandableEvent = React.memo( - ({ browserFields, event, timelineId, isAlert, loading, detailsData }) => { + ({ browserFields, event, timelineId, timelineTabType, isAlert, loading, detailsData }) => { const [view, setView] = useState(EventsViewType.summaryView); const message = useMemo(() => { @@ -116,6 +117,7 @@ export const ExpandableEvent = React.memo( id={event.eventId!} isAlert={isAlert} onViewSelected={setView} + timelineTabType={timelineTabType} timelineId={timelineId} view={view} /> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 41ac16a12e6485..2b26e3f9eb0b51 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -17,7 +17,7 @@ import { isTab } from '../../../common/components/accessibility/helpers'; import { useSourcererScope } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { FlyoutHeader, FlyoutHeaderPanel } from '../flyout/header'; -import { TimelineType } from '../../../../common/types/timeline'; +import { TimelineType, TimelineTabs } from '../../../../common/types/timeline'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { activeTimeline } from '../../containers/active_timeline_context'; import { EVENTS_COUNT_BUTTON_CLASS_NAME, onTimelineTabKeyPressed } from './helpers'; @@ -68,7 +68,9 @@ const StatefulTimelineComponent: React.FC = ({ timelineId }) => { id: timelineId, columns: defaultHeaders, indexNames: selectedPatterns, - expandedEvent: activeTimeline.getExpandedEvent(), + expandedEvent: { + [TimelineTabs.query]: activeTimeline.getExpandedEvent(), + }, show: false, }) ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx index bfb990cbd7364a..34e5aed885d5c1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { filter, pick, uniqBy } from 'lodash/fp'; +import { filter, uniqBy } from 'lodash/fp'; import { EuiAvatar, EuiFlexGroup, @@ -21,17 +21,17 @@ import styled from 'styled-components'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions } from '../../../store/timeline'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { TimelineStatus } from '../../../../../common/types/timeline'; +import { TimelineStatus, TimelineTabs } from '../../../../../common/types/timeline'; import { appSelectors } from '../../../../common/store/app'; -import { timelineDefaults } from '../../../store/timeline/defaults'; import { AddNote } from '../../notes/add_note'; import { CREATED_BY, NOTES } from '../../notes/translations'; import { PARTICIPANTS } from '../../../../cases/translations'; import { NotePreviews } from '../../open_timeline/note_previews'; import { TimelineResultNote } from '../../open_timeline/types'; import { EventDetails } from '../event_details'; +import { getTimelineNoteSelector } from './selectors'; const FullWidthFlexGroup = styled(EuiFlexGroup)` width: 100%; @@ -121,18 +121,14 @@ interface NotesTabContentProps { const NotesTabContentComponent: React.FC = ({ timelineId }) => { const dispatch = useDispatch(); - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const getTimelineNotes = useMemo(() => getTimelineNoteSelector(), []); const { createdBy, expandedEvent, eventIdToNoteIds, + noteIds, status: timelineStatus, - } = useDeepEqualSelector((state) => - pick( - ['createdBy', 'expandedEvent', 'eventIdToNoteIds', 'status'], - getTimeline(state, timelineId) ?? timelineDefaults - ) - ); + } = useDeepEqualSelector((state) => getTimelineNotes(state, timelineId)); const { browserFields, docValueFields } = useSourcererScope(SourcererScopeName.timeline); @@ -142,7 +138,20 @@ const NotesTabContentComponent: React.FC = ({ timelineId } ); const [newNote, setNewNote] = useState(''); const isImmutable = timelineStatus === TimelineStatus.immutable; - const notes: TimelineResultNote[] = useDeepEqualSelector(getNotesAsCommentsList); + const appNotes: TimelineResultNote[] = useDeepEqualSelector(getNotesAsCommentsList); + + const allTimelineNoteIds = useMemo(() => { + const eventNoteIds = Object.values(eventIdToNoteIds).reduce( + (acc, v) => [...acc, ...v], + [] + ); + return [...noteIds, ...eventNoteIds]; + }, [noteIds, eventIdToNoteIds]); + + const notes = useMemo( + () => appNotes.filter((appNote) => allTimelineNoteIds.includes(appNote?.noteId ?? '-1')), + [appNotes, allTimelineNoteIds] + ); // filter for savedObjectId to make sure we don't display `elastic` user while saving the note const participants = useMemo(() => uniqBy('updatedBy', filter('savedObjectId', notes)), [notes]); @@ -153,20 +162,21 @@ const NotesTabContentComponent: React.FC = ({ timelineId } ); const handleOnEventClosed = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); + dispatch(timelineActions.toggleExpandedEvent({ tabType: TimelineTabs.notes, timelineId })); }, [dispatch, timelineId]); const EventDetailsContent = useMemo( () => - expandedEvent.eventId ? ( + expandedEvent?.eventId != null ? ( ) : null, - [browserFields, docValueFields, expandedEvent.eventId, handleOnEventClosed, timelineId] + [browserFields, docValueFields, expandedEvent, handleOnEventClosed, timelineId] ); const SidebarContent = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts new file mode 100644 index 00000000000000..37ee980b1a4ae0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSelector } from 'reselect'; + +import { timelineSelectors } from '../../../store/timeline'; + +export const getTimelineNoteSelector = () => + createSelector(timelineSelectors.selectTimeline, (timeline) => { + return { + createdBy: timeline.createdBy, + expandedEvent: timeline.expandedEvent?.notes ?? {}, + eventIdToNoteIds: timeline?.eventIdToNoteIds ?? {}, + noteIds: timeline.noteIds, + status: timeline.status, + }; + }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx index a0d2ca57f90b36..1054b5405d9d94 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx @@ -23,11 +23,12 @@ import { EventDetailsWidthProvider } from '../../../../common/components/events_ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { timelineDefaults } from '../../../store/timeline/defaults'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; -import { TimelineModel, TimelineTabs } from '../../../store/timeline/model'; +import { TimelineModel } from '../../../store/timeline/model'; import { EventDetails } from '../event_details'; import { ToggleExpandedEvent } from '../../../store/timeline/actions'; import { State } from '../../../../common/store'; import { calculateTotalPages } from '../helpers'; +import { TimelineTabs } from '../../../../../common/types/timeline'; const StyledEuiFlyoutBody = styled(EuiFlyoutBody)` overflow-y: hidden; @@ -167,7 +168,7 @@ export const PinnedTabContentComponent: React.FC = ({ }); const handleOnEventClosed = useCallback(() => { - onEventClosed({ timelineId }); + onEventClosed({ tabType: TimelineTabs.pinned, timelineId }); }, [timelineId, onEventClosed]); return ( @@ -218,6 +219,7 @@ export const PinnedTabContentComponent: React.FC = ({ @@ -248,7 +250,7 @@ const makeMapStateToProps = () => { itemsPerPage, itemsPerPageOptions, pinnedEventIds, - showEventDetails: !!expandedEvent.eventId, + showEventDetails: !!expandedEvent[TimelineTabs.pinned]?.eventId, sort, }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx index 4769c826a2fad5..b24a4afcbeea27 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.test.tsx @@ -17,12 +17,11 @@ import { QueryTabContentComponent, Props as QueryTabContentComponentProps } from import { Sort } from '../body/sort'; import { mockDataProviders } from '../data_providers/mock/mock_data_providers'; import { useMountAppended } from '../../../../common/utils/use_mount_appended'; -import { TimelineId, TimelineStatus } from '../../../../../common/types/timeline'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../../common/types/timeline'; import { useTimelineEvents } from '../../../containers/index'; import { useTimelineEventsDetails } from '../../../containers/details/index'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { mockSourcererScope } from '../../../../common/containers/sourcerer/mocks'; -import { TimelineTabs } from '../../../store/timeline/model'; jest.mock('../../../containers/index', () => ({ useTimelineEvents: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index c0840d58174b32..d4c03117adcb93 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -14,7 +14,7 @@ import { EuiBadge, } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; -import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react'; +import React, { useState, useMemo, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { Dispatch } from 'redux'; import { connect, ConnectedProps } from 'react-redux'; @@ -33,7 +33,7 @@ import { calculateTotalPages, combineQueries } from '../helpers'; import { TimelineRefetch } from '../refetch_timeline'; import { esQuery, FilterManager } from '../../../../../../../../src/plugins/data/public'; import { useManageTimeline } from '../../manage_timeline'; -import { TimelineEventsType, TimelineId } from '../../../../../common/types/timeline'; +import { TimelineEventsType, TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { requiredFieldsForActions } from '../../../../detections/components/alerts_table/default_config'; import { SuperDatePicker } from '../../../../common/components/super_date_picker'; import { EventDetailsWidthProvider } from '../../../../common/components/events_viewer/event_details_width_context'; @@ -44,7 +44,7 @@ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; import { useSourcererScope } from '../../../../common/containers/sourcerer'; import { useTimelineEventsCountPortal } from '../../../../common/hooks/use_timeline_events_count'; -import { TimelineModel, TimelineTabs } from '../../../../timelines/store/timeline/model'; +import { TimelineModel } from '../../../../timelines/store/timeline/model'; import { EventDetails } from '../event_details'; import { TimelineDatePickerLock } from '../date_picker_lock'; import { HideShowContainer } from '../styles'; @@ -173,9 +173,6 @@ export const QueryTabContentComponent: React.FC = ({ kqlQueryExpression, ]); - const prevCombinedQueries = useRef<{ - filterQuery: string; - } | null>(null); const combinedQueries = useMemo( () => combineQueries({ @@ -211,12 +208,7 @@ export const QueryTabContentComponent: React.FC = ({ return [...columnFields, ...requiredFieldsForActions]; }, [columns]); - const prevTimelineQuerySortField = useRef< - Array<{ - field: string; - direction: Direction; - }> - >([]); + const timelineQuerySortField = useMemo( () => sort.map(({ columnId, sortDirection }) => ({ @@ -252,7 +244,7 @@ export const QueryTabContentComponent: React.FC = ({ }); const handleOnEventClosed = useCallback(() => { - onEventClosed({ timelineId }); + onEventClosed({ tabType: TimelineTabs.query, timelineId }); if (timelineId === TimelineId.active) { activeTimeline.toggleExpandedEvent({ @@ -266,17 +258,6 @@ export const QueryTabContentComponent: React.FC = ({ setIsTimelineLoading({ id: timelineId, isLoading: isQueryLoading || loadingSourcerer }); }, [loadingSourcerer, timelineId, isQueryLoading, setIsTimelineLoading]); - useEffect(() => { - if (!deepEqual(prevCombinedQueries.current, combinedQueries)) { - prevCombinedQueries.current = combinedQueries; - handleOnEventClosed(); - } - if (!deepEqual(prevTimelineQuerySortField.current, timelineQuerySortField)) { - prevTimelineQuerySortField.current = timelineQuerySortField; - handleOnEventClosed(); - } - }, [combinedQueries, handleOnEventClosed, timelineQuerySortField]); - return ( <> @@ -368,6 +349,7 @@ export const QueryTabContentComponent: React.FC = ({ @@ -416,7 +398,7 @@ const makeMapStateToProps = () => { dataProviders, eventType: eventType ?? 'raw', end: input.timerange.to, - expandedEvent, + expandedEvent: expandedEvent[TimelineTabs.query] ?? {}, filters: timelineFilter, timelineId, isLive: input.policy.kind === 'interval', @@ -425,7 +407,7 @@ const makeMapStateToProps = () => { kqlMode, kqlQueryExpression, showCallOutUnauthorizedMsg: getShowCallOutUnauthorizedMsg(state), - showEventDetails: !!expandedEvent.eventId, + showEventDetails: !!expandedEvent[TimelineTabs.query]?.eventId, show, sort, start: input.timerange.from, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index c97571fbbd6f35..25312ac2747ae3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -8,16 +8,21 @@ import { EuiBadge, EuiLoadingContent, EuiTabs, EuiTab } from '@elastic/eui'; import React, { lazy, memo, Suspense, useCallback, useEffect, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; +import { TimelineTabs } from '../../../../../common/types/timeline'; -import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; +import { + useShallowEqualSelector, + useDeepEqualSelector, +} from '../../../../common/hooks/use_selector'; import { TimelineEventsCountBadge } from '../../../../common/hooks/use_timeline_events_count'; import { timelineActions } from '../../../store/timeline'; -import { TimelineTabs } from '../../../store/timeline/model'; import { getActiveTabSelector, + getNoteIdsSelector, getNotesSelector, getPinnedEventSelector, getShowTimelineSelector, + getEventIdToNoteIdsSelector, } from './selectors'; import * as i18n from './translations'; @@ -137,37 +142,55 @@ const TabsContentComponent: React.FC = ({ timelineId, graphEve const getActiveTab = useMemo(() => getActiveTabSelector(), []); const getShowTimeline = useMemo(() => getShowTimelineSelector(), []); const getNumberOfPinnedEvents = useMemo(() => getPinnedEventSelector(), []); - const getNumberOfNotes = useMemo(() => getNotesSelector(), []); + const getAppNotes = useMemo(() => getNotesSelector(), []); + const getTimelineNoteIds = useMemo(() => getNoteIdsSelector(), []); + const getTimelinePinnedEventNotes = useMemo(() => getEventIdToNoteIdsSelector(), []); + const activeTab = useShallowEqualSelector((state) => getActiveTab(state, timelineId)); const showTimeline = useShallowEqualSelector((state) => getShowTimeline(state, timelineId)); const numberOfPinnedEvents = useShallowEqualSelector((state) => getNumberOfPinnedEvents(state, timelineId) ); - const numberOfNotes = useShallowEqualSelector((state) => getNumberOfNotes(state)); + const globalTimelineNoteIds = useDeepEqualSelector((state) => + getTimelineNoteIds(state, timelineId) + ); + const eventIdToNoteIds = useDeepEqualSelector((state) => + getTimelinePinnedEventNotes(state, timelineId) + ); + const appNotes = useDeepEqualSelector((state) => getAppNotes(state)); + + const allTimelineNoteIds = useMemo(() => { + const eventNoteIds = Object.values(eventIdToNoteIds).reduce( + (acc, v) => [...acc, ...v], + [] + ); + return [...globalTimelineNoteIds, ...eventNoteIds]; + }, [globalTimelineNoteIds, eventIdToNoteIds]); + + const numberOfNotes = useMemo( + () => appNotes.filter((appNote) => allTimelineNoteIds.includes(appNote.id)).length, + [appNotes, allTimelineNoteIds] + ); const setQueryAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.query }) ); }, [dispatch, timelineId]); const setGraphAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }) ); }, [dispatch, timelineId]); const setNotesAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.notes }) ); }, [dispatch, timelineId]); const setPinnedAsActiveTab = useCallback(() => { - dispatch(timelineActions.toggleExpandedEvent({ timelineId })); dispatch( timelineActions.setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.pinned }) ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts index 332785161b09ad..ff65c35588a8d5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts @@ -5,8 +5,8 @@ */ import { createSelector } from 'reselect'; +import { TimelineTabs } from '../../../../../common/types/timeline'; import { selectNotesById } from '../../../../common/store/app/selectors'; -import { TimelineTabs } from '../../../store/timeline/model'; import { selectTimeline } from '../../../store/timeline/selectors'; export const getActiveTabSelector = () => @@ -18,5 +18,11 @@ export const getShowTimelineSelector = () => export const getPinnedEventSelector = () => createSelector(selectTimeline, (timeline) => Object.keys(timeline?.pinnedEventIds ?? {}).length); +export const getNoteIdsSelector = () => + createSelector(selectTimeline, (timeline) => timeline?.noteIds ?? []); + +export const getEventIdToNoteIdsSelector = () => + createSelector(selectTimeline, (timeline) => timeline?.eventIdToNoteIds ?? {}); + export const getNotesSelector = () => - createSelector(selectNotesById, (notesById) => Object.keys(notesById ?? {}).length); + createSelector(selectNotesById, (notesById) => Object.values(notesById)); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts b/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts index 287fcd7f11e93d..3d6d061157b296 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/active_timeline_context.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimelineExpandedEvent } from '../../../common/types/timeline'; +import { TimelineExpandedEventType } from '../../../common/types/timeline'; import { TimelineEventsAllRequestOptions } from '../../../common/search_strategy/timeline'; import { TimelineArgs } from '.'; @@ -21,7 +21,7 @@ import { TimelineArgs } from '.'; class ActiveTimelineEvents { private _activePage: number = 0; - private _expandedEvent: TimelineExpandedEvent = {}; + private _expandedEvent: TimelineExpandedEventType = {}; private _pageName: string = ''; private _request: TimelineEventsAllRequestOptions | null = null; private _response: TimelineArgs | null = null; @@ -38,7 +38,7 @@ class ActiveTimelineEvents { return this._expandedEvent; } - toggleExpandedEvent(expandedEvent: TimelineExpandedEvent) { + toggleExpandedEvent(expandedEvent: TimelineExpandedEventType) { if (expandedEvent.eventId === this._expandedEvent.eventId) { this._expandedEvent = {}; } else { @@ -46,7 +46,7 @@ class ActiveTimelineEvents { } } - setExpandedEvent(expandedEvent: TimelineExpandedEvent) { + setExpandedEvent(expandedEvent: TimelineExpandedEventType) { this._expandedEvent = expandedEvent; } diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index ebc86b3c5cf5e7..556221f2d4bfde 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { noop } from 'lodash/fp'; +import { isEmpty, noop } from 'lodash/fp'; import { useCallback, useEffect, useRef, useState } from 'react'; import deepEqual from 'fast-deep-equal'; @@ -18,6 +18,7 @@ import { TimelineEventsDetailsStrategyResponse, } from '../../../../common/search_strategy'; import { isCompleteResponse, isErrorResponse } from '../../../../../../../src/plugins/data/public'; +import { AbortError } from '../../../../../../../src/plugins/kibana_utils/common'; export interface EventsArgs { detailsData: TimelineEventsDetailsItem[] | null; } @@ -50,7 +51,7 @@ export const useTimelineEventsDetails = ({ const timelineDetailsSearch = useCallback( (request: TimelineEventsDetailsRequestOptions | null) => { - if (request == null || skip) { + if (request == null || skip || isEmpty(request.eventId)) { return; } @@ -84,11 +85,13 @@ export const useTimelineEventsDetails = ({ searchSubscription$.unsubscribe(); } }, - error: () => { + error: (msg) => { if (!didCancel) { setLoading(false); } - notifications.toasts.addDanger('Failed to run search'); + if (!(msg instanceof AbortError)) { + notifications.toasts.addDanger('Failed to run search'); + } }, }); }; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index 487dc171f5994c..aefeda04dd962d 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -15,13 +15,15 @@ import { } from '../../../timelines/components/timeline/data_providers/data_provider'; import { SerializedFilterQuery } from '../../../common/store/types'; -import { KqlMode, TimelineModel, ColumnHeaderOptions, TimelineTabs } from './model'; +import { KqlMode, TimelineModel, ColumnHeaderOptions } from './model'; import { TimelineNonEcsData } from '../../../../common/search_strategy/timeline'; import { TimelineEventsType, - TimelineExpandedEvent, + TimelineExpandedEventType, TimelineTypeLiteral, RowRendererId, + TimelineExpandedEvent, + TimelineTabs, } from '../../../../common/types/timeline'; import { InsertTimeline } from './types'; @@ -36,8 +38,9 @@ export const addNoteToEvent = actionCreator<{ id: string; noteId: string; eventI ); export interface ToggleExpandedEvent { + event?: TimelineExpandedEventType; + tabType?: TimelineTabs; timelineId: string; - event?: TimelineExpandedEvent; } export const toggleExpandedEvent = actionCreator('TOGGLE_EXPANDED_EVENT'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 211bba3cc47d28..fd0d6bd3a9aaa0 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { Direction } from '../../../graphql/types'; import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers'; import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; -import { SubsetTimelineModel, TimelineModel, TimelineTabs } from './model'; +import { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false const { from: start, to: end } = normalizeTimeRange({ from: '', to: '' }, false); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts index d890fbe6a1069e..ec9ded610417f5 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts @@ -5,10 +5,10 @@ */ import { Filter, esFilters } from '../../../../../../../src/plugins/data/public'; -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { Direction } from '../../../graphql/types'; import { convertTimelineAsInput } from './epic'; -import { TimelineModel, TimelineTabs } from './model'; +import { TimelineModel } from './model'; describe('Epic Timeline', () => { describe('#convertTimelineAsInput ', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 3014ae8d19d32c..513d61ea862fa2 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -40,8 +40,7 @@ import { Direction } from '../../../graphql/types'; import { addTimelineInStorage } from '../../containers/local_storage'; import { isPageTimeline } from './epic_local_storage'; -import { TimelineId, TimelineStatus } from '../../../../common/types/timeline'; -import { TimelineTabs } from './model'; +import { TimelineId, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; jest.mock('../../containers/local_storage'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index f3ff3fffa53b96..c385f211537806 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -17,11 +17,11 @@ import type { TimelineType, TimelineStatus, RowRendererId, + TimelineTabs, } from '../../../../common/types/timeline'; export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; - export type ColumnHeaderType = 'not-filtered' | 'text-filter'; /** Uniquely identifies a column */ @@ -43,13 +43,6 @@ export interface ColumnHeaderOptions { width: number; } -export enum TimelineTabs { - query = 'query', - graph = 'graph', - notes = 'notes', - pinned = 'pinned', -} - export interface TimelineModel { /** The selected tab to displayed in the timeline */ activeTab: TimelineTabs; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index 59d5800271b8ab..4ae271ed7a4914 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -5,7 +5,7 @@ */ import { cloneDeep } from 'lodash/fp'; -import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; +import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { IS_OPERATOR, @@ -40,7 +40,7 @@ import { updateTimelineTitle, upsertTimelineColumn, } from './helpers'; -import { ColumnHeaderOptions, TimelineModel, TimelineTabs } from './model'; +import { ColumnHeaderOptions, TimelineModel } from './model'; import { timelineDefaults } from './defaults'; import { TimelineById } from './types'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 8ba4d548712667..2603c1c677956e 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -103,7 +103,7 @@ import { } from './helpers'; import { TimelineState, EMPTY_TIMELINE_BY_ID } from './types'; -import { TimelineType } from '../../../../common/types/timeline'; +import { TimelineType, TimelineTabs } from '../../../../common/types/timeline'; export const initialTimelineState: TimelineState = { timelineById: EMPTY_TIMELINE_BY_ID, @@ -178,16 +178,22 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) ...state, timelineById: addTimelineNoteToEvent({ id, noteId, eventId, timelineById: state.timelineById }), })) - .case(toggleExpandedEvent, (state, { timelineId, event = {} }) => ({ - ...state, - timelineById: { - ...state.timelineById, - [timelineId]: { - ...state.timelineById[timelineId], - expandedEvent: event, + .case(toggleExpandedEvent, (state, { tabType, timelineId, event = {} }) => { + const expandedTabType = tabType ?? TimelineTabs.query; + return { + ...state, + timelineById: { + ...state.timelineById, + [timelineId]: { + ...state.timelineById[timelineId], + expandedEvent: { + ...state.timelineById[timelineId].expandedEvent, + [expandedTabType]: event, + }, + }, }, - }, - })) + }; + }) .case(addProvider, (state, { id, provider }) => ({ ...state, timelineById: addTimelineProvider({ id, provider, timelineById: state.timelineById }), From 113634a66b1e8dc0fcd26512690a46eb5ec03fff Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Wed, 23 Dec 2020 21:44:57 -0500 Subject: [PATCH 086/100] [Security Solution] [Detections] Multiple timestamp fields (#86368) * query timestamp override and default @timestamp field, adds functional test for this * fix logic for when to filter out timestamp override documents * update the total hits field of the search result if we find hits within the secondary search. Without updating the total hits field, we could be finding events but not indexing them based on the bulk create logic * update integration test, updates logic for performing second search and excluding documents with timestamp override field * cleanup comments, remove commented out console logs, fix logic to break out of loop during secondary search after * default param to 'succeeded' * remove commented out code * always perform a secondary search when timestamp override field is present * perf improvement and fix bug where sortIds were being mixed between search after calls * set sortIds to undefined when not present on search result * exit loop and prevent extraneous searches from occurring if we exhaust sort ids --- .../signals/build_events_query.test.ts | 6 + .../signals/build_events_query.ts | 94 +++++++------- .../signals/find_threshold_signals.ts | 1 + .../signals/search_after_bulk_create.test.ts | 33 +---- .../signals/search_after_bulk_create.ts | 116 ++++++++++++++---- .../signals/single_search_after.test.ts | 7 +- .../signals/single_search_after.ts | 3 + .../threshold_find_previous_signals.ts | 1 + .../detection_engine/signals/utils.test.ts | 14 ++- .../lib/detection_engine/signals/utils.ts | 67 ++++++++++ .../basic/tests/find_statuses.ts | 4 +- .../basic/tests/open_close_signals.ts | 10 +- .../security_and_spaces/tests/add_actions.ts | 6 +- .../tests/create_exceptions.ts | 8 +- .../security_and_spaces/tests/create_rules.ts | 48 +++++++- .../tests/create_rules_bulk.ts | 4 +- .../tests/create_threat_matching.ts | 12 +- .../exception_operators_data_types/date.ts | 46 +++---- .../exception_operators_data_types/double.ts | 62 +++++----- .../exception_operators_data_types/float.ts | 62 +++++----- .../exception_operators_data_types/integer.ts | 62 +++++----- .../exception_operators_data_types/ip.ts | 58 ++++----- .../ip_array.ts | 56 ++++----- .../exception_operators_data_types/keyword.ts | 48 ++++---- .../keyword_array.ts | 48 ++++---- .../exception_operators_data_types/long.ts | 62 +++++----- .../exception_operators_data_types/text.ts | 74 +++++------ .../text_array.ts | 48 ++++---- .../tests/find_statuses.ts | 4 +- .../tests/generating_signals.ts | 38 +++--- .../tests/open_close_signals.ts | 14 +-- .../detection_engine_api_integration/utils.ts | 30 ++++- .../timestamp_override/data.json.gz | Bin 0 -> 217 bytes .../timestamp_override/mappings.json | 19 +++ 34 files changed, 689 insertions(+), 476 deletions(-) create mode 100644 x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz create mode 100644 x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts index 043066faa80106..f9899fb55bb6a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts @@ -16,6 +16,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -95,6 +96,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: '', timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -175,6 +177,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: fakeSortId, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -256,6 +259,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: fakeSortIdNumber, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -336,6 +340,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, @@ -423,6 +428,7 @@ describe('create_signals', () => { size: 100, searchAfterSortId: undefined, timestampOverride: undefined, + excludeDocsWithTimestampOverride: false, }); expect(query).toEqual({ allowNoIndices: true, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts index beca56770a9cad..31a424cdbcc1b8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts @@ -19,6 +19,7 @@ interface BuildEventsSearchQuery { sortOrder?: SortOrderOrUndefined; searchAfterSortId: string | number | undefined; timestampOverride: TimestampOverrideOrUndefined; + excludeDocsWithTimestampOverride: boolean; } export const buildEventsSearchQuery = ({ @@ -31,66 +32,65 @@ export const buildEventsSearchQuery = ({ searchAfterSortId, sortOrder, timestampOverride, + excludeDocsWithTimestampOverride, }: BuildEventsSearchQuery) => { - const timestamp = timestampOverride ?? '@timestamp'; - const docFields = - timestampOverride != null - ? [ - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - { - field: timestampOverride, - format: 'strict_date_optional_time', - }, - ] - : [ - { - field: '@timestamp', - format: 'strict_date_optional_time', - }, - ]; + const defaultTimeFields = ['@timestamp']; + const timestamps = + timestampOverride != null ? [timestampOverride, ...defaultTimeFields] : defaultTimeFields; + const docFields = timestamps.map((tstamp) => ({ + field: tstamp, + format: 'strict_date_optional_time', + })); + + const sortField = + timestampOverride != null && !excludeDocsWithTimestampOverride + ? timestampOverride + : '@timestamp'; - const filterWithTime = [ - filter, + const rangeFilter: unknown[] = [ { bool: { - filter: [ + should: [ { - bool: { - should: [ - { - range: { - [timestamp]: { - gte: from, - format: 'strict_date_optional_time', - }, - }, - }, - ], - minimum_should_match: 1, + range: { + [sortField]: { + gte: from, + format: 'strict_date_optional_time', + }, }, }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ { - bool: { - should: [ - { - range: { - [timestamp]: { - lte: to, - format: 'strict_date_optional_time', - }, - }, - }, - ], - minimum_should_match: 1, + range: { + [sortField]: { + lte: to, + format: 'strict_date_optional_time', + }, }, }, ], + minimum_should_match: 1, }, }, ]; + if (excludeDocsWithTimestampOverride) { + rangeFilter.push({ + bool: { + must_not: { + exists: { + field: timestampOverride, + }, + }, + }, + }); + } + const filterWithTime = [filter, { bool: { filter: rangeFilter } }]; const searchQuery = { allowNoIndices: true, @@ -112,7 +112,7 @@ export const buildEventsSearchQuery = ({ ...(aggregations ? { aggregations } : {}), sort: [ { - [timestamp]: { + [sortField]: { order: sortOrder ?? 'asc', }, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts index 7141b61a23e6e8..239edcd1f18451 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/find_threshold_signals.ts @@ -85,5 +85,6 @@ export const findThresholdSignals = async ({ pageSize: 1, sortOrder: 'desc', buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); }; 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 67246a830ce90a..caac728f0a136c 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 @@ -183,31 +183,6 @@ describe('searchAfterAndBulkCreate', () => { }, ], }) - .mockResolvedValueOnce(sampleDocSearchResultsNoSortIdNoHits()) - .mockResolvedValueOnce(repeatedSearchResultsWithSortId(4, 1, someGuids.slice(9, 12))) - .mockResolvedValueOnce({ - took: 100, - errors: false, - items: [ - { - create: { - status: 201, - }, - }, - ], - }) - .mockResolvedValueOnce(repeatedSearchResultsWithSortId(4, 1, someGuids.slice(0, 3))) - .mockResolvedValueOnce({ - took: 100, - errors: false, - items: [ - { - create: { - status: 201, - }, - }, - ], - }) .mockResolvedValueOnce(sampleDocSearchResultsNoSortIdNoHits()); const exceptionItem = getExceptionListItemSchemaMock(); @@ -250,8 +225,8 @@ describe('searchAfterAndBulkCreate', () => { buildRuleMessage, }); expect(success).toEqual(true); - expect(mockService.callCluster).toHaveBeenCalledTimes(12); - expect(createdSignalsCount).toEqual(5); + expect(mockService.callCluster).toHaveBeenCalledTimes(8); + expect(createdSignalsCount).toEqual(3); expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); @@ -461,7 +436,7 @@ describe('searchAfterAndBulkCreate', () => { // 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[8][0]).toContain( - 'sortIds was empty on searchResult' + 'ran out of sort ids to sort on name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' ); }); @@ -542,7 +517,7 @@ describe('searchAfterAndBulkCreate', () => { // 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[15][0]).toContain( - 'sortIds was empty on searchResult name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' + 'ran out of sort ids to sort on name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' ); }); 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 b79f758cd7503f..fa47ef25a2db07 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 @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable complexity */ import { singleSearchAfter } from './single_search_after'; import { singleBulkCreate } from './single_bulk_create'; @@ -10,10 +11,12 @@ import { filterEventsAgainstList } from './filters/filter_events_against_list'; import { sendAlertTelemetryEvents } from './send_telemetry_events'; import { createSearchAfterReturnType, + createSearchResultReturnType, createSearchAfterReturnTypeFromResponse, createTotalHitsFromSearchResult, getSignalTimeTuples, mergeReturns, + mergeSearchResults, } from './utils'; import { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } from './types'; @@ -49,6 +52,9 @@ export const searchAfterAndBulkCreate = async ({ // sortId tells us where to start our next consecutive search_after query let sortId: string | undefined; + let hasSortId = true; // default to true so we execute the search on initial run + let backupSortId: string | undefined; + let hasBackupSortId = ruleParams.timestampOverride ? true : false; // signalsCreatedCount keeps track of how many signals we have created, // to ensure we don't exceed maxSignals @@ -78,10 +84,11 @@ export const searchAfterAndBulkCreate = async ({ signalsCreatedCount = 0; while (signalsCreatedCount < tuple.maxSignals) { try { + let mergedSearchResults = createSearchResultReturnType(); logger.debug(buildRuleMessage(`sortIds: ${sortId}`)); // perform search_after with optionally undefined sortId - const { searchResult, searchDuration, searchErrors } = await singleSearchAfter({ + const singleSearchAfterPromise = singleSearchAfter({ buildRuleMessage, searchAfterSortId: sortId, index: inputIndexPattern, @@ -92,23 +99,92 @@ export const searchAfterAndBulkCreate = async ({ filter, pageSize: tuple.maxSignals < pageSize ? Math.ceil(tuple.maxSignals) : pageSize, // maximum number of docs to receive per search result. timestampOverride: ruleParams.timestampOverride, + excludeDocsWithTimestampOverride: false, }); - toReturn = mergeReturns([ - toReturn, - createSearchAfterReturnTypeFromResponse({ - searchResult, + + // if there is a timestampOverride param we always want to do a secondary search against @timestamp + if (ruleParams.timestampOverride != null && hasBackupSortId) { + // only execute search if we have something to sort on or if it is the first search + const singleSearchAfterDefaultTimestamp = singleSearchAfter({ + buildRuleMessage, + searchAfterSortId: backupSortId, + index: inputIndexPattern, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + services, + logger, + filter, + pageSize: tuple.maxSignals < pageSize ? Math.ceil(tuple.maxSignals) : pageSize, // maximum number of docs to receive per search result. timestampOverride: ruleParams.timestampOverride, - }), - createSearchAfterReturnType({ - searchAfterTimes: [searchDuration], - errors: searchErrors, - }), - ]); + excludeDocsWithTimestampOverride: true, + }); + const { + searchResult: searchResultB, + searchDuration: searchDurationB, + searchErrors: searchErrorsB, + } = await singleSearchAfterDefaultTimestamp; + + // call this function setSortIdOrExit() + const lastSortId = searchResultB?.hits?.hits[searchResultB.hits.hits.length - 1]?.sort; + if (lastSortId != null && lastSortId.length !== 0) { + backupSortId = lastSortId[0]; + hasBackupSortId = true; + } else { + // if no sort id on backup search and the initial search result was also empty + logger.debug(buildRuleMessage('backupSortIds was empty on searchResultB')); + hasBackupSortId = false; + } + + mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResultB]); + + // merge the search result from the secondary search with the first + toReturn = mergeReturns([ + toReturn, + createSearchAfterReturnTypeFromResponse({ + searchResult: mergedSearchResults, + timestampOverride: undefined, + }), + createSearchAfterReturnType({ + searchAfterTimes: [searchDurationB], + errors: searchErrorsB, + }), + ]); + } + + if (hasSortId) { + // only execute search if we have something to sort on or if it is the first search + const { searchResult, searchDuration, searchErrors } = await singleSearchAfterPromise; + mergedSearchResults = mergeSearchResults([mergedSearchResults, searchResult]); + toReturn = mergeReturns([ + toReturn, + createSearchAfterReturnTypeFromResponse({ + searchResult: mergedSearchResults, + timestampOverride: ruleParams.timestampOverride, + }), + createSearchAfterReturnType({ + searchAfterTimes: [searchDuration], + errors: searchErrors, + }), + ]); + + // we are guaranteed to have searchResult hits at this point + // because we check before if the totalHits or + // searchResult.hits.hits.length is 0 + // call this function setSortIdOrExit() + const lastSortId = searchResult.hits.hits[searchResult.hits.hits.length - 1]?.sort; + if (lastSortId != null && lastSortId.length !== 0) { + sortId = lastSortId[0]; + hasSortId = true; + } else { + hasSortId = false; + } + } + // determine if there are any candidate signals to be processed - const totalHits = createTotalHitsFromSearchResult({ searchResult }); + const totalHits = createTotalHitsFromSearchResult({ searchResult: mergedSearchResults }); logger.debug(buildRuleMessage(`totalHits: ${totalHits}`)); logger.debug( - buildRuleMessage(`searchResult.hit.hits.length: ${searchResult.hits.hits.length}`) + buildRuleMessage(`searchResult.hit.hits.length: ${mergedSearchResults.hits.hits.length}`) ); // search results yielded zero hits so exit @@ -119,7 +195,7 @@ export const searchAfterAndBulkCreate = async ({ // e.g. totalHits was 156, index 50 of 100 results, do another search-after // this time with a new sortId, index 22 of the remaining 56, get another sortId // search with that sortId, total is still 156 but the hits.hits array is empty. - if (totalHits === 0 || searchResult.hits.hits.length === 0) { + if (totalHits === 0 || mergedSearchResults.hits.hits.length === 0) { logger.debug( buildRuleMessage( `${ @@ -137,7 +213,7 @@ export const searchAfterAndBulkCreate = async ({ listClient, exceptionsList, logger, - eventSearchResult: searchResult, + eventSearchResult: mergedSearchResults, buildRuleMessage, }); @@ -205,14 +281,8 @@ export const searchAfterAndBulkCreate = async ({ ); } - // we are guaranteed to have searchResult hits at this point - // because we check before if the totalHits or - // searchResult.hits.hits.length is 0 - const lastSortId = searchResult.hits.hits[searchResult.hits.hits.length - 1].sort; - if (lastSortId != null && lastSortId.length !== 0) { - sortId = lastSortId[0]; - } else { - logger.debug(buildRuleMessage('sortIds was empty on searchResult')); + if (!hasSortId && !hasBackupSortId) { + logger.debug(buildRuleMessage('ran out of sort ids to sort on')); break; } } catch (exc: unknown) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts index c4869f024a9777..12d91dcde2ff76 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts @@ -40,6 +40,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchResult).toEqual(sampleDocSearchResultsNoSortId()); }); @@ -56,6 +57,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchErrors).toEqual([]); }); @@ -104,9 +106,10 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', ]); }); test('if singleSearchAfter works with a given sort id', async () => { @@ -123,6 +126,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); expect(searchResult).toEqual(sampleDocSearchResultsWithSortId()); }); @@ -143,6 +147,7 @@ describe('singleSearchAfter', () => { filter: undefined, timestampOverride: undefined, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }) ).rejects.toThrow('Fake Error'); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts index 23ef9fcea8e530..79e1f9896d63f4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts @@ -29,6 +29,7 @@ interface SingleSearchAfterParams { filter: unknown; timestampOverride: TimestampOverrideOrUndefined; buildRuleMessage: BuildRuleMessage; + excludeDocsWithTimestampOverride: boolean; } // utilize search_after for paging results into bulk. @@ -45,6 +46,7 @@ export const singleSearchAfter = async ({ sortOrder, timestampOverride, buildRuleMessage, + excludeDocsWithTimestampOverride, }: SingleSearchAfterParams): Promise<{ searchResult: SignalSearchResponse; searchDuration: string; @@ -61,6 +63,7 @@ export const singleSearchAfter = async ({ sortOrder, searchAfterSortId, timestampOverride, + excludeDocsWithTimestampOverride, }); const start = performance.now(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts index 960693bc703d67..6e7f63deb06f77 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold_find_previous_signals.ts @@ -83,5 +83,6 @@ export const findPreviousThresholdSignals = async ({ filter, pageSize: 0, buildRuleMessage, + excludeDocsWithTimestampOverride: false, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 073e30bbc6e26b..b410fb7c35be02 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -879,7 +879,7 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', ]); }); @@ -918,8 +918,8 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', - 'reason: "some reason 2" type: "some type 2" caused by reason: "some reason 2" caused by type: "some type 2"', + 'index: "index-123" reason: "some reason" type: "some type" caused by reason: "some reason" caused by type: "some type"', + 'index: "index-345" reason: "some reason 2" type: "some type 2" caused by reason: "some reason 2" caused by type: "some type 2"', ]); }); @@ -933,7 +933,7 @@ describe('utils', () => { }, ]; const createdErrors = createErrorsFromShard({ errors }); - expect(createdErrors).toEqual(['']); + expect(createdErrors).toEqual(['index: "index-123"']); }); test('You can have a single value for the shard errors and get expected output without extra spaces anywhere', () => { @@ -948,7 +948,9 @@ describe('utils', () => { }, ]; const createdErrors = createErrorsFromShard({ errors }); - expect(createdErrors).toEqual(['reason: "some reason something went wrong"']); + expect(createdErrors).toEqual([ + 'index: "index-123" reason: "some reason something went wrong"', + ]); }); test('You can have two values for the shard errors and get expected output with one space exactly between the two values', () => { @@ -965,7 +967,7 @@ describe('utils', () => { ]; const createdErrors = createErrorsFromShard({ errors }); expect(createdErrors).toEqual([ - 'reason: "some reason something went wrong" caused by type: "some type"', + 'index: "index-123" reason: "some reason something went wrong" caused by type: "some type"', ]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 92a27319723d6c..ab14643f30e41c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -530,6 +530,7 @@ export const getSignalTimeTuples = ({ export const createErrorsFromShard = ({ errors }: { errors: ShardError[] }): string[] => { return errors.map((error) => { const { + index, reason: { reason, type, @@ -541,6 +542,7 @@ export const createErrorsFromShard = ({ errors }: { errors: ShardError[] }): str } = error; return [ + ...(index != null ? [`index: "${index}"`] : []), ...(reason != null ? [`reason: "${reason}"`] : []), ...(type != null ? [`type: "${type}"`] : []), ...(causedByReason != null ? [`caused by reason: "${causedByReason}"`] : []), @@ -629,6 +631,25 @@ export const createSearchAfterReturnType = ({ }; }; +export const createSearchResultReturnType = (): SignalSearchResponse => { + return { + took: 0, + timed_out: false, + _shards: { + total: 0, + successful: 0, + failed: 0, + skipped: 0, + failures: [], + }, + hits: { + total: 0, + max_score: 0, + hits: [], + }, + }; +}; + export const mergeReturns = ( searchAfters: SearchAfterAndBulkCreateReturnType[] ): SearchAfterAndBulkCreateReturnType => { @@ -665,6 +686,52 @@ export const mergeReturns = ( }); }; +export const mergeSearchResults = (searchResults: SignalSearchResponse[]) => { + return searchResults.reduce((prev, next) => { + const { + took: existingTook, + timed_out: existingTimedOut, + // _scroll_id: existingScrollId, + _shards: existingShards, + // aggregations: existingAggregations, + hits: existingHits, + } = prev; + + const { + took: newTook, + timed_out: newTimedOut, + _scroll_id: newScrollId, + _shards: newShards, + aggregations: newAggregations, + hits: newHits, + } = next; + + return { + took: Math.max(newTook, existingTook), + timed_out: newTimedOut && existingTimedOut, + _scroll_id: newScrollId, + _shards: { + total: newShards.total + existingShards.total, + successful: newShards.successful + existingShards.successful, + failed: newShards.failed + existingShards.failed, + skipped: newShards.skipped + existingShards.skipped, + failures: [ + ...(existingShards.failures != null ? existingShards.failures : []), + ...(newShards.failures != null ? newShards.failures : []), + ], + }, + aggregations: newAggregations, + hits: { + total: + createTotalHitsFromSearchResult({ searchResult: prev }) + + createTotalHitsFromSearchResult({ searchResult: next }), + max_score: Math.max(newHits.max_score, existingHits.max_score), + hits: [...existingHits.hits, ...newHits.hits], + }, + }; + }); +}; + export const createTotalHitsFromSearchResult = ({ searchResult, }: { diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts index fe80402b607312..785b74d3342761 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/find_statuses.ts @@ -15,7 +15,7 @@ import { deleteAllRulesStatuses, getSimpleRule, createRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -47,7 +47,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a single rule status when a single rule is loaded from a find status with defaults added', async () => { const resBody = await createRule(supertest, getSimpleRule('rule-1', true)); - await waitForRuleSuccess(supertest, resBody.id); + await waitForRuleSuccessOrStatus(supertest, resBody.id); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts index f8a25b0081ef96..2e00be6f77061e 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/open_close_signals.ts @@ -23,7 +23,7 @@ import { createRule, waitForSignalsToBePresent, getSignalsByIds, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, getRuleForSignalTesting, } from '../../utils'; @@ -79,7 +79,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to execute and get 10 signals', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -88,7 +88,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be have set the signals in an open state initially', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const everySignalOpen = signalsOpen.hits.hits.every( @@ -104,7 +104,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to get a count of 10 closed signals when closing 10', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able close 10 signals immediately and they all should be closed', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts index bbd85e353e0955..a2c3fc6c6c288b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/add_actions.ts @@ -17,7 +17,7 @@ import { getWebHookAction, getRuleWithWebHookAction, getSimpleRuleOutputWithWebHookAction, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, createRule, } from '../../utils'; @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); const rule = await createRule(supertest, getRuleWithWebHookAction(hookAction.id, true)); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); // expected result for status should be 'succeeded' const { body } = await supertest @@ -86,7 +86,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, ruleWithAction); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); // expected result for status should be 'succeeded' const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts index 7e4a6ad86cda5c..b90bea66be11ff 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts @@ -26,7 +26,7 @@ import { removeServerGeneratedProperties, downgradeImmutableRule, createRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, installPrePackagedRules, getRule, createExceptionList, @@ -113,7 +113,7 @@ export default ({ getService }: FtrProviderContext) => { }; const rule = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); const bodyToCompare = removeServerGeneratedProperties(rule); const expected: Partial = { @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { ], }; const { id: createdId } = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 10, [createdId]); const signalsOpen = await getSignalsByIds(supertest, [createdId]); expect(signalsOpen.hits.hits.length).equal(10); @@ -490,7 +490,7 @@ export default ({ getService }: FtrProviderContext) => { ], }; const rule = await createRule(supertest, ruleWithException); - await waitForRuleSuccess(supertest, rule.id); + await waitForRuleSuccessOrStatus(supertest, rule.id); const signalsOpen = await getSignalsByIds(supertest, [rule.id]); expect(signalsOpen.hits.hits.length).equal(0); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts index 0da12ebba055a0..0cde7bf9a22fc5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules.ts @@ -24,13 +24,16 @@ import { removeServerGeneratedPropertiesIncludingRuleId, getSimpleMlRule, getSimpleMlRuleOutput, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, + waitForSignalsToBePresent, getRuleForSignalTesting, + getRuleForSignalTestingWithTimestampOverride, } from '../../utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); describe('create_rules', () => { describe('validation errors', () => { @@ -97,7 +100,7 @@ export default ({ getService }: FtrProviderContext) => { .send(simpleRule) .expect(200); - await waitForRuleSuccess(supertest, body.id); + await waitForRuleSuccessOrStatus(supertest, body.id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) @@ -201,5 +204,46 @@ export default ({ getService }: FtrProviderContext) => { }); }); }); + describe('missing timestamps', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + // to edit these files run the following script + // cd $HOME/kibana/x-pack && nvm use && node ../scripts/es_archiver edit security_solution/timestamp_override + await esArchiver.load('security_solution/timestamp_override'); + }); + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(supertest); + await esArchiver.unload('security_solution/timestamp_override'); + }); + it('should create a single rule which has a timestamp override and generates two signals with a failing status', async () => { + // should be a failing status because one of the indices in the index pattern is missing + // the timestamp override field. + + // defaults to event.ingested timestamp override. + // event.ingested is one of the timestamp fields set on the es archive data + // inside of x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz + const simpleRule = getRuleForSignalTestingWithTimestampOverride(['myfa*']); + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(simpleRule) + .expect(200); + const bodyId = body.id; + + await waitForRuleSuccessOrStatus(supertest, bodyId, 'failed'); + await waitForSignalsToBePresent(supertest, 2, [bodyId]); + + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [bodyId] }) + .expect(200); + + // set to "failed" for now. Will update this with a partial failure + // once I figure out the logic + expect(statusBody[bodyId].current_status.status).to.eql('failed'); + }); + }); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts index 7ea47312a50302..2577c6b1636045 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts @@ -22,7 +22,7 @@ import { getSimpleRuleWithoutRuleId, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, } from '../../utils'; // eslint-disable-next-line import/no-default-export @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext): void => { .send([simpleRule]) .expect(200); - await waitForRuleSuccess(supertest, body[0].id); + await waitForRuleSuccessOrStatus(supertest, body[0].id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index 21cfab3db6d6a3..1f7deddbd5e76c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -19,7 +19,7 @@ import { deleteSignalsIndex, getSignalsByIds, removeServerGeneratedProperties, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../utils'; @@ -72,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => { supertest, getCreateThreatMatchRulesSchemaMock('rule-1', true) ); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const { body: statusBody } = await supertest .post(DETECTION_ENGINE_RULES_STATUS_URL) @@ -128,7 +128,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -163,7 +163,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); @@ -201,7 +201,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); @@ -239,7 +239,7 @@ export default ({ getService }: FtrProviderContext) => { }; const ruleResponse = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, ruleResponse.id); + await waitForRuleSuccessOrStatus(supertest, ruleResponse.id); const signalsOpen = await getSignalsByIds(supertest, [ruleResponse.id]); expect(signalsOpen.hits.hits.length).equal(0); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts index 09cc470defa081..4271ce9b37ebb9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the dates from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['date']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -183,7 +183,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -203,7 +203,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -221,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -248,7 +248,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -291,7 +291,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -314,7 +314,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -358,7 +358,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -396,7 +396,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -415,7 +415,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -445,7 +445,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -477,7 +477,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -510,7 +510,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); expect(hits).to.eql([]); @@ -534,7 +534,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -562,7 +562,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); @@ -595,7 +595,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.date).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts index a5793489cd8d02..158e17299fe9fa 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the double from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['double']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); expect(hits).to.eql([]); @@ -562,7 +562,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -589,7 +589,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -612,7 +612,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -635,7 +635,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -660,7 +660,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -683,7 +683,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -706,7 +706,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); @@ -732,7 +732,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.double).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts index 955d27c086466f..0bea2d73151f23 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the float from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['float']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.float).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts index a1275afe288bf1..600c1a609a6945 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the integer from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['integer']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.integer).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts index 311354c63ca4a0..bcdebed3dd45b9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the ips from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['ip']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -174,7 +174,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -192,7 +192,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -213,7 +213,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -231,7 +231,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -258,7 +258,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -278,7 +278,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -297,7 +297,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -355,7 +355,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -373,7 +373,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -412,7 +412,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -437,7 +437,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -460,7 +460,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -488,7 +488,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -541,7 +541,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -570,7 +570,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -595,7 +595,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -618,7 +618,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -646,7 +646,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -673,7 +673,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -700,7 +700,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 8f4827ec6e71c2..9d6f1f2fb297a8 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the ips from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['ip_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -159,7 +159,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -182,7 +182,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -203,7 +203,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -221,7 +221,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -246,7 +246,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -273,7 +273,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -293,7 +293,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([]); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -397,7 +397,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([[]]); @@ -416,7 +416,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -445,7 +445,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -472,7 +472,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -500,7 +500,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); expect(ips).to.eql([[]]); @@ -536,7 +536,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -567,7 +567,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -592,7 +592,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -615,7 +615,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -646,7 +646,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -687,7 +687,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); @@ -721,7 +721,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source.ip).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts index e4e80cb1b65ea4..a0183ad794a2f0 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the keyword from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['keyword']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -69,7 +69,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -131,7 +131,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -174,7 +174,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -194,7 +194,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -212,7 +212,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -239,7 +239,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -259,7 +259,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -278,7 +278,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -297,7 +297,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -336,7 +336,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -354,7 +354,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -428,7 +428,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -451,7 +451,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -474,7 +474,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -502,7 +502,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -526,7 +526,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -549,7 +549,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -577,7 +577,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts index 01e301c350851b..81ea04de5def08 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the keyword from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['keyword_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -179,7 +179,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -206,7 +206,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -226,7 +226,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -249,7 +249,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -289,7 +289,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([]); @@ -307,7 +307,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -330,7 +330,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([[]]); @@ -349,7 +349,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -388,7 +388,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -426,7 +426,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -453,7 +453,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -480,7 +480,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); expect(hits).to.eql([[]]); @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -555,7 +555,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -578,7 +578,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.keyword).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts index ee52c41bc78e87..56667dbca925e6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -52,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the long from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['long']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -98,7 +98,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -133,7 +133,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -176,7 +176,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -241,7 +241,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -261,7 +261,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -280,7 +280,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -299,7 +299,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -318,7 +318,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -338,7 +338,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -356,7 +356,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -376,7 +376,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -395,7 +395,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -421,7 +421,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -444,7 +444,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -467,7 +467,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -491,7 +491,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -514,7 +514,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -537,7 +537,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); expect(hits).to.eql([]); @@ -559,7 +559,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -586,7 +586,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -632,7 +632,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -657,7 +657,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -680,7 +680,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -703,7 +703,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); @@ -726,7 +726,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.long).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 095d8851493899..74507fc030e686 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -22,7 +22,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -53,7 +53,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the text from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -72,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -99,7 +99,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -134,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -177,7 +177,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -195,7 +195,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -214,7 +214,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -232,7 +232,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -253,7 +253,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -271,7 +271,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -298,7 +298,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -316,7 +316,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -335,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql(['word four', 'word one', 'word three', 'word two']); @@ -353,7 +353,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -374,7 +374,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -393,7 +393,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -412,7 +412,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -431,7 +431,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -451,7 +451,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -469,7 +469,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -489,7 +489,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -534,7 +534,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -557,7 +557,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -585,7 +585,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -609,7 +609,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -637,7 +637,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -660,7 +660,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -688,7 +688,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -714,7 +714,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -737,7 +737,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -765,7 +765,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -790,7 +790,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -818,7 +818,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -841,7 +841,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -869,7 +869,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index ed63f1a0db25f5..9a77cee6be1eb9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -21,7 +21,7 @@ import { deleteSignalsIndex, getRuleForSignalTesting, getSignalsById, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../../utils'; @@ -50,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { it('should find all the text from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['text_as_array']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -74,7 +74,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -140,7 +140,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -161,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -179,7 +179,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -206,7 +206,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -226,7 +226,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -249,7 +249,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -268,7 +268,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -289,7 +289,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([]); @@ -307,7 +307,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -330,7 +330,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([[]]); @@ -349,7 +349,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -388,7 +388,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -426,7 +426,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -453,7 +453,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -480,7 +480,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); expect(hits).to.eql([[]]); @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -555,7 +555,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -578,7 +578,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); @@ -604,7 +604,7 @@ export default ({ getService }: FtrProviderContext) => { }, ], ]); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source.text).sort(); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts index 8bb4c45d91bdd6..dfec35e4a64f3b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/find_statuses.ts @@ -14,7 +14,7 @@ import { deleteSignalsIndex, deleteAllRulesStatuses, getSimpleRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, createRule, } from '../../utils'; @@ -66,7 +66,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a single rule status when a single rule is loaded from a find status with defaults added', async () => { const resBody = await createRule(supertest, getSimpleRule('rule-1', true)); - await waitForRuleSuccess(supertest, resBody.id); + await waitForRuleSuccessOrStatus(supertest, resBody.id); // query the single rule from _find const { body } = await supertest diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index e3264786ff38bb..34f70743265503 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -22,7 +22,7 @@ import { getSignalsByIds, getSignalsByRuleIds, getSimpleRule, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, waitForSignalsToBePresent, } from '../../utils'; import { SIGNALS_TEMPLATE_VERSION } from '../../../../plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template'; @@ -63,7 +63,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -75,7 +75,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -87,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -136,7 +136,7 @@ export default ({ getService }: FtrProviderContext) => { query: `_id:${ID}`, }; const { id: createdId } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -146,7 +146,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Get our single signal on top of a signal @@ -212,7 +212,7 @@ export default ({ getService }: FtrProviderContext) => { query: 'sequence by host.name [any where true] [any where true]', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signals = await getSignalsByRuleIds(supertest, ['eql-rule']); const signal = signals.hits.hits[0]._source.signal; @@ -267,7 +267,7 @@ export default ({ getService }: FtrProviderContext) => { query: 'sequence by host.name [any where true] [any where true]', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByRuleIds(supertest, ['eql-rule']); const sequenceSignal = signalsOpen.hits.hits.find( @@ -355,7 +355,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -368,7 +368,7 @@ export default ({ getService }: FtrProviderContext) => { }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -380,7 +380,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -424,7 +424,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -433,7 +433,7 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'signal-on-signal', }; const { id: createdId } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Get our single signal on top of a signal @@ -508,7 +508,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).greaterThan(0); @@ -520,7 +520,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits[0]._source.signal.rule.rule_id).eql(getSimpleRule().rule_id); @@ -532,7 +532,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); // remove rule to cut down on touch points for test changes when the rule format changes @@ -582,7 +582,7 @@ export default ({ getService }: FtrProviderContext) => { query: '_id:1', }; const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); // Run signals on top of that 1 signal which should create a single signal (on top of) a signal @@ -591,7 +591,7 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'signal-on-signal', }; const { id: createdId } = await createRule(supertest, ruleForSignals); - await waitForRuleSuccess(supertest, createdId); + await waitForRuleSuccessOrStatus(supertest, createdId); await waitForSignalsToBePresent(supertest, 1, [createdId]); // Get our single signal on top of a signal @@ -661,7 +661,7 @@ export default ({ getService }: FtrProviderContext) => { const executeRuleAndGetSignals = async (rule: QueryCreateSchema) => { const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsResponse = await getSignalsByIds(supertest, [id]); const signals = signalsResponse.hits.hits.map((hit) => hit._source); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts index 87e3b145ed6fd6..ee787f1b616e3d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts @@ -23,7 +23,7 @@ import { createRule, waitForSignalsToBePresent, getSignalsByIds, - waitForRuleSuccess, + waitForRuleSuccessOrStatus, getRuleForSignalTesting, } from '../../utils'; import { createUserAndRole } from '../roles_users_utils'; @@ -82,7 +82,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to execute and get 10 signals', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); expect(signalsOpen.hits.hits.length).equal(10); @@ -91,7 +91,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be have set the signals in an open state initially', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const everySignalOpen = signalsOpen.hits.hits.every( @@ -107,7 +107,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to get a count of 10 closed signals when closing 10', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 10, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -134,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able close signals immediately and they all should be closed', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsByIds(supertest, [id]); const signalIds = signalsOpen.hits.hits.map((signal) => signal._id); @@ -169,7 +169,7 @@ export default ({ getService }: FtrProviderContext) => { it('should NOT be able to close signals with t1 analyst user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); await createUserAndRole(securityService, ROLES.t1_analyst); const signalsOpen = await getSignalsByIds(supertest, [id]); @@ -207,7 +207,7 @@ export default ({ getService }: FtrProviderContext) => { it('should be able to close signals with soc_manager user', async () => { const rule = getRuleForSignalTesting(['auditbeat-*']); const { id } = await createRule(supertest, rule); - await waitForRuleSuccess(supertest, id); + await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); const userAndRole = ROLES.soc_manager; await createUserAndRole(securityService, userAndRole); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 5a36b950b6a5b3..9cff40758bd495 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -119,6 +119,25 @@ export const getRuleForSignalTesting = ( from: '1900-01-01T00:00:00.000Z', }); +export const getRuleForSignalTestingWithTimestampOverride = ( + index: string[], + ruleId = 'rule-1', + enabled = true, + timestampOverride = 'event.ingested' +): QueryCreateSchema => ({ + name: 'Signal Testing Query', + description: 'Tests a simple query', + enabled, + risk_score: 1, + rule_id: ruleId, + severity: 'high', + index, + type: 'query', + query: '*:*', + timestamp_override: timestampOverride, + from: '1900-01-01T00:00:00.000Z', +}); + /** * This is a typical simple rule for testing that is easy for most basic testing * @param ruleId The rule id @@ -864,21 +883,22 @@ export const getRule = async ( }; /** - * Waits for the rule in find status to be succeeded before continuing + * Waits for the rule in find status to be 'succeeded' + * or the provided status, before continuing * @param supertest Deps */ -export const waitForRuleSuccess = async ( +export const waitForRuleSuccessOrStatus = async ( supertest: SuperTest, - id: string + id: string, + status: 'succeeded' | 'failed' | 'partial failure' = 'succeeded' ): Promise => { - // wait for Task Manager to finish executing the rule await waitFor(async () => { const { body } = await supertest .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) .set('kbn-xsrf', 'true') .send({ ids: [id] }) .expect(200); - return body[id]?.current_status?.status === 'succeeded'; + return body[id]?.current_status?.status === status; }, 'waitForRuleSuccess'); }; diff --git a/x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz b/x-pack/test/functional/es_archives/security_solution/timestamp_override/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..be351495c2f2e11b896d0b657c6285d0713cd418 GIT binary patch literal 217 zcmV;~04Dz*iwFP!000026Qz&M4uUWcgztTdre`s<1T>s{2v1%Nffb2h2^5r&zPoMZ zM^vKGTX*)G?o5j?CZfrK_?SdIBnvInL0W00Rf8Ina|BlnWX&Nsff+4oP_-?2RfHq0 zlnlx;h|QNrNK=k4yhtNVi2-Ei>#y$hStUs%5o)KqKG932xm2vf-{qQ5Ho6o8HJru7 zW{$;B3W&6m+03>mv#7VFu1`Imu9xo0-jTr|yO$ioJeR#QsxZd?R(5`>>^xT9h(uu` T$ntOceQb3Dk3QF;RRRD2u~uS3 literal 0 HcmV?d00001 diff --git a/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json b/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json new file mode 100644 index 00000000000000..28de7eeb2eb01d --- /dev/null +++ b/x-pack/test/functional/es_archives/security_solution/timestamp_override/mappings.json @@ -0,0 +1,19 @@ +{ + "type": "index", + "value": { + "index": "myfakeindex-1", + "mappings" : { + "properties" : { + "message" : { + "type" : "text", + "fields" : { + "keyword" : { + "type" : "keyword", + "ignore_above" : 256 + } + } + } + } + } + } +} \ No newline at end of file From 25bf06c57358e7392777ae33952339aea4af4ad1 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Thu, 24 Dec 2020 11:01:01 +0300 Subject: [PATCH 087/100] Add telemetry to vis_type_xy plugin (#86751) * Add telemetry for detailedTooltip and fittingFunction * Fixed type problems Co-authored-by: nickofthyme Co-authored-by: Ryan Keairns Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: Joe Reuter Co-authored-by: Stratoula Kalafateli --- src/plugins/vis_type_xy/kibana.json | 2 +- .../point_series/elastic_charts_options.tsx | 17 +++++++++++++++-- src/plugins/vis_type_xy/public/plugin.ts | 7 ++++++- src/plugins/vis_type_xy/public/services.ts | 5 +++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/plugins/vis_type_xy/kibana.json b/src/plugins/vis_type_xy/kibana.json index 14c3ce36bf375b..619fa8e71c0dde 100644 --- a/src/plugins/vis_type_xy/kibana.json +++ b/src/plugins/vis_type_xy/kibana.json @@ -3,6 +3,6 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["charts", "data", "expressions", "visualizations"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"], "requiredBundles": ["kibanaUtils", "visDefaultEditor"] } diff --git a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx index f40972e86af6b0..a3e573741644cd 100644 --- a/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx +++ b/src/plugins/vis_type_xy/public/editor/components/options/point_series/elastic_charts_options.tsx @@ -20,14 +20,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { METRIC_TYPE } from '@kbn/analytics'; import { SelectOption, SwitchOption } from '../../../../../../vis_default_editor/public'; import { ChartType } from '../../../../../common'; import { VisParams } from '../../../../types'; import { ValidationVisOptionsProps } from '../../common'; +import { getTrackUiMetric } from '../../../../services'; export function ElasticChartsOptions(props: ValidationVisOptionsProps) { + const trackUiMetric = getTrackUiMetric(); const { stateParams, setValue, vis, aggs } = props; const hasLineChart = stateParams.seriesParams.some( @@ -49,7 +52,12 @@ export function ElasticChartsOptions(props: ValidationVisOptionsProps })} paramName="detailedTooltip" value={stateParams.detailedTooltip} - setValue={setValue} + setValue={(paramName, value) => { + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'detailed_tooltip_switched'); + } + setValue(paramName, value); + }} /> {hasLineChart && ( @@ -61,7 +69,12 @@ export function ElasticChartsOptions(props: ValidationVisOptionsProps options={vis.type.editorConfig.collections.fittingFunctions} paramName="fittingFunction" value={stateParams.fittingFunction} - setValue={setValue} + setValue={(paramName, value) => { + if (trackUiMetric) { + trackUiMetric(METRIC_TYPE.CLICK, 'fitting_function_selected'); + } + setValue(paramName, value); + }} /> )} diff --git a/src/plugins/vis_type_xy/public/plugin.ts b/src/plugins/vis_type_xy/public/plugin.ts index 07084de67fd219..ab22ae57ebbdf9 100644 --- a/src/plugins/vis_type_xy/public/plugin.ts +++ b/src/plugins/vis_type_xy/public/plugin.ts @@ -22,6 +22,7 @@ import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; import { ChartsPluginSetup } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; +import { UsageCollectionSetup } from '../../usage_collection/public'; import { createVisTypeXyVisFn } from './xy_vis_fn'; import { @@ -32,6 +33,7 @@ import { setTimefilter, setUISettings, setDocLinks, + setTrackUiMetric, } from './services'; import { visTypesDefinitions } from './vis_types'; import { LEGACY_CHARTS_LIBRARY } from '../common'; @@ -47,6 +49,7 @@ export interface VisTypeXyPluginSetupDependencies { expressions: ReturnType; visualizations: VisualizationsSetup; charts: ChartsPluginSetup; + usageCollection: UsageCollectionSetup; } /** @internal */ @@ -69,7 +72,7 @@ export class VisTypeXyPlugin > { public async setup( core: VisTypeXyCoreSetup, - { expressions, visualizations, charts }: VisTypeXyPluginSetupDependencies + { expressions, visualizations, charts, usageCollection }: VisTypeXyPluginSetupDependencies ) { if (!core.uiSettings.get(LEGACY_CHARTS_LIBRARY, false)) { setUISettings(core.uiSettings); @@ -81,6 +84,8 @@ export class VisTypeXyPlugin visTypesDefinitions.forEach(visualizations.createBaseVisualization); } + setTrackUiMetric(usageCollection?.reportUiCounter.bind(usageCollection, 'vis_type_xy')); + return {}; } diff --git a/src/plugins/vis_type_xy/public/services.ts b/src/plugins/vis_type_xy/public/services.ts index 5a72759ecff6cb..086cab8fb217aa 100644 --- a/src/plugins/vis_type_xy/public/services.ts +++ b/src/plugins/vis_type_xy/public/services.ts @@ -17,6 +17,7 @@ * under the License. */ +import { UiCounterMetricType } from '@kbn/analytics'; import { CoreSetup, DocLinksStart } from '../../../core/public'; import { createGetterSetter } from '../../kibana_utils/public'; import { DataPublicPluginStart } from '../../data/public'; @@ -47,3 +48,7 @@ export const [getColorsService, setColorsService] = createGetterSetter< >('xy charts.color'); export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks'); + +export const [getTrackUiMetric, setTrackUiMetric] = createGetterSetter< + (metricType: UiCounterMetricType, eventName: string | string[]) => void +>('trackUiMetric'); From 6b257bb6c3e395b93d6b8645899d4e99adbdb709 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Dec 2020 10:01:56 +0200 Subject: [PATCH 088/100] Update dependency vega to ^5.17.3 (#86891) Co-authored-by: Renovate Bot --- package.json | 2 +- yarn.lock | 76 ++++++++++++++++++++++++++-------------------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index 49787bb8ab34ed..61b13a06bffe9c 100644 --- a/package.json +++ b/package.json @@ -824,7 +824,7 @@ "url-loader": "^2.2.0", "use-resize-observer": "^6.0.0", "val-loader": "^1.1.1", - "vega": "^5.17.1", + "vega": "^5.17.3", "vega-lite": "^4.17.0", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.24.2", diff --git a/yarn.lock b/yarn.lock index 6fa3ea09731b32..956630bafa935a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28340,20 +28340,20 @@ vega-event-selector@^2.0.6, vega-event-selector@~2.0.6: resolved "https://registry.yarnpkg.com/vega-event-selector/-/vega-event-selector-2.0.6.tgz#6beb00e066b78371dde1a0f40cb5e0bbaecfd8bc" integrity sha512-UwCu50Sqd8kNZ1X/XgiAY+QAyQUmGFAwyDu7y0T5fs6/TPQnDo/Bo346NgSgINBEhEKOAMY1Nd/rPOk4UEm/ew== -vega-expression@^3.0.0, vega-expression@~3.0.0: +vega-expression@^4.0.0, vega-expression@^4.0.1, vega-expression@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-4.0.1.tgz#c03e4fc68a00acac49557faa4e4ed6ac8a59c5fd" + integrity sha512-ZrDj0hP8NmrCpdLFf7Rd/xMUHGoSYsAOTaYp7uXZ2dkEH5x0uPy5laECMc8TiQvL8W+8IrN2HAWCMRthTSRe2Q== + dependencies: + vega-util "^1.16.0" + +vega-expression@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-3.0.0.tgz#39179d010b34c57513162bf1ab5a7bff4b31be91" integrity sha512-/ObjIOK94MB+ziTuh8HZt2eWlKUPT/piRJLal5tx5QL1sQbfRi++7lHKTaKMLXLqc4Xqp9/DewE3PqQ6tYzaUA== dependencies: vega-util "^1.15.2" -vega-expression@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vega-expression/-/vega-expression-3.0.1.tgz#bbccd8f59371a537eab16f3d9eff5cbeaa27532d" - integrity sha512-+UwOFEkBnAWo8Zud6i8O4Pd2W6QqmPUOaAhjNtj0OxRL+d+Duoy7M4edUDZ+YuoUcMnjjBFfDQu7oRAA1fIMEQ== - dependencies: - vega-util "^1.15.2" - vega-force@~4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/vega-force/-/vega-force-4.0.7.tgz#6dc39ecb7889d9102661244d62fbc8d8714162ee" @@ -28374,22 +28374,22 @@ vega-format@^1.0.4, vega-format@~1.0.4: vega-time "^2.0.3" vega-util "^1.15.2" -vega-functions@^5.8.0, vega-functions@~5.8.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.8.0.tgz#48e02b0e5b14261cd445bda3c4721a18b02c810c" - integrity sha512-xaUqWZHEX+EuJuKfN0Biux3rrCHDEHmMbW7LHYyyEqguR0i6+zhtOSUEWmYqDfzB/+BlIwCk5Vif6q6/mzJxbQ== +vega-functions@^5.10.0, vega-functions@~5.10.0: + version "5.10.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.10.0.tgz#3d384111f13b3b0dd38a4fca656c5ae54b66e158" + integrity sha512-1l28OxUwOj8FEvRU62Oz2hiTuDECrvx1DPU1qLebBKhlgaKbcCk3XyHrn1kUzhMKpXq+SFv5VPxchZP47ASSvQ== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" d3-geo "^2.0.1" vega-dataflow "^5.7.3" - vega-expression "^3.0.0" + vega-expression "^4.0.1" vega-scale "^7.1.1" vega-scenegraph "^4.9.2" - vega-selections "^5.1.4" + vega-selections "^5.1.5" vega-statistics "^1.7.9" vega-time "^2.0.4" - vega-util "^1.15.2" + vega-util "^1.16.0" vega-geo@~4.3.8: version "4.3.8" @@ -28453,14 +28453,14 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0: vega-format "^1.0.4" vega-util "^1.16.0" -vega-parser@~6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.0.tgz#485fb6fcd79d14b09efee340e2b55fb510e57e20" - integrity sha512-u14bHXV8vtcuMIJkMNoDAJ4Xu3lwKIkep+YEkPumWvlwl3fClWy26EAcwTneeM3rXu2F6ZJI6W3ddu/If8u13w== +vega-parser@~6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.2.tgz#7f25751177e38c3239560a9c427ded8d2ba617bb" + integrity sha512-aGyZrNzPrBruEb/WhemKDuDjQsIkMDGIgnSJci0b+9ZVxjyAzMl7UfGbiYorPiJlnIercjUJbMoFD6fCIf4gqQ== dependencies: vega-dataflow "^5.7.3" vega-event-selector "^2.0.6" - vega-functions "^5.8.0" + vega-functions "^5.10.0" vega-scale "^7.1.1" vega-util "^1.15.2" @@ -28518,12 +28518,12 @@ vega-schema-url-parser@^2.1.0: resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.1.0.tgz#847f9cf9f1624f36f8a51abc1adb41ebc6673cb4" integrity sha512-JHT1PfOyVzOohj89uNunLPirs05Nf59isPT5gnwIkJph96rRgTIBJE7l7yLqndd7fLjr3P8JXHGAryRp74sCaQ== -vega-selections@^5.1.4: - version "5.1.4" - resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.1.4.tgz#cc086fac5b4e646f9f1e000777f8786782d8516a" - integrity sha512-L7CHwcIjVf90GoW2tS2x5O496O5Joaerp5A1KM6VJ1uo4z6KfqxY6M/328a/uaAs0LC5qbQgXT3htFbtUrPW/A== +vega-selections@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.1.5.tgz#c7662edf26c1cfb18623573b30590c9774348d1c" + integrity sha512-oRSsfkqYqA5xfEJqDpgnSDd+w0k6p6SGYisMD6rGXMxuPl0x0Uy6RvDr4nbEtB+dpWdoWEvgrsZVS6axyDNWvQ== dependencies: - vega-expression "^3.0.0" + vega-expression "^4.0.0" vega-util "^1.15.2" vega-spec-injector@^0.0.2: @@ -28586,16 +28586,16 @@ vega-view-transforms@~4.5.8: vega-scenegraph "^4.9.2" vega-util "^1.15.2" -vega-view@~5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.9.0.tgz#ee6d5abd66d2503dec71e05e7ca8cf813465ae3f" - integrity sha512-HqRFuqO2OwoPHHK+CVt8vB8fu2L8GjQerLpmEpglWtCPDns5+gn5B6F7M8Ah8v24WlfqW7cLrY81t9OloPZOyw== +vega-view@~5.9.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/vega-view/-/vega-view-5.9.2.tgz#cb957e481a952abbe7b3a11aa2d58cc728f295e7" + integrity sha512-XAwKWyVjLClR3aCbTLCWdZj7aZozOULNg7078GxJIgVcBJOENCAidceI/H7JieyUZ96p3AiEHLQdWr167InBpg== dependencies: d3-array "^2.7.1" d3-timer "^2.0.0" vega-dataflow "^5.7.3" vega-format "^1.0.4" - vega-functions "^5.8.0" + vega-functions "^5.10.0" vega-runtime "^6.1.3" vega-scenegraph "^4.9.2" vega-util "^1.15.2" @@ -28620,24 +28620,24 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.17.1: - version "5.17.1" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.1.tgz#ac95144b40137201b9d71a13615cc5b6eac6e5f7" - integrity sha512-ev1S6ohnsyeqps/bUVbhByoAbucap8vXPuiAJcxxft/EpgQGbIX/x42l0ijc3U1QHow2Lr3khtE1RshyU4lW2w== +vega@^5.17.3: + version "5.17.3" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.3.tgz#9901f24c8cf5ff2e98f3fddb372b8f5a6d8502d8" + integrity sha512-c8N2pNg9MMmC6shNpoxVw3aVp2XPFOgmWNX5BEOAdCaGHRnSgzNy44+gYdGRaIe6+ljTzZg99Mf+OLO50IP42A== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" vega-encode "~4.8.3" vega-event-selector "~2.0.6" - vega-expression "~3.0.1" + vega-expression "~4.0.1" vega-force "~4.0.7" vega-format "~1.0.4" - vega-functions "~5.8.0" + vega-functions "~5.10.0" vega-geo "~4.3.8" vega-hierarchy "~4.0.9" vega-label "~1.0.0" vega-loader "~4.4.0" - vega-parser "~6.1.0" + vega-parser "~6.1.2" vega-projection "~1.4.5" vega-regression "~1.0.9" vega-runtime "~6.1.3" @@ -28648,7 +28648,7 @@ vega@^5.17.1: vega-transforms "~4.9.3" vega-typings "~0.19.2" vega-util "~1.16.0" - vega-view "~5.9.0" + vega-view "~5.9.2" vega-view-transforms "~4.5.8" vega-voronoi "~4.1.5" vega-wordcloud "~4.1.3" From 6fc041c1d4a8bf9600977b089d1f4b1df40995e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Thu, 24 Dec 2020 09:36:58 +0100 Subject: [PATCH 089/100] filtering inventory page by transaction type (#86434) * filtering inventory page by transaction type * addressing pr comments * addressing pr comments * addressing pr comments * addressing pr comments * addressing pr comments * addressing pr comments * addressing pr comments * fixing test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ServiceList/__fixtures__/props.json | 34 -- .../__fixtures__/service_api_mock_data.ts | 29 ++ .../service_inventory/ServiceList/index.tsx | 401 ++++++++++------- .../ServiceList/service_list.test.tsx | 46 +- .../lib/helpers/transaction_error_rate.ts | 5 +- .../__snapshots__/queries.test.ts.snap | 306 +++---------- .../get_services/get_health_statuses.ts | 56 +++ .../get_service_transaction_stats.ts | 199 +++++++++ .../get_services/get_services_items.ts | 41 +- .../get_services/get_services_items_stats.ts | 413 ------------------ .../basic/tests/services/top_services.ts | 24 +- 11 files changed, 624 insertions(+), 930 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json create mode 100644 x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts create mode 100644 x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts create mode 100644 x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts delete mode 100644 x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json deleted file mode 100644 index 2e213c44bccf06..00000000000000 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/props.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "items": [ - { - "serviceName": "opbeans-node", - "agentName": "nodejs", - "transactionsPerMinute": { - "value": 0, - "timeseries": [] - }, - "errorsPerMinute": { - "value": 46.06666666666667, - "timeseries": [] - }, - "environments": ["test"] - }, - { - "serviceName": "opbeans-python", - "agentName": "python", - "transactionsPerMinute": { - "value": 86.93333333333334, - "timeseries": [] - }, - "errorsPerMinute": { - "value": 12.6, - "timeseries": [] - }, - "avgResponseTime": { - "value": 91535.42944785276, - "timeseries": [] - }, - "environments": [] - } - ] -} diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts new file mode 100644 index 00000000000000..04e1c9f8cbcabc --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/__fixtures__/service_api_mock_data.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; + +type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; + +export const items: ServiceListAPIResponse['items'] = [ + { + serviceName: 'opbeans-node', + transactionType: 'request', + agentName: 'nodejs', + transactionsPerMinute: { value: 0, timeseries: [] }, + transactionErrorRate: { value: 46.06666666666667, timeseries: [] }, + avgResponseTime: { value: null, timeseries: [] }, + environments: ['test'], + }, + { + serviceName: 'opbeans-python', + transactionType: 'page-load', + agentName: 'python', + transactionsPerMinute: { value: 86.93333333333334, timeseries: [] }, + transactionErrorRate: { value: 12.6, timeseries: [] }, + avgResponseTime: { value: 91535.42944785276, timeseries: [] }, + environments: [], + }, +]; diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx index 157d3ecc738a1a..27a2cf6418ece2 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx @@ -6,10 +6,16 @@ import { EuiFlexItem, EuiFlexGroup, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import { ValuesType } from 'utility-types'; import { orderBy } from 'lodash'; +import { EuiIcon } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; +import { + TRANSACTION_PAGE_LOAD, + TRANSACTION_REQUEST, +} from '../../../../../common/transaction_types'; import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { @@ -55,126 +61,6 @@ const ToolTipWrapper = styled.span` } `; -export const SERVICE_COLUMNS: Array> = [ - { - field: 'healthStatus', - name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { - defaultMessage: 'Health', - }), - width: px(unit * 6), - sortable: true, - render: (_, { healthStatus }) => { - return ( - - ); - }, - }, - { - field: 'serviceName', - name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', { - defaultMessage: 'Name', - }), - width: '40%', - sortable: true, - render: (_, { serviceName, agentName }) => ( - - - - {agentName && ( - - - - )} - - - {formatString(serviceName)} - - - - - - ), - }, - { - field: 'environments', - name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { - defaultMessage: 'Environment', - }), - width: px(unit * 10), - sortable: true, - render: (_, { environments }) => ( - - ), - }, - { - field: 'avgResponseTime', - name: i18n.translate('xpack.apm.servicesTable.avgResponseTimeColumnLabel', { - defaultMessage: 'Avg. response time', - }), - sortable: true, - dataType: 'number', - render: (_, { avgResponseTime }) => ( - - ), - align: 'left', - width: px(unit * 10), - }, - { - field: 'transactionsPerMinute', - name: i18n.translate( - 'xpack.apm.servicesTable.transactionsPerMinuteColumnLabel', - { - defaultMessage: 'Trans. per minute', - } - ), - sortable: true, - dataType: 'number', - render: (_, { transactionsPerMinute }) => ( - - ), - align: 'left', - width: px(unit * 10), - }, - { - field: 'transactionErrorRate', - name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { - defaultMessage: 'Error rate %', - }), - sortable: true, - dataType: 'number', - render: (_, { transactionErrorRate }) => { - const value = transactionErrorRate?.value; - - const valueLabel = asPercent(value, 1); - - return ( - - ); - }, - align: 'left', - width: px(unit * 10), - }, -]; - const SERVICE_HEALTH_STATUS_ORDER = [ ServiceHealthStatus.unknown, ServiceHealthStatus.healthy, @@ -182,59 +68,244 @@ const SERVICE_HEALTH_STATUS_ORDER = [ ServiceHealthStatus.critical, ]; +export function getServiceColumns({ + showTransactionTypeColumn, +}: { + showTransactionTypeColumn: boolean; +}): Array> { + return [ + { + field: 'healthStatus', + name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', { + defaultMessage: 'Health', + }), + width: px(unit * 6), + sortable: true, + render: (_, { healthStatus }) => { + return ( + + ); + }, + }, + { + field: 'serviceName', + name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', { + defaultMessage: 'Name', + }), + width: '40%', + sortable: true, + render: (_, { serviceName, agentName }) => ( + + + + {agentName && ( + + + + )} + + + {formatString(serviceName)} + + + + + + ), + }, + { + field: 'environments', + name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { + defaultMessage: 'Environment', + }), + width: px(unit * 10), + sortable: true, + render: (_, { environments }) => ( + + ), + }, + ...(showTransactionTypeColumn + ? [ + { + field: 'transactionType', + name: i18n.translate( + 'xpack.apm.servicesTable.transactionColumnLabel', + { + defaultMessage: 'Transaction type', + } + ), + width: px(unit * 10), + sortable: true, + }, + ] + : []), + { + field: 'avgResponseTime', + name: i18n.translate( + 'xpack.apm.servicesTable.avgResponseTimeColumnLabel', + { + defaultMessage: 'Avg. response time', + } + ), + sortable: true, + dataType: 'number', + render: (_, { avgResponseTime }) => ( + + ), + align: 'left', + width: px(unit * 10), + }, + { + field: 'transactionsPerMinute', + name: i18n.translate( + 'xpack.apm.servicesTable.transactionsPerMinuteColumnLabel', + { + defaultMessage: 'Trans. per minute', + } + ), + sortable: true, + dataType: 'number', + render: (_, { transactionsPerMinute }) => ( + + ), + align: 'left', + width: px(unit * 10), + }, + { + field: 'transactionErrorRate', + name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { + defaultMessage: 'Error rate %', + }), + sortable: true, + dataType: 'number', + render: (_, { transactionErrorRate }) => { + const value = transactionErrorRate?.value; + + const valueLabel = asPercent(value, 1); + + return ( + + ); + }, + align: 'left', + width: px(unit * 10), + }, + ]; +} + export function ServiceList({ items, noItemsMessage }: Props) { const displayHealthStatus = items.some((item) => 'healthStatus' in item); + const showTransactionTypeColumn = items.some( + ({ transactionType }) => + transactionType !== TRANSACTION_REQUEST && + transactionType !== TRANSACTION_PAGE_LOAD + ); + + const serviceColumns = useMemo( + () => getServiceColumns({ showTransactionTypeColumn }), + [showTransactionTypeColumn] + ); + const columns = displayHealthStatus - ? SERVICE_COLUMNS - : SERVICE_COLUMNS.filter((column) => column.field !== 'healthStatus'); + ? serviceColumns + : serviceColumns.filter((column) => column.field !== 'healthStatus'); const initialSortField = displayHealthStatus ? 'healthStatus' : 'transactionsPerMinute'; return ( - { - // For healthStatus, sort items by healthStatus first, then by TPM - return sortField === 'healthStatus' - ? orderBy( - itemsToSort, - [ - (item) => { - return item.healthStatus - ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) - : -1; - }, - (item) => item.transactionsPerMinute?.value ?? 0, - ], - [sortDirection, sortDirection] - ) - : orderBy( - itemsToSort, - (item) => { - switch (sortField) { - // Use `?? -1` here so `undefined` will appear after/before `0`. - // In the table this will make the "N/A" items always at the - // bottom/top. - case 'avgResponseTime': - return item.avgResponseTime?.value ?? -1; - case 'transactionsPerMinute': - return item.transactionsPerMinute?.value ?? -1; - case 'transactionErrorRate': - return item.transactionErrorRate?.value ?? -1; - default: - return item[sortField as keyof typeof item]; + + + + + + )} + > + + + + + + {i18n.translate( + 'xpack.apm.servicesTable.metricsExplanationLabel', + { defaultMessage: 'What are these metrics?' } + )} + + + + + + { + // For healthStatus, sort items by healthStatus first, then by TPM + return sortField === 'healthStatus' + ? orderBy( + itemsToSort, + [ + (item) => { + return item.healthStatus + ? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus) + : -1; + }, + (item) => item.transactionsPerMinute?.value ?? 0, + ], + [sortDirection, sortDirection] + ) + : orderBy( + itemsToSort, + (item) => { + switch (sortField) { + // Use `?? -1` here so `undefined` will appear after/before `0`. + // In the table this will make the "N/A" items always at the + // bottom/top. + case 'avgResponseTime': + return item.avgResponseTime?.value ?? -1; + case 'transactionsPerMinute': + return item.transactionsPerMinute?.value ?? -1; + case 'transactionErrorRate': + return item.transactionErrorRate?.value ?? -1; + default: + return item[sortField as keyof typeof item]; + } + }, + sortDirection + ); + }} + /> + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx index 1c6fa9fe0447e1..45a4afeb532353 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/service_list.test.tsx @@ -9,11 +9,8 @@ import { MemoryRouter } from 'react-router-dom'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { mockMoment, renderWithTheme } from '../../../../utils/testHelpers'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; -import { ServiceList, SERVICE_COLUMNS } from './'; -import props from './__fixtures__/props.json'; - -type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; +import { getServiceColumns, ServiceList } from './'; +import { items } from './__fixtures__/service_api_mock_data'; function Wrapper({ children }: { children?: ReactNode }) { return ( @@ -36,10 +33,7 @@ describe('ServiceList', () => { it('renders with data', () => { expect(() => - renderWithTheme( - , - { wrapper: Wrapper } - ) + renderWithTheme(, { wrapper: Wrapper }) ).not.toThrowError(); }); @@ -61,9 +55,9 @@ describe('ServiceList', () => { }, environments: ['test'], }; - const renderedColumns = SERVICE_COLUMNS.map((c) => - c.render!(service[c.field!], service) - ); + const renderedColumns = getServiceColumns({ + showTransactionTypeColumn: false, + }).map((c) => c.render!(service[c.field!], service)); expect(renderedColumns[0]).toMatchInlineSnapshot(` { describe('without ML data', () => { it('does not render the health column', () => { - const { queryByText } = renderWithTheme( - , - { - wrapper: Wrapper, - } - ); + const { queryByText } = renderWithTheme(, { + wrapper: Wrapper, + }); const healthHeading = queryByText('Health'); expect(healthHeading).toBeNull(); }); it('sorts by transactions per minute', async () => { - const { findByTitle } = renderWithTheme( - , - { - wrapper: Wrapper, - } - ); + const { findByTitle } = renderWithTheme(, { + wrapper: Wrapper, + }); expect( await findByTitle('Trans. per minute; Sorted in descending order') @@ -103,12 +91,10 @@ describe('ServiceList', () => { it('renders the health column', async () => { const { findByTitle } = renderWithTheme( ({ - ...item, - healthStatus: ServiceHealthStatus.warning, - }) - )} + items={items.map((item) => ({ + ...item, + healthStatus: ServiceHealthStatus.warning, + }))} />, { wrapper: Wrapper } ); diff --git a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts index 536be56e152a35..876fc6b822213c 100644 --- a/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/helpers/transaction_error_rate.ts @@ -18,7 +18,10 @@ export function getOutcomeAggregation({ searchAggregatedTransactions: boolean; }) { return { - terms: { field: EVENT_OUTCOME }, + terms: { + field: EVENT_OUTCOME, + include: [EventOutcome.failure, EventOutcome.success], + }, aggs: { // simply using the doc count to get the number of requests is not possible for transaction metrics (histograms) // to work around this we get the number of transactions by counting the number of latency values diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index a6818f96c728ee..21402e4c8dac08 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -100,196 +100,27 @@ Array [ "aggs": Object { "services": Object { "aggs": Object { - "average": Object { - "avg": Object { - "field": "transaction.duration.us", - }, - }, - "timeseries": Object { + "transactionType": Object { "aggs": Object { - "average": Object { - "avg": Object { - "field": "transaction.duration.us", + "agentName": Object { + "top_hits": Object { + "docvalue_fields": Array [ + "agent.name", + ], + "size": 1, }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, - }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - "metric", - "error", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "agent_name": Object { - "top_hits": Object { - "_source": Array [ - "agent.name", - ], - "size": 1, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "count": Object { - "value_count": Object { - "field": "transaction.duration.us", - }, - }, - "timeseries": Object { - "aggs": Object { - "count": Object { - "value_count": Object { + "avg_duration": Object { + "avg": Object { "field": "transaction.duration.us", }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, - }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, - }, - }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "outcomes": Object { - "aggs": Object { - "count": Object { - "value_count": Object { - "field": "transaction.duration.us", + "environments": Object { + "terms": Object { + "field": "service.environment", + "missing": "", }, }, - }, - "terms": Object { - "field": "event.outcome", - }, - }, - "timeseries": Object { - "aggs": Object { "outcomes": Object { "aggs": Object { "count": Object { @@ -300,73 +131,62 @@ Array [ }, "terms": Object { "field": "event.outcome", + "include": Array [ + "failure", + "success", + ], }, }, - }, - "date_histogram": Object { - "extended_bounds": Object { - "max": 1528977600000, - "min": 1528113600000, + "real_document_count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, }, - "field": "@timestamp", - "fixed_interval": "43200s", - "min_doc_count": 0, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 1528113600000, - "lte": 1528977600000, + "timeseries": Object { + "aggs": Object { + "avg_duration": Object { + "avg": Object { + "field": "transaction.duration.us", + }, + }, + "outcomes": Object { + "aggs": Object { + "count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, + }, + }, + "terms": Object { + "field": "event.outcome", + "include": Array [ + "failure", + "success", + ], + }, + }, + "real_document_count": Object { + "value_count": Object { + "field": "transaction.duration.us", + }, + }, + }, + "date_histogram": Object { + "extended_bounds": Object { + "max": 1528977600000, + "min": 1528113600000, + }, + "field": "@timestamp", + "fixed_interval": "43200s", + "min_doc_count": 0, + }, }, }, - }, - Object { - "term": Object { - "service.environment": "test", - }, - }, - Object { - "terms": Object { - "event.outcome": Array [ - "failure", - "success", - ], - }, - }, - ], - }, - }, - "size": 0, - }, - }, - Object { - "apm": Object { - "events": Array [ - "transaction", - "metric", - "error", - ], - }, - "body": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "environments": Object { "terms": Object { - "field": "service.environment", - "size": 100, + "field": "transaction.type", + "order": Object { + "real_document_count": "desc", + }, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts new file mode 100644 index 00000000000000..206827a744113d --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_health_statuses.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getSeverity } from '../../../../common/anomaly_detection'; +import { getServiceHealthStatus } from '../../../../common/service_health_status'; +import { + getMLJobIds, + getServiceAnomalies, +} from '../../service_map/get_service_anomalies'; +import { + ServicesItemsProjection, + ServicesItemsSetup, +} from './get_services_items'; + +interface AggregationParams { + setup: ServicesItemsSetup; + projection: ServicesItemsProjection; + searchAggregatedTransactions: boolean; +} + +export const getHealthStatuses = async ( + { setup }: AggregationParams, + mlAnomaliesEnvironment?: string +) => { + if (!setup.ml) { + return []; + } + + const jobIds = await getMLJobIds( + setup.ml.anomalyDetectors, + mlAnomaliesEnvironment + ); + if (!jobIds.length) { + return []; + } + + const anomalies = await getServiceAnomalies({ + setup, + environment: mlAnomaliesEnvironment, + }); + + return Object.keys(anomalies.serviceAnomalies).map((serviceName) => { + const stats = anomalies.serviceAnomalies[serviceName]; + + const severity = getSeverity(stats.anomalyScore); + const healthStatus = getServiceHealthStatus({ severity }); + + return { + serviceName, + healthStatus, + }; + }); +}; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts new file mode 100644 index 00000000000000..0ee7080dc0834c --- /dev/null +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_service_transaction_stats.ts @@ -0,0 +1,199 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + AGENT_NAME, + SERVICE_ENVIRONMENT, + SERVICE_NAME, + TRANSACTION_TYPE, +} from '../../../../common/elasticsearch_fieldnames'; +import { + TRANSACTION_PAGE_LOAD, + TRANSACTION_REQUEST, +} from '../../../../common/transaction_types'; +import { rangeFilter } from '../../../../common/utils/range_filter'; +import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; +import { + getDocumentTypeFilterForAggregatedTransactions, + getProcessorEventForAggregatedTransactions, + getTransactionDurationFieldForAggregatedTransactions, +} from '../../helpers/aggregated_transactions'; +import { getBucketSize } from '../../helpers/get_bucket_size'; +import { + calculateTransactionErrorPercentage, + getOutcomeAggregation, +} from '../../helpers/transaction_error_rate'; +import { ServicesItemsSetup } from './get_services_items'; + +interface AggregationParams { + setup: ServicesItemsSetup; + searchAggregatedTransactions: boolean; +} + +const MAX_NUMBER_OF_SERVICES = 500; + +function calculateAvgDuration({ + value, + deltaAsMinutes, +}: { + value: number; + deltaAsMinutes: number; +}) { + return value / deltaAsMinutes; +} + +export async function getServiceTransactionStats({ + setup, + searchAggregatedTransactions, +}: AggregationParams) { + const { apmEventClient, start, end, esFilter } = setup; + + const outcomes = getOutcomeAggregation({ searchAggregatedTransactions }); + + const metrics = { + real_document_count: { + value_count: { + field: getTransactionDurationFieldForAggregatedTransactions( + searchAggregatedTransactions + ), + }, + }, + avg_duration: { + avg: { + field: getTransactionDurationFieldForAggregatedTransactions( + searchAggregatedTransactions + ), + }, + }, + outcomes, + }; + + const response = await apmEventClient.search({ + apm: { + events: [ + getProcessorEventForAggregatedTransactions( + searchAggregatedTransactions + ), + ], + }, + body: { + size: 0, + query: { + bool: { + filter: [ + { range: rangeFilter(start, end) }, + ...esFilter, + ...getDocumentTypeFilterForAggregatedTransactions( + searchAggregatedTransactions + ), + ], + }, + }, + aggs: { + services: { + terms: { + field: SERVICE_NAME, + size: MAX_NUMBER_OF_SERVICES, + }, + aggs: { + transactionType: { + terms: { + field: TRANSACTION_TYPE, + order: { real_document_count: 'desc' }, + }, + aggs: { + ...metrics, + environments: { + terms: { + field: SERVICE_ENVIRONMENT, + missing: '', + }, + }, + agentName: { + top_hits: { + docvalue_fields: [AGENT_NAME] as const, + size: 1, + }, + }, + timeseries: { + date_histogram: { + field: '@timestamp', + fixed_interval: getBucketSize({ + start, + end, + numBuckets: 20, + }).intervalString, + min_doc_count: 0, + extended_bounds: { min: start, max: end }, + }, + aggs: metrics, + }, + }, + }, + }, + }, + }, + }, + }); + + const deltaAsMinutes = (setup.end - setup.start) / 1000 / 60; + + return ( + response.aggregations?.services.buckets.map((bucket) => { + const topTransactionTypeBucket = + bucket.transactionType.buckets.find( + ({ key }) => + key === TRANSACTION_REQUEST || key === TRANSACTION_PAGE_LOAD + ) ?? bucket.transactionType.buckets[0]; + + return { + serviceName: bucket.key as string, + transactionType: topTransactionTypeBucket.key as string, + environments: topTransactionTypeBucket.environments.buckets + .map((environmentBucket) => environmentBucket.key as string) + .filter(Boolean), + agentName: topTransactionTypeBucket.agentName.hits.hits[0].fields[ + 'agent.name' + ]?.[0] as AgentName, + avgResponseTime: { + value: topTransactionTypeBucket.avg_duration.value, + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: dateBucket.avg_duration.value, + }) + ), + }, + transactionErrorRate: { + value: calculateTransactionErrorPercentage( + topTransactionTypeBucket.outcomes + ), + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: calculateTransactionErrorPercentage(dateBucket.outcomes), + }) + ), + }, + transactionsPerMinute: { + value: calculateAvgDuration({ + value: topTransactionTypeBucket.real_document_count.value, + deltaAsMinutes, + }), + timeseries: topTransactionTypeBucket.timeseries.buckets.map( + (dateBucket) => ({ + x: dateBucket.key, + y: calculateAvgDuration({ + value: dateBucket.real_document_count.value, + deltaAsMinutes, + }), + }) + ), + }, + }; + }) ?? [] + ); +} diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index 11f3e44fce87c2..359c677b00bafb 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -7,14 +7,8 @@ import { Logger } from '@kbn/logging'; import { joinByKey } from '../../../../common/utils/join_by_key'; import { getServicesProjection } from '../../../projections/services'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; -import { - getAgentNames, - getEnvironments, - getHealthStatuses, - getTransactionDurationAverages, - getTransactionErrorRates, - getTransactionRates, -} from './get_services_items_stats'; +import { getHealthStatuses } from './get_health_statuses'; +import { getServiceTransactionStats } from './get_service_transaction_stats'; export type ServicesItemsSetup = Setup & SetupTimeRange; export type ServicesItemsProjection = ReturnType; @@ -37,46 +31,23 @@ export async function getServicesItems({ searchAggregatedTransactions, }; - const [ - transactionDurationAverages, - agentNames, - transactionRates, - transactionErrorRates, - environments, - healthStatuses, - ] = await Promise.all([ - getTransactionDurationAverages(params), - getAgentNames(params), - getTransactionRates(params), - getTransactionErrorRates(params), - getEnvironments(params), + const [transactionStats, healthStatuses] = await Promise.all([ + getServiceTransactionStats(params), getHealthStatuses(params, setup.uiFilters.environment).catch((err) => { logger.error(err); return []; }), ]); - const apmServiceMetrics = joinByKey( - [ - ...transactionDurationAverages, - ...agentNames, - ...transactionRates, - ...transactionErrorRates, - ...environments, - ], - 'serviceName' - ); - - const apmServices = apmServiceMetrics.map(({ serviceName }) => serviceName); + const apmServices = transactionStats.map(({ serviceName }) => serviceName); // make sure to exclude health statuses from services // that are not found in APM data - const matchedHealthStatuses = healthStatuses.filter(({ serviceName }) => apmServices.includes(serviceName) ); - const allMetrics = [...apmServiceMetrics, ...matchedHealthStatuses]; + const allMetrics = [...transactionStats, ...matchedHealthStatuses]; return joinByKey(allMetrics, 'serviceName'); } diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts deleted file mode 100644 index c8ebaa13d9df94..00000000000000 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items_stats.ts +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getServiceHealthStatus } from '../../../../common/service_health_status'; -import { EventOutcome } from '../../../../common/event_outcome'; -import { getSeverity } from '../../../../common/anomaly_detection'; -import { AgentName } from '../../../../typings/es_schemas/ui/fields/agent'; -import { - AGENT_NAME, - SERVICE_ENVIRONMENT, - EVENT_OUTCOME, -} from '../../../../common/elasticsearch_fieldnames'; -import { mergeProjection } from '../../../projections/util/merge_projection'; -import { - ServicesItemsSetup, - ServicesItemsProjection, -} from './get_services_items'; -import { - getDocumentTypeFilterForAggregatedTransactions, - getProcessorEventForAggregatedTransactions, - getTransactionDurationFieldForAggregatedTransactions, -} from '../../helpers/aggregated_transactions'; -import { getBucketSize } from '../../helpers/get_bucket_size'; -import { - getMLJobIds, - getServiceAnomalies, -} from '../../service_map/get_service_anomalies'; -import { - calculateTransactionErrorPercentage, - getOutcomeAggregation, - getTransactionErrorRateTimeSeries, -} from '../../helpers/transaction_error_rate'; - -function getDateHistogramOpts(start: number, end: number) { - return { - field: '@timestamp', - fixed_interval: getBucketSize({ start, end, numBuckets: 20 }) - .intervalString, - min_doc_count: 0, - extended_bounds: { min: start, max: end }, - }; -} - -const MAX_NUMBER_OF_SERVICES = 500; - -const getDeltaAsMinutes = (setup: ServicesItemsSetup) => - (setup.end - setup.start) / 1000 / 60; - -interface AggregationParams { - setup: ServicesItemsSetup; - projection: ServicesItemsProjection; - searchAggregatedTransactions: boolean; -} - -export const getTransactionDurationAverages = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - average: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - average: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - avgResponseTime: { - value: serviceBucket.average.value, - timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ - x: dateBucket.key, - y: dateBucket.average.value, - })), - }, - })); -}; - -export const getAgentNames = async ({ - setup, - projection, -}: AggregationParams) => { - const { apmEventClient } = setup; - const response = await apmEventClient.search( - mergeProjection(projection, { - body: { - size: 0, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - agent_name: { - top_hits: { - _source: [AGENT_NAME], - size: 1, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - agentName: serviceBucket.agent_name.hits.hits[0]?._source.agent - .name as AgentName, - })); -}; - -export const getTransactionRates = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - count: { - value_count: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - count: { - value_count: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - const deltaAsMinutes = getDeltaAsMinutes(setup); - - return aggregations.services.buckets.map((serviceBucket) => { - const transactionsPerMinute = serviceBucket.count.value / deltaAsMinutes; - return { - serviceName: serviceBucket.key as string, - transactionsPerMinute: { - value: transactionsPerMinute, - timeseries: serviceBucket.timeseries.buckets.map((dateBucket) => ({ - x: dateBucket.key, - y: dateBucket.count.value / deltaAsMinutes, - })), - }, - }; - }); -}; - -export const getTransactionErrorRates = async ({ - setup, - projection, - searchAggregatedTransactions, -}: AggregationParams) => { - const { apmEventClient, start, end } = setup; - - const outcomes = getOutcomeAggregation({ searchAggregatedTransactions }); - - const response = await apmEventClient.search( - mergeProjection(projection, { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - ...projection.body.query.bool.filter, - { - terms: { - [EVENT_OUTCOME]: [EventOutcome.failure, EventOutcome.success], - }, - }, - ], - }, - }, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - outcomes, - timeseries: { - date_histogram: getDateHistogramOpts(start, end), - aggs: { - outcomes, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => { - const transactionErrorRate = calculateTransactionErrorPercentage( - serviceBucket.outcomes - ); - return { - serviceName: serviceBucket.key as string, - transactionErrorRate: { - value: transactionErrorRate, - timeseries: getTransactionErrorRateTimeSeries( - serviceBucket.timeseries.buckets - ), - }, - }; - }); -}; - -export const getEnvironments = async ({ - setup, - projection, -}: AggregationParams) => { - const { apmEventClient, config } = setup; - const maxServiceEnvironments = config['xpack.apm.maxServiceEnvironments']; - const response = await apmEventClient.search( - mergeProjection(projection, { - body: { - size: 0, - aggs: { - services: { - terms: { - ...projection.body.aggs.services.terms, - size: MAX_NUMBER_OF_SERVICES, - }, - aggs: { - environments: { - terms: { - field: SERVICE_ENVIRONMENT, - size: maxServiceEnvironments, - }, - }, - }, - }, - }, - }, - }) - ); - - const { aggregations } = response; - - if (!aggregations) { - return []; - } - - return aggregations.services.buckets.map((serviceBucket) => ({ - serviceName: serviceBucket.key as string, - environments: serviceBucket.environments.buckets.map( - (envBucket) => envBucket.key as string - ), - })); -}; - -export const getHealthStatuses = async ( - { setup }: AggregationParams, - mlAnomaliesEnvironment?: string -) => { - if (!setup.ml) { - return []; - } - - const jobIds = await getMLJobIds( - setup.ml.anomalyDetectors, - mlAnomaliesEnvironment - ); - if (!jobIds.length) { - return []; - } - - const anomalies = await getServiceAnomalies({ - setup, - environment: mlAnomaliesEnvironment, - }); - - return Object.keys(anomalies.serviceAnomalies).map((serviceName) => { - const stats = anomalies.serviceAnomalies[serviceName]; - - const severity = getSeverity(stats.anomalyScore); - const healthStatus = getServiceHealthStatus({ severity }); - - return { - serviceName, - healthStatus, - }; - }); -}; diff --git a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts index 52c9dd74167f5e..b1606776733311 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts @@ -99,21 +99,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { Array [ Object { "avgResponseTime": Object { - "value": 556200.153101878, + "value": 420419.34550767, }, "transactionErrorRate": Object { "value": 0, }, "transactionsPerMinute": Object { - "value": 117.133333333333, + "value": 45.6333333333333, }, }, Object { "avgResponseTime": Object { - "value": 2629229.16666667, + "value": 2382833.33333333, + }, + "transactionErrorRate": Object { + "value": null, }, "transactionsPerMinute": Object { - "value": 3.2, + "value": 0.2, }, }, Object { @@ -151,24 +154,24 @@ export default function ApiTest({ getService }: FtrProviderContext) { }, Object { "avgResponseTime": Object { - "value": 563605.417040359, + "value": 24920.1052631579, }, "transactionErrorRate": Object { "value": 0.0210526315789474, }, "transactionsPerMinute": Object { - "value": 7.43333333333333, + "value": 3.16666666666667, }, }, Object { "avgResponseTime": Object { - "value": 217138.013645224, + "value": 29542.6607142857, }, "transactionErrorRate": Object { - "value": 0.315789473684211, + "value": 0.0357142857142857, }, "transactionsPerMinute": Object { - "value": 17.1, + "value": 1.86666666666667, }, }, Object { @@ -186,6 +189,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { "avgResponseTime": Object { "value": 2319812.5, }, + "transactionErrorRate": Object { + "value": null, + }, "transactionsPerMinute": Object { "value": 0.533333333333333, }, From 47ce575cc0d21cb87482a27543419b8b9bd756cb Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Thu, 24 Dec 2020 13:52:09 +0100 Subject: [PATCH 090/100] [ML] Fix charts grid on the Anomaly Explorer page (#86904) * [ML] fix AR charts grid items width * [ML] update test snapshot --- .../explorer_charts_container_service.test.js.snap | 4 ++-- .../explorer_charts_container_service.js | 10 +++++----- .../application/explorer/swimlane_container.tsx | 11 ++++------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/__snapshots__/explorer_charts_container_service.test.js.snap b/x-pack/plugins/ml/public/application/explorer/explorer_charts/__snapshots__/explorer_charts_container_service.test.js.snap index 64638fcd23734c..ecb7fc07711e8b 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/__snapshots__/explorer_charts_container_service.test.js.snap +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/__snapshots__/explorer_charts_container_service.test.js.snap @@ -596,8 +596,8 @@ Object { "loading": false, "metricFieldName": "responsetime", "metricFunction": "avg", - "plotEarliest": 1486560600000, - "plotLatest": 1486765800000, + "plotEarliest": 1486542600000, + "plotLatest": 1486783800000, "selectedEarliest": 1486656000000, "selectedLatest": 1486670399999, "timeField": "@timestamp", diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js index 47087e776d6dd3..a2c530c9ca4940 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container_service.js @@ -29,6 +29,7 @@ import { explorerService } from '../explorer_dashboard_service'; import { CHART_TYPE } from '../explorer_constants'; import { i18n } from '@kbn/i18n'; +import { SWIM_LANE_LABEL_WIDTH } from '../swimlane_container'; export function getDefaultChartsData() { return { @@ -57,15 +58,14 @@ export const anomalyDataChange = function ( ) { const data = getDefaultChartsData(); + const containerWith = chartsContainerWidth + SWIM_LANE_LABEL_WIDTH; + const filteredRecords = anomalyRecords.filter((record) => { return Number(record.record_score) >= severity; }); const [allSeriesRecords, errorMessages] = processRecordsForDisplay(filteredRecords); // Calculate the number of charts per row, depending on the width available, to a max of 4. - let chartsPerRow = Math.min( - Math.max(Math.floor(chartsContainerWidth / 550), 1), - MAX_CHARTS_PER_ROW - ); + let chartsPerRow = Math.min(Math.max(Math.floor(containerWith / 550), 1), MAX_CHARTS_PER_ROW); if (allSeriesRecords.length === 1) { chartsPerRow = 1; } @@ -81,7 +81,7 @@ export const anomalyDataChange = function ( // Calculate the time range of the charts, which is a function of the chart width and max job bucket span. data.tooManyBuckets = false; - const chartWidth = Math.floor(chartsContainerWidth / chartsPerRow); + const chartWidth = Math.floor(containerWith / chartsPerRow); const { chartRange, tooManyBuckets } = calculateChartRange( seriesConfigs, selectedEarliestMs, diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index 101d4857a89b1e..145f6cc0fcf7ae 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -46,11 +46,11 @@ import { useUiSettings } from '../contexts/kibana'; /** * Ignore insignificant resize, e.g. browser scrollbar appearance. */ -const RESIZE_IGNORED_DIFF_PX = 20; const RESIZE_THROTTLE_TIME_MS = 500; const CELL_HEIGHT = 30; const LEGEND_HEIGHT = 34; const Y_AXIS_HEIGHT = 24; +export const SWIM_LANE_LABEL_WIDTH = 200; export function isViewBySwimLaneData(arg: any): arg is ViewBySwimLaneData { return arg && arg.hasOwnProperty('cardinality'); @@ -167,12 +167,9 @@ export const SwimlaneContainer: FC = ({ const resizeHandler = useCallback( throttle((e: { width: number; height: number }) => { - const labelWidth = 200; - const resultNewWidth = e.width - labelWidth; - if (Math.abs(resultNewWidth - chartWidth) > RESIZE_IGNORED_DIFF_PX) { - setChartWidth(resultNewWidth); - onResize(resultNewWidth); - } + const resultNewWidth = e.width - SWIM_LANE_LABEL_WIDTH; + setChartWidth(resultNewWidth); + onResize(resultNewWidth); }, RESIZE_THROTTLE_TIME_MS), [chartWidth] ); From c0d6e12c3c1a06c2499ca505b8d8fc9bda4812b2 Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Mon, 28 Dec 2020 08:08:04 +0100 Subject: [PATCH 091/100] Removes archives (#86537) --- .../security_solution/cypress/README.md | 11 +- .../cypress/integration/alerts.spec.ts | 139 +- ...alerts_detection_callouts_readonly.spec.ts | 3 +- .../alerts_detection_exceptions.spec.ts | 5 +- .../alerts_detection_rules.spec.ts | 39 +- .../alerts_detection_rules_custom.spec.ts | 49 +- .../alerts_detection_rules_eql.spec.ts | 7 +- .../alerts_detection_rules_export.spec.ts | 5 +- ...ts_detection_rules_indicator_match.spec.ts | 2 - .../alerts_detection_rules_ml.spec.ts | 4 +- .../alerts_detection_rules_override.spec.ts | 6 +- .../alerts_detection_rules_prebuilt.spec.ts | 9 +- .../alerts_detection_rules_threshold.spec.ts | 2 - .../integration/alerts_timeline.spec.ts | 24 +- .../cypress/integration/cases.spec.ts | 4 +- .../cases_connector_options.spec.ts | 2 +- .../integration/cases_connectors.spec.ts | 2 +- .../cypress/integration/events_viewer.spec.ts | 14 +- .../integration/fields_browser.spec.ts | 28 +- .../cypress/integration/sourcerer.spec.ts | 2 +- .../timeline_attach_to_case.spec.ts | 30 +- .../integration/timeline_creation.spec.ts | 15 +- .../timeline_local_storage.spec.ts | 2 +- .../timeline_template_creation.spec.ts | 2 +- .../timeline_templates_export.spec.ts | 2 +- .../timeline_toggle_column.spec.ts | 8 +- .../integration/timelines_export.spec.ts | 2 +- .../integration/url_compatibility.spec.ts | 2 +- .../cypress/integration/value_lists.spec.ts | 3 - .../security_solution/cypress/objects/rule.ts | 17 +- .../cypress/screens/alerts_detection_rules.ts | 2 + .../cypress/screens/create_new_rule.ts | 3 +- .../cypress/screens/timeline.ts | 4 +- .../cypress/tasks/api_calls/cases.ts | 28 + .../cypress/tasks/api_calls/rules.ts | 31 +- .../security_solution/cypress/tasks/common.ts | 19 + .../cypress/tasks/hosts/events.ts | 2 +- .../cypress/tasks/timeline.ts | 7 +- .../es_archives/alerts/data.json.gz | Bin 58602 -> 0 bytes .../es_archives/alerts/mappings.json | 8124 -------------- .../case_and_timeline/data.json.gz | Bin 3687 -> 0 bytes .../case_and_timeline/mappings.json | 2616 ----- .../es_archives/closed_alerts/data.json.gz | Bin 55877 -> 0 bytes .../es_archives/closed_alerts/mappings.json | 8124 -------------- .../es_archives/custom_rules/data.json.gz | Bin 3084 -> 0 bytes .../es_archives/custom_rules/mappings.json | 6243 ----------- .../prebuilt_rules_loaded/data.json.gz | Bin 42571 -> 0 bytes .../prebuilt_rules_loaded/mappings.json | 2967 ----- .../es_archives/timeline_alerts/data.json.gz | Bin 225608 -> 0 bytes .../es_archives/timeline_alerts/mappings.json | 9588 ----------------- 50 files changed, 281 insertions(+), 37917 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts delete mode 100644 x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json delete mode 100644 x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/case_and_timeline/mappings.json delete mode 100644 x-pack/test/security_solution_cypress/es_archives/closed_alerts/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/closed_alerts/mappings.json delete mode 100644 x-pack/test/security_solution_cypress/es_archives/custom_rules/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json delete mode 100644 x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json delete mode 100644 x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz delete mode 100644 x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json diff --git a/x-pack/plugins/security_solution/cypress/README.md b/x-pack/plugins/security_solution/cypress/README.md index b82f4a392483cf..4fb98f0983ee93 100644 --- a/x-pack/plugins/security_solution/cypress/README.md +++ b/x-pack/plugins/security_solution/cypress/README.md @@ -179,9 +179,9 @@ CYPRESS_BASE_URL=http(s)://:@ CYPRESS_ELASTICSEARCH_ ## Best Practices -### Clean up the state between tests +### Clean up the state -Remember to clean up the state of the test after its execution. +Remember to use the `cleanKibana` method before starting the execution of the test ### Minimize the use of es_archive @@ -192,15 +192,12 @@ When possible, create all the data that you need for executing the tests using t Loading the web page takes a big amount of time, in order to minimize that impact, the following points should be taken into consideration until another solution is implemented: -- Don't refresh the page for every test to clean the state of it. -- Instead, group the tests that are similar in different contexts. +- Group the tests that are similar in different contexts. - For every context login only once, clean the state between tests if needed without re-loading the page. - All tests in a spec file must be order-independent. - - If you need to reload the page to make the tests order-independent, consider to create a new context. - + Remember that minimizing the number of times the web page is loaded, we minimize as well the execution time. - ## Reporting When Cypress tests are run on the command line via non visual mode diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index a15aad1bd8cc3d..82e214398f69a1 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { newRule } from '../objects/rule'; import { ALERTS, ALERTS_COUNT, @@ -24,11 +25,13 @@ import { waitForAlertsToBeLoaded, markInProgressFirstAlert, goToInProgressAlerts, + waitForAlertsIndexToBeCreated, } from '../tasks/alerts'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { loginAndWaitForPage } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; @@ -36,25 +39,21 @@ describe('Alerts', () => { context('Closing alerts', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('alerts'); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Closes and opens alerts', () => { - waitForAlertsPanelToBeLoaded(); - waitForAlertsToBeLoaded(); - + const numberOfAlertsToBeClosed = 3; cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { cy.get(SHOWING_ALERTS).should('have.text', `Showing ${numberOfAlerts} alerts`); - const numberOfAlertsToBeClosed = 3; selectNumberOfAlerts(numberOfAlertsToBeClosed); cy.get(SELECTED_ALERTS).should( @@ -64,8 +63,6 @@ describe('Alerts', () => { closeAlerts(); waitForAlerts(); - cy.reload(); - waitForAlerts(); const expectedNumberOfAlertsAfterClosing = +numberOfAlerts - numberOfAlertsToBeClosed; cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlertsAfterClosing.toString()); @@ -92,11 +89,6 @@ describe('Alerts', () => { openAlerts(); waitForAlerts(); - cy.reload(); - waitForAlertsToBeLoaded(); - waitForAlerts(); - goToClosedAlerts(); - waitForAlerts(); const expectedNumberOfClosedAlertsAfterOpened = 2; cy.get(ALERTS_COUNT).should( @@ -124,8 +116,6 @@ describe('Alerts', () => { }); it('Closes one alert when more than one opened alerts are selected', () => { - waitForAlertsToBeLoaded(); - cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { @@ -137,8 +127,6 @@ describe('Alerts', () => { cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); closeFirstAlert(); - cy.reload(); - waitForAlertsToBeLoaded(); waitForAlerts(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeClosed; @@ -164,52 +152,66 @@ describe('Alerts', () => { context('Opening alerts', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('closed_alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); + selectNumberOfAlerts(5); + + cy.get(SELECTED_ALERTS).should('have.text', `Selected 5 alerts`); - afterEach(() => { - esArchiverUnload('closed_alerts'); + closeAlerts(); + waitForAlerts(); + refreshPage(); }); it('Open one alert when more than one closed alerts are selected', () => { - waitForAlerts(); - goToClosedAlerts(); - waitForAlertsToBeLoaded(); + waitForAlertsToPopulate(); cy.get(ALERTS_COUNT) .invoke('text') - .then((numberOfAlerts) => { - const numberOfAlertsToBeOpened = 1; - const numberOfAlertsToBeSelected = 3; - - cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled'); - selectNumberOfAlerts(numberOfAlertsToBeSelected); - cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); - - openFirstAlert(); - cy.reload(); + .then((numberOfOpenedAlertsText) => { + const numberOfOpenedAlerts = parseInt(numberOfOpenedAlertsText, 10); goToClosedAlerts(); - waitForAlertsToBeLoaded(); - waitForAlerts(); - - const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; - cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); - cy.get(SHOWING_ALERTS).should( - 'have.text', - `Showing ${expectedNumberOfAlerts.toString()} alerts` - ); - - goToOpenedAlerts(); - waitForAlerts(); - - cy.get(ALERTS_COUNT).should('have.text', numberOfAlertsToBeOpened.toString()); - cy.get(SHOWING_ALERTS).should( - 'have.text', - `Showing ${numberOfAlertsToBeOpened.toString()} alert` - ); - cy.get(ALERTS).should('have.length', numberOfAlertsToBeOpened); + cy.get(ALERTS_COUNT) + .invoke('text') + .then((numberOfAlerts) => { + const numberOfAlertsToBeOpened = 1; + const numberOfAlertsToBeSelected = 3; + + cy.get(TAKE_ACTION_POPOVER_BTN).should('have.attr', 'disabled'); + selectNumberOfAlerts(numberOfAlertsToBeSelected); + cy.get(SELECTED_ALERTS).should( + 'have.text', + `Selected ${numberOfAlertsToBeSelected} alerts` + ); + + cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); + + openFirstAlert(); + waitForAlerts(); + + const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeOpened; + cy.get(ALERTS_COUNT).should('have.text', expectedNumberOfAlerts.toString()); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${expectedNumberOfAlerts.toString()} alerts` + ); + + goToOpenedAlerts(); + waitForAlerts(); + + cy.get(ALERTS_COUNT).should( + 'have.text', + (numberOfOpenedAlerts + numberOfAlertsToBeOpened).toString() + ); + cy.get(SHOWING_ALERTS).should( + 'have.text', + `Showing ${(numberOfOpenedAlerts + numberOfAlertsToBeOpened).toString()} alerts` + ); + }); }); }); }); @@ -217,20 +219,15 @@ describe('Alerts', () => { context('Marking alerts as in-progress', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('alerts'); - removeSignalsIndex(); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Mark one alert in progress when more than one open alerts are selected', () => { - waitForAlerts(); - waitForAlertsToBeLoaded(); - cy.get(ALERTS_COUNT) .invoke('text') .then((numberOfAlerts) => { @@ -242,8 +239,6 @@ describe('Alerts', () => { cy.get(TAKE_ACTION_POPOVER_BTN).should('not.have.attr', 'disabled'); markInProgressFirstAlert(); - cy.reload(); - goToOpenedAlerts(); waitForAlertsToBeLoaded(); const expectedNumberOfAlerts = +numberOfAlerts - numberOfAlertsToBeMarkedInProgress; diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts index fa48c0bc1abc65..4bf54963a5322a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_callouts_readonly.spec.ts @@ -16,7 +16,7 @@ import { } from '../tasks/login'; import { waitForAlertsIndexToBeCreated } from '../tasks/alerts'; import { goToRuleDetails } from '../tasks/alerts_detection_rules'; -import { createCustomRule, deleteCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule, deleteCustomRule } from '../tasks/api_calls/rules'; import { getCallOut, waitForCallOutToBeShown, dismissCallOut } from '../tasks/common/callouts'; import { cleanKibana } from '../tasks/common'; @@ -42,7 +42,6 @@ describe('Detections > Callouts indicating read-only access to resources', () => // First, we have to open the app on behalf of a priviledged user in order to initialize it. // Otherwise the app will be disabled and show a "welcome"-like page. cleanKibana(); - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL, ROLES.platform_engineer); waitForAlertsIndexToBeCreated(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts index 265f4d43c71c18..44519adc255524 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_exceptions.spec.ts @@ -16,7 +16,7 @@ import { goToOpenedAlerts, waitForAlertsIndexToBeCreated, } from '../tasks/alerts'; -import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule } from '../tasks/api_calls/rules'; import { goToRuleDetails } from '../tasks/alerts_detection_rules'; import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; @@ -35,11 +35,10 @@ import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; import { cleanKibana } from '../tasks/common'; -describe.skip('Exceptions', () => { +describe('Exceptions', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; beforeEach(() => { cleanKibana(); - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsIndexToBeCreated(); createCustomRule(newRule); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts index 4284b05205c698..9eb2127acb4466 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules.spec.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { - FIFTH_RULE, FIRST_RULE, RULE_NAME, RULE_SWITCH, SECOND_RULE, - SEVENTH_RULE, RULE_AUTO_REFRESH_IDLE_MODAL, + FOURTH_RULE, } from '../screens/alerts_detection_rules'; import { @@ -28,43 +27,45 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRuleToBeActivated, } from '../tasks/alerts_detection_rules'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DEFAULT_RULE_REFRESH_INTERVAL_VALUE } from '../../common/constants'; import { DETECTIONS_URL } from '../urls/navigation'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; +import { existingRule, newOverrideRule, newRule, newThresholdRule } from '../objects/rule'; describe('Alerts detection rules', () => { - before(() => { + beforeEach(() => { cleanKibana(); removeSignalsIndex(); - esArchiverLoad('prebuilt_rules_loaded'); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRule(newRule, 'rule1'); + createCustomRule(existingRule, 'rule2'); + createCustomRule(newOverrideRule, 'rule3'); + createCustomRule(newThresholdRule, 'rule4'); }); after(() => { - esArchiverUnload('prebuilt_rules_loaded'); + cy.clock().invoke('restore'); }); it('Sorts by activated rules', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); - waitForAlertsPanelToBeLoaded(); - waitForAlertsIndexToBeCreated(); goToManageAlertsDetectionRules(); - waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); cy.get(RULE_NAME) - .eq(FIFTH_RULE) + .eq(SECOND_RULE) .invoke('text') - .then((fifthRuleName) => { - activateRule(FIFTH_RULE); + .then((secondInitialRuleName) => { + activateRule(SECOND_RULE); waitForRuleToBeActivated(); cy.get(RULE_NAME) - .eq(SEVENTH_RULE) + .eq(FOURTH_RULE) .invoke('text') - .then((seventhRuleName) => { - activateRule(SEVENTH_RULE); + .then((fourthInitialRuleName) => { + activateRule(FOURTH_RULE); waitForRuleToBeActivated(); sortByActivatedRules(); cy.get(RULE_NAME) @@ -76,8 +77,8 @@ describe('Alerts detection rules', () => { .invoke('text') .then((secondRuleName) => { const expectedRulesNames = `${firstRuleName} ${secondRuleName}`; - cy.wrap(expectedRulesNames).should('include', fifthRuleName); - cy.wrap(expectedRulesNames).should('include', seventhRuleName); + cy.wrap(expectedRulesNames).should('include', secondInitialRuleName); + cy.wrap(expectedRulesNames).should('include', fourthInitialRuleName); }); }); cy.get(RULE_SWITCH).eq(FIRST_RULE).should('have.attr', 'role', 'switch'); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index fb196fde3ae83b..d0b0862034a3ba 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -5,7 +5,7 @@ */ import { formatMitreAttackDescription } from '../helpers/rules'; -import { newRule, existingRule, indexPatterns, editedRule } from '../objects/rule'; +import { newRule, existingRule, indexPatterns, editedRule, newOverrideRule } from '../objects/rule'; import { ALERT_RULE_METHOD, ALERT_RULE_NAME, @@ -84,7 +84,7 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -100,8 +100,8 @@ import { waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { saveEditedRule, waitForKibana } from '../tasks/edit_rule'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; @@ -114,9 +114,8 @@ describe('Custom detection rules creation', () => { const rule = { ...newRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); @@ -213,22 +212,20 @@ describe('Custom detection rules creation', () => { }); }); -describe.skip('Custom detection rules deletion and edition', () => { - beforeEach(() => { - cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('custom_rules'); - loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); - waitForAlertsPanelToBeLoaded(); - waitForAlertsIndexToBeCreated(); - goToManageAlertsDetectionRules(); - }); - - afterEach(() => { - esArchiverUnload('custom_rules'); - }); - +describe('Custom detection rules deletion and edition', () => { context('Deletion', () => { + beforeEach(() => { + cleanKibana(); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + goToManageAlertsDetectionRules(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule, 'rule1'); + createCustomRuleActivated(newOverrideRule, 'rule2'); + createCustomRuleActivated(existingRule, 'rule3'); + refreshPage(); + goToManageAlertsDetectionRules(); + }); + it('Deletes one rule', () => { cy.get(RULES_TABLE) .find(RULES_ROW) @@ -263,7 +260,7 @@ describe.skip('Custom detection rules deletion and edition', () => { .find(RULES_ROW) .then((rules) => { const initialNumberOfRules = rules.length; - const numberOfRulesToBeDeleted = 3; + const numberOfRulesToBeDeleted = 2; const expectedNumberOfRulesAfterDeletion = initialNumberOfRules - numberOfRulesToBeDeleted; @@ -294,6 +291,16 @@ describe.skip('Custom detection rules deletion and edition', () => { const expectedEditedIndexPatterns = editedRule.index && editedRule.index.length ? editedRule.index : indexPatterns; + beforeEach(() => { + cleanKibana(); + loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); + goToManageAlertsDetectionRules(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(existingRule, 'rule1'); + refreshPage(); + goToManageAlertsDetectionRules(); + }); + it('Allows a rule to be edited', () => { editFirstRule(); waitForKibana(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index d02c015a5f1f73..6567ee07c4e3ac 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -62,7 +62,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -88,9 +87,8 @@ describe.skip('Detection rules, EQL', () => { const rule = { ...eqlRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(eqlRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); @@ -180,9 +178,8 @@ describe.skip('Detection rules, sequence EQL', () => { const expectedNumberOfSequenceAlerts = 1; const rule = { ...eqlSequenceRule }; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(eqlSequenceRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts index a9c1f7c331d0e3..0f5ce9c47a4392 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_export.spec.ts @@ -11,7 +11,7 @@ import { waitForAlertsPanelToBeLoaded, } from '../tasks/alerts'; import { exportFirstRule } from '../tasks/alerts_detection_rules'; -import { createCustomRule, removeSignalsIndex } from '../tasks/api_calls/rules'; +import { createCustomRule } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; @@ -19,9 +19,8 @@ import { DETECTIONS_URL } from '../urls/navigation'; describe.skip('Export rules', () => { let ruleResponse: Cypress.Response; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); cy.intercept( 'POST', '/api/detection_engine/rules/_export?exclude_export_details=false&file_name=rules_export.ndjson' diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts index 4e97b619fc274a..1f2793abcbf1f4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_indicator_match.spec.ts @@ -65,7 +65,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { createAndActivateRule, @@ -91,7 +90,6 @@ describe('Detection rules, Indicator Match', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); esArchiverLoad('threat_indicator'); esArchiverLoad('threat_data'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index c651139248e0c2..baefcba945447c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -52,7 +52,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; import { createAndActivateRule, @@ -72,9 +71,8 @@ describe.skip('Detection rules, machine learning', () => { const expectedMitre = formatMitreAttackDescription(machineLearningRule.mitre); const expectedNumberOfRules = 1; - before(() => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); }); it('Creates and activates a new ml rule', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index a543dca00b010d..c641d572f515c4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -69,7 +69,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -84,9 +83,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/85671 -// FLAKY: https://github.com/elastic/kibana/issues/84020 -describe.skip('Detection rules, override', () => { +describe('Detection rules, override', () => { const expectedUrls = newOverrideRule.referenceUrls.join(''); const expectedFalsePositives = newOverrideRule.falsePositivesExamples.join(''); const expectedTags = newOverrideRule.tags.join(''); @@ -96,7 +93,6 @@ describe.skip('Detection rules, override', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newOverrideRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts index a4e41631ea2463..4d2efc47db483e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_prebuilt.spec.ts @@ -30,20 +30,16 @@ import { waitForPrebuiltDetectionRulesToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { esArchiverLoadEmptyKibana } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; import { totalNumberOfPrebuiltRules } from '../objects/rule'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -describe.skip('Alerts rules, prebuilt rules', () => { - before(() => { +describe('Alerts rules, prebuilt rules', () => { + beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoadEmptyKibana(); }); it('Loads prebuilt rules', () => { @@ -84,7 +80,6 @@ describe('Deleting prebuilt rules', () => { const expectedElasticRulesBtnText = `Elastic rules (${expectedNumberOfRules})`; cleanKibana(); - esArchiverLoadEmptyKibana(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 812d0fa29f9b74..058bac6258ffcb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -64,7 +64,6 @@ import { waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, waitForRulesToBeLoaded, } from '../tasks/alerts_detection_rules'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { @@ -90,7 +89,6 @@ describe.skip('Detection rules, threshold', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); createTimeline(newThresholdRule.timeline).then((response) => { rule.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; }); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts index d5fba65a70149b..e42410f7fb38d7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_timeline.spec.ts @@ -4,30 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ +import { newRule } from '../objects/rule'; import { PROVIDER_BADGE } from '../screens/timeline'; -import { investigateFirstAlertInTimeline, waitForAlertsPanelToBeLoaded } from '../tasks/alerts'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; +import { + investigateFirstAlertInTimeline, + waitForAlertsIndexToBeCreated, + waitForAlertsPanelToBeLoaded, +} from '../tasks/alerts'; +import { createCustomRuleActivated } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { waitForAlertsToPopulate } from '../tasks/create_new_rule'; import { loginAndWaitForPage } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; describe('Alerts timeline', () => { beforeEach(() => { cleanKibana(); - removeSignalsIndex(); - esArchiverLoad('timeline_alerts'); loginAndWaitForPage(DETECTIONS_URL); - }); - - afterEach(() => { - esArchiverUnload('timeline_alerts'); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); + createCustomRuleActivated(newRule); + refreshPage(); + waitForAlertsToPopulate(); }); it('Investigate alert in default timeline', () => { - waitForAlertsPanelToBeLoaded(); investigateFirstAlertInTimeline(); cy.get(PROVIDER_BADGE) .first() diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts index d53b98b6c103d7..18325401d9abc8 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts @@ -51,10 +51,10 @@ import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { CASES_URL } from '../urls/navigation'; -describe.skip('Cases', () => { +describe('Cases', () => { const mycase = { ...case1 }; - before(() => { + beforeEach(() => { cleanKibana(); createTimeline(case1.timeline).then((response) => { mycase.timeline.id = response.body.data.persistTimeline.timeline.savedObjectId; diff --git a/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts index c41b79ef336535..e8fd69864cb3e3 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases_connector_options.spec.ts @@ -27,7 +27,7 @@ import { CONNECTOR_CARD_DETAILS, CONNECTOR_TITLE } from '../screens/case_details import { cleanKibana } from '../tasks/common'; describe('Cases connector incident fields', () => { - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('GET', '/api/cases/configure/connectors/_find', mockConnectorsResponse); cy.intercept('POST', `/api/actions/action/${connectorIds.jira}/_execute`, (req) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts index 8bd9f5b09f2c85..9e39a210c1113b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases_connectors.spec.ts @@ -38,7 +38,7 @@ describe('Cases connectors', () => { ], version: 'WzEwNCwxXQ==', }; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', '/api/actions/action').as('createConnector'); cy.intercept('POST', '/api/cases/configure', (req) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts index f7a19fa281bee7..4e34dcac1873df 100644 --- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts @@ -23,7 +23,6 @@ import { openEvents } from '../tasks/hosts/main'; import { addsHostGeoCityNameToHeader, addsHostGeoCountryNameToHeader, - closeModal, dragAndDropColumn, openEventsViewerFieldsBrowser, opensInspectQueryModal, @@ -63,7 +62,7 @@ describe.skip('Events Viewer', () => { }); it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', 'default ECS'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); }); it('displays a checked checkbox for all of the default events viewer columns that are also in the default ECS category', () => { @@ -80,11 +79,6 @@ describe.skip('Events Viewer', () => { openEvents(); }); - after(() => { - closeModal(); - cy.get(INSPECT_MODAL).should('not.exist'); - }); - it('launches the inspect query modal when the inspect button is clicked', () => { waitsForEventsToBeLoaded(); opensInspectQueryModal(); @@ -142,7 +136,7 @@ describe.skip('Events Viewer', () => { .invoke('text') .then((initialNumberOfEvents) => { kqlSearch(`${filterInput}{enter}`); - cy.get(HEADER_SUBTITLE).invoke('text').should('not.equal', initialNumberOfEvents); + cy.get(HEADER_SUBTITLE).should('not.have.text', initialNumberOfEvents); }); }); }); @@ -167,9 +161,9 @@ describe.skip('Events Viewer', () => { const expectedOrderAfterDragAndDrop = 'message@timestamp1host.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; - cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder); + cy.get(HEADERS_GROUP).should('have.text', originalColumnOrder); dragAndDropColumn({ column: 0, newPosition: 0 }); - cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop); + cy.get(HEADERS_GROUP).should('have.text', expectedOrderAfterDragAndDrop); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts index d99981b42d0497..98cb7418a08a66 100644 --- a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts @@ -46,7 +46,7 @@ const defaultHeaders = [ ]; describe('Fields Browser', () => { - context.skip('Fields Browser rendering', () => { + context('Fields Browser rendering', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); @@ -60,13 +60,14 @@ describe('Fields Browser', () => { }); it('displays the `default ECS` category (by default)', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', 'default ECS'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', 'default ECS'); }); it('the `defaultECS` (selected) category count matches the default timeline header count', () => { - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT) - .invoke('text') - .should('eq', `${defaultHeaders.length}`); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should( + 'have.text', + `${defaultHeaders.length}` + ); }); it('displays a checked checkbox for all of the default timeline columns', () => { @@ -80,7 +81,7 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).invoke('text').should('eq', '2 categories'); + cy.get(FIELDS_BROWSER_CATEGORIES_COUNT).should('have.text', '2 categories'); }); it('displays a search results label with the expected count of fields matching the filter input', () => { @@ -94,9 +95,10 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_SYSTEM_CATEGORIES_COUNT) .invoke('text') .then((systemCategoriesCount) => { - cy.get(FIELDS_BROWSER_FIELDS_COUNT) - .invoke('text') - .should('eq', `${+hostCategoriesCount + +systemCategoriesCount} fields`); + cy.get(FIELDS_BROWSER_FIELDS_COUNT).should( + 'have.text', + `${+hostCategoriesCount + +systemCategoriesCount} fields` + ); }); }); }); @@ -106,11 +108,11 @@ describe('Fields Browser', () => { filterFieldsBrowser(filterInput); - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).invoke('text').should('eq', '4'); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_COUNT).should('have.text', '4'); }); }); - context.skip('Editing the timeline', () => { + context('Editing the timeline', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); @@ -137,7 +139,7 @@ describe('Fields Browser', () => { const category = 'host'; filterFieldsBrowser(category); - cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).invoke('text').should('eq', category); + cy.get(FIELDS_BROWSER_SELECTED_CATEGORY_TITLE).should('have.text', category); }); it('adds a field to the timeline when the user clicks the checkbox', () => { @@ -151,7 +153,7 @@ describe('Fields Browser', () => { cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist'); }); - it('adds a field to the timeline when the user drags and drops a field', () => { + it.skip('adds a field to the timeline when the user drags and drops a field', () => { const filterInput = 'host.geo.c'; filterFieldsBrowser(filterInput); diff --git a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts index b441d33d34baf8..aa126e2f33c905 100644 --- a/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/sourcerer.spec.ts @@ -28,7 +28,7 @@ import { populateTimeline } from '../tasks/timeline'; import { SERVER_SIDE_EVENT_COUNT } from '../screens/timeline'; import { cleanKibana } from '../tasks/common'; -describe('Sourcerer', () => { +describe.skip('Sourcerer', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts index 74bf4f03b0b14e..bbb6f672f11126 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts @@ -12,11 +12,11 @@ import { selectCase, } from '../tasks/timeline'; import { DESCRIPTION_INPUT, ADD_COMMENT_INPUT } from '../screens/create_new_case'; -import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; -import { TIMELINE_CASE_ID } from '../objects/case'; -import { caseTimeline, timeline } from '../objects/timeline'; -import { createTimeline, deleteTimeline } from '../tasks/api_calls/timelines'; +import { case1 } from '../objects/case'; +import { timeline } from '../objects/timeline'; +import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; +import { createCase } from '../tasks/api_calls/cases'; describe('attach timeline to case', () => { const myTimeline = { ...timeline }; @@ -29,10 +29,6 @@ describe('attach timeline to case', () => { }); }); - after(() => { - deleteTimeline(myTimeline.id!); - }); - it('attach timeline to a new case', () => { loginAndWaitForTimeline(myTimeline.id!); attachTimelineToNewCase(); @@ -62,25 +58,29 @@ describe('attach timeline to case', () => { }); context('with cases created', () => { + let timelineId: string; + let caseId: string; before(() => { cleanKibana(); - esArchiverLoad('case_and_timeline'); + createTimeline(timeline).then((response) => { + timelineId = response.body.data.persistTimeline.timeline.savedObjectId; + }); + createCase(case1).then((response) => { + caseId = response.body.id; + }); }); it('attach timeline to an existing case', () => { - loginAndWaitForTimeline(caseTimeline.id!); + loginAndWaitForTimeline(timelineId); attachTimelineToExistingCase(); - selectCase(TIMELINE_CASE_ID); + selectCase(caseId); cy.location('origin').then((origin) => { cy.get(ADD_COMMENT_INPUT).should( 'have.text', - `[${ - caseTimeline.title - }](${origin}/app/security/timelines?timeline=(id:%27${caseTimeline.id!}%27,isOpen:!t))` + `[${timeline.title}](${origin}/app/security/timelines?timeline=(id:%27${timelineId}%27,isOpen:!t))` ); }); - esArchiverUnload('case_and_timeline'); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts index 5d44c057c7383a..a926a5ac4938a7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts @@ -9,9 +9,9 @@ import { FAVORITE_TIMELINE, LOCKED_ICON, NOTES_TAB_BUTTON, + NOTES_TEXT, // NOTES_COUNT, NOTES_TEXT_AREA, - NOTE_CONTENT, PIN_EVENT, TIMELINE_DESCRIPTION, TIMELINE_FILTER, @@ -25,7 +25,6 @@ import { TIMELINES_NOTES_COUNT, TIMELINES_FAVORITE, } from '../screens/timelines'; -import { getTimelineById } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPage } from '../tasks/login'; @@ -47,11 +46,10 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/79389 -describe.skip('Timelines', () => { +describe('Timelines', () => { let timelineId: string; - before(() => { + beforeEach(() => { cleanKibana(); }); @@ -98,15 +96,10 @@ describe.skip('Timelines', () => { cy.get(PIN_EVENT) .should('have.attr', 'aria-label') .and('match', /Unpin the event in row 2/); - cy.get(LOCKED_ICON).should('be.visible'); cy.get(NOTES_TAB_BUTTON).click(); cy.get(NOTES_TEXT_AREA).should('exist'); - getTimelineById(timelineId).then((singleTimeline) => { - const noteId = singleTimeline!.body.data.getOneTimeline.notes[0].noteId; - - cy.get(NOTE_CONTENT(noteId)).should('have.text', timeline.notes); - }); + cy.get(NOTES_TEXT).should('have.text', timeline.notes); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index 8b84ae7815452c..1d0256dbfbdc92 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -15,7 +15,7 @@ import { removeColumn } from '../tasks/timeline'; // Failing: See https://github.com/elastic/kibana/issues/75794 describe.skip('persistent timeline', () => { - before(() => { + beforeEach(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); openEvents(); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts index f1aaa4ab8b980a..5672a232e04850 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts @@ -44,7 +44,7 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; describe('Timeline Templates', () => { - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('PATCH', '/api/timeline').as('timeline'); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts index 015c0fc80e2923..f2af37c939d023 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates_export.spec.ts @@ -19,7 +19,7 @@ describe('Export timelines', () => { let templateResponse: Cypress.Response; let templateId: string; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', 'api/timeline/_export?file_name=timelines_export.ndjson').as('export'); createTimelineTemplate(timelineTemplate).then((response) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts index 9a03936c3683fe..705aff7b14c6c7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_toggle_column.spec.ts @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { timeline } from '../objects/timeline'; import { ID_HEADER_FIELD, ID_TOGGLE_FIELD, TIMESTAMP_HEADER_FIELD, TIMESTAMP_TOGGLE_FIELD, } from '../screens/timeline'; -import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { loginAndWaitForPage } from '../tasks/login'; @@ -28,13 +26,11 @@ import { import { HOSTS_URL } from '../urls/navigation'; -describe('toggle column in timeline', () => { +describe.skip('toggle column in timeline', () => { before(() => { cleanKibana(); cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export'); - createTimeline(timeline).then((response) => { - loginAndWaitForPage(HOSTS_URL); - }); + loginAndWaitForPage(HOSTS_URL); }); beforeEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts index 064d98bf01b24a..a75074baeef54e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines_export.spec.ts @@ -15,7 +15,7 @@ import { cleanKibana } from '../tasks/common'; describe('Export timelines', () => { let timelineResponse: Cypress.Response; let timelineId: string; - before(() => { + beforeEach(() => { cleanKibana(); cy.intercept('POST', '/api/timeline/_export?file_name=timelines_export.ndjson').as('export'); createTimeline(timeline).then((response) => { diff --git a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts index 58ef4cd2d96bac..cf433891ac9291 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts @@ -19,7 +19,7 @@ const ABSOLUTE_DATE = { startTime: '2019-08-01T20:03:29.186Z', }; -describe('URL compatibility', () => { +describe.skip('URL compatibility', () => { before(() => { cleanKibana(); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts b/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts index 0b1ab12f37c91f..ae0c4f35177a9c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/value_lists.spec.ts @@ -26,7 +26,6 @@ import { exportValueList, } from '../tasks/lists'; import { VALUE_LISTS_TABLE, VALUE_LISTS_ROW, VALUE_LISTS_MODAL_ACTIVATOR } from '../screens/lists'; -import { removeSignalsIndex } from '../tasks/api_calls/rules'; import { cleanKibana } from '../tasks/common'; describe('value lists', () => { @@ -36,7 +35,6 @@ describe('value lists', () => { }); beforeEach(() => { - removeSignalsIndex(); loginAndWaitForPageWithoutDateRange(DETECTIONS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); @@ -46,7 +44,6 @@ describe('value lists', () => { }); afterEach(() => { - removeSignalsIndex(); deleteAllValueListsFromUI(); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index d48ac26472c71d..c4515379eaeb25 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -176,18 +176,11 @@ export const newRule: CustomRule = { }; export const existingRule: CustomRule = { - customQuery: 'host.name:*', + customQuery: 'host.name: *', name: 'Rule 1', description: 'Description for Rule 1', - index: [ - 'apm-*-transaction*', - 'auditbeat-*', - 'endgame-*', - 'filebeat-*', - 'packetbeat-*', - 'winlogbeat-*', - ], - interval: '4m', + index: ['auditbeat-*'], + interval: '10s', severity: 'High', riskScore: '19', tags: ['rule1'], @@ -203,7 +196,7 @@ export const existingRule: CustomRule = { export const newOverrideRule: OverrideRule = { customQuery: 'host.name: *', index: indexPatterns, - name: 'New Rule Test', + name: 'Override Rule', description: 'The new rule description.', severity: 'High', riskScore: '17', @@ -224,7 +217,7 @@ export const newOverrideRule: OverrideRule = { export const newThresholdRule: ThresholdRule = { customQuery: 'host.name: *', index: indexPatterns, - name: 'New Rule Test', + name: 'Threshold Rule', description: 'The new rule description.', severity: 'High', riskScore: '17', diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts index 5ac8cd8f6cc9f7..d13102620ec192 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_detection_rules.ts @@ -26,6 +26,8 @@ export const FIFTH_RULE = 4; export const FIRST_RULE = 0; +export const FOURTH_RULE = 3; + export const LOAD_PREBUILT_RULES_BTN = '[data-test-subj="load-prebuilt-rules"]'; export const LOADING_INITIAL_PREBUILT_RULES_TABLE = diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 17567b61ad3145..9db30a174ae086 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -33,8 +33,7 @@ export const COMBO_BOX_RESULT = '.euiFilterSelectItem'; export const CREATE_AND_ACTIVATE_BTN = '[data-test-subj="create-activate"]'; -export const CUSTOM_QUERY_INPUT = - '[data-test-subj="detectionEngineStepDefineRuleQueryBar"] [data-test-subj="queryInput"]'; +export const CUSTOM_QUERY_INPUT = '[data-test-subj="queryInput"]'; export const THREAT_MATCH_QUERY_INPUT = '[data-test-subj="detectionEngineStepDefineThreatRuleQueryBar"] [data-test-subj="queryInput"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 6f31a470dd61ed..ea3c42e2650eb0 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -53,7 +53,7 @@ export const LOCKED_ICON = '[data-test-subj="timeline-date-picker-lock-button"]' export const NOTES = '[data-test-subj="note-card-body"]'; -const NOTE_BY_NOTE_ID = (noteId: string) => `[data-test-subj="note-preview-${noteId}"]`; +export const NOTE_BY_NOTE_ID = (noteId: string) => `[data-test-subj="note-preview-${noteId}"]`; export const NOTE_CONTENT = (noteId: string) => `${NOTE_BY_NOTE_ID(noteId)} p`; @@ -61,6 +61,8 @@ export const NOTES_TEXT_AREA = '[data-test-subj="add-a-note"] textarea'; export const NOTES_TAB_BUTTON = '[data-test-subj="timelineTabs-notes"]'; +export const NOTES_TEXT = '.euiMarkdownFormat'; + export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]'; export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts new file mode 100644 index 00000000000000..4510ebf254ee72 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/cases.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TestCase } from '../../objects/case'; + +export const createCase = (newCase: TestCase) => + cy.request({ + method: 'POST', + url: 'api/cases', + body: { + description: newCase.description, + title: newCase.name, + tags: ['tag'], + connector: { + id: 'none', + name: 'none', + type: '.none', + fields: null, + }, + settings: { + syncAlerts: true, + }, + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 34fc00428d2cd4..29cdf4ec2be5d0 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -6,12 +6,12 @@ import { CustomRule } from '../../objects/rule'; -export const createCustomRule = (rule: CustomRule) => +export const createCustomRule = (rule: CustomRule, ruleId = 'rule_testing') => cy.request({ method: 'POST', url: 'api/detection_engine/rules', body: { - rule_id: 'rule_testing', + rule_id: ruleId, risk_score: parseInt(rule.riskScore, 10), description: rule.description, interval: '10s', @@ -27,11 +27,34 @@ export const createCustomRule = (rule: CustomRule) => headers: { 'kbn-xsrf': 'cypress-creds' }, }); -export const deleteCustomRule = () => { +export const createCustomRuleActivated = (rule: CustomRule, ruleId = 'rule_testing') => + cy.request({ + method: 'POST', + url: 'api/detection_engine/rules', + body: { + rule_id: ruleId, + risk_score: parseInt(rule.riskScore, 10), + description: rule.description, + interval: '10s', + name: rule.name, + severity: rule.severity.toLocaleLowerCase(), + type: 'query', + from: 'now-17520h', + index: ['auditbeat-*'], + query: rule.customQuery, + language: 'kuery', + enabled: true, + tags: ['rule1'], + }, + headers: { 'kbn-xsrf': 'cypress-creds' }, + }); + +export const deleteCustomRule = (ruleId = 'rule_testing') => { cy.request({ method: 'DELETE', - url: 'api/detection_engine/rules?rule_id=rule_testing', + url: `api/detection_engine/rules?rule_id=${ruleId}`, headers: { 'kbn-xsrf': 'cypress-creds' }, + failOnStatusCode: false, }); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index fbd4c4145e8ff7..b6625a76981e85 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { removeSignalsIndex } from './api_calls/rules'; import { esArchiverLoadEmptyKibana } from './es_archiver'; const primaryButton = 0; @@ -65,5 +66,23 @@ export const reload = (afterReload: () => void) => { export const cleanKibana = () => { cy.exec(`curl -XDELETE "${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*" -k`); + + // We wait until the kibana indexes are deleted + cy.waitUntil(() => { + cy.wait(500); + return cy + .request(`${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*`) + .then((response) => JSON.stringify(response.body) === '{}'); + }); esArchiverLoadEmptyKibana(); + + // We wait until the kibana indexes are created + cy.waitUntil(() => { + cy.wait(500); + return cy + .request(`${Cypress.env('ELASTICSEARCH_URL')}/.kibana\*`) + .then((response) => JSON.stringify(response.body) !== '{}'); + }); + + removeSignalsIndex(); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts index 401a78767ac578..3e6b0ec0afaaa9 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts @@ -42,7 +42,7 @@ export const loadMoreEvents = () => { export const openEventsViewerFieldsBrowser = () => { cy.get(EVENTS_VIEWER_FIELDS_BUTTON).click({ force: true }); - cy.get(SERVER_SIDE_EVENT_COUNT).invoke('text').should('not.equal', '0'); + cy.get(SERVER_SIDE_EVENT_COUNT).should('not.have.text', '0'); cy.get(FIELDS_BROWSER_CONTAINER).should('exist'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index fee1bc4ae68920..0361bf4b72b525 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -166,12 +166,7 @@ export const pinFirstEvent = () => { export const populateTimeline = () => { executeTimelineKQL(hostExistsQuery); - cy.get(SERVER_SIDE_EVENT_COUNT) - .invoke('text') - .then((strCount) => { - const intCount = +strCount; - cy.wrap(intCount).should('be.above', 0); - }); + cy.get(SERVER_SIDE_EVENT_COUNT).should('not.have.text', '0'); }; export const unpinFirstEvent = () => { diff --git a/x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/alerts/data.json.gz deleted file mode 100644 index c0d7fb18bbdb2cecdd96b293730dcef7dbd0aa0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58602 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

    <@<3bF$Vx!=P?#9{ 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 diff --git a/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json deleted file mode 100644 index 00a2f6fb8c8df5..00000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/alerts/mappings.json +++ /dev/null @@ -1,8124 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "auditbeat-7.6.0": { - "is_write_index": true - } - }, - "index": "auditbeat-7.6.0-2020.03.11-000001", - "mappings": { - "_meta": { - "beat": "auditbeat", - "version": "7.6.0" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "auditd": { - "properties": { - "data": { - "properties": { - "a0": { - "ignore_above": 1024, - "type": "keyword" - }, - "a1": { - "ignore_above": 1024, - "type": "keyword" - }, - "a2": { - "ignore_above": 1024, - "type": "keyword" - }, - "a3": { - "ignore_above": 1024, - "type": "keyword" - }, - "a[0-3]": { - "ignore_above": 1024, - "type": "keyword" - }, - "acct": { - "ignore_above": 1024, - "type": "keyword" - }, - "acl": { - "ignore_above": 1024, - "type": "keyword" - }, - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "added": { - "ignore_above": 1024, - "type": "keyword" - }, - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "apparmor": { - "ignore_above": 1024, - "type": "keyword" - }, - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "argc": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_limit": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_wait_time": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_failure": { - "ignore_above": 1024, - "type": "keyword" - }, - "banners": { - "ignore_above": 1024, - "type": "keyword" - }, - "bool": { - "ignore_above": 1024, - "type": "keyword" - }, - "bus": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "capability": { - "ignore_above": 1024, - "type": "keyword" - }, - "cgroup": { - "ignore_above": 1024, - "type": "keyword" - }, - "changed": { - "ignore_above": 1024, - "type": "keyword" - }, - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "compat": { - "ignore_above": 1024, - "type": "keyword" - }, - "daddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "default-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "dmac": { - "ignore_above": 1024, - "type": "keyword" - }, - "dport": { - "ignore_above": 1024, - "type": "keyword" - }, - "enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "entries": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit": { - "ignore_above": 1024, - "type": "keyword" - }, - "fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "fd": { - "ignore_above": 1024, - "type": "keyword" - }, - "fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "feature": { - "ignore_above": 1024, - "type": "keyword" - }, - "fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "file": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "format": { - "ignore_above": 1024, - "type": "keyword" - }, - "fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "grantors": { - "ignore_above": 1024, - "type": "keyword" - }, - "grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "hook": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "icmp_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "igid": { - "ignore_above": 1024, - "type": "keyword" - }, - "img-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "inif": { - "ignore_above": 1024, - "type": "keyword" - }, - "ino": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "invalid_context": { - "ignore_above": 1024, - "type": "keyword" - }, - "ioctlcmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipx-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "items": { - "ignore_above": 1024, - "type": "keyword" - }, - "iuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "ksize": { - "ignore_above": 1024, - "type": "keyword" - }, - "laddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "len": { - "ignore_above": 1024, - "type": "keyword" - }, - "list": { - "ignore_above": 1024, - "type": "keyword" - }, - "lport": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "macproto": { - "ignore_above": 1024, - "type": "keyword" - }, - "maj": { - "ignore_above": 1024, - "type": "keyword" - }, - "major": { - "ignore_above": 1024, - "type": "keyword" - }, - "minor": { - "ignore_above": 1024, - "type": "keyword" - }, - "model": { - "ignore_above": 1024, - "type": "keyword" - }, - "msg": { - "ignore_above": 1024, - "type": "keyword" - }, - "nargs": { - "ignore_above": 1024, - "type": "keyword" - }, - "net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ocomm": { - "ignore_above": 1024, - "type": "keyword" - }, - "oflag": { - "ignore_above": 1024, - "type": "keyword" - }, - "old": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_val": { - "ignore_above": 1024, - "type": "keyword" - }, - "op": { - "ignore_above": 1024, - "type": "keyword" - }, - "opid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oses": { - "ignore_above": 1024, - "type": "keyword" - }, - "outif": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - }, - "per": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm_mask": { - "ignore_above": 1024, - "type": "keyword" - }, - "permissive": { - "ignore_above": 1024, - "type": "keyword" - }, - "pfs": { - "ignore_above": 1024, - "type": "keyword" - }, - "printer": { - "ignore_above": 1024, - "type": "keyword" - }, - "prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "proto": { - "ignore_above": 1024, - "type": "keyword" - }, - "qbytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "range": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "removed": { - "ignore_above": 1024, - "type": "keyword" - }, - "res": { - "ignore_above": 1024, - "type": "keyword" - }, - "resrc": { - "ignore_above": 1024, - "type": "keyword" - }, - "rport": { - "ignore_above": 1024, - "type": "keyword" - }, - "sauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "scontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "selected-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperm": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperms": { - "ignore_above": 1024, - "type": "keyword" - }, - "seqno": { - "ignore_above": 1024, - "type": "keyword" - }, - "seresult": { - "ignore_above": 1024, - "type": "keyword" - }, - "ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "sig": { - "ignore_above": 1024, - "type": "keyword" - }, - "sigev_signo": { - "ignore_above": 1024, - "type": "keyword" - }, - "smac": { - "ignore_above": 1024, - "type": "keyword" - }, - "socket": { - "properties": { - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "ignore_above": 1024, - "type": "keyword" - }, - "saddr": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "spid": { - "ignore_above": 1024, - "type": "keyword" - }, - "sport": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "subj": { - "ignore_above": 1024, - "type": "keyword" - }, - "success": { - "ignore_above": 1024, - "type": "keyword" - }, - "syscall": { - "ignore_above": 1024, - "type": "keyword" - }, - "table": { - "ignore_above": 1024, - "type": "keyword" - }, - "tclass": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - }, - "uri": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "val": { - "ignore_above": 1024, - "type": "keyword" - }, - "ver": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "watch": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "paths": { - "properties": { - "dev": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "item": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "nametype": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_role": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_user": { - "ignore_above": 1024, - "type": "keyword" - }, - "objtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "ogid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ouid": { - "ignore_above": 1024, - "type": "keyword" - }, - "rdev": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "session": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "actor": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "how": { - "ignore_above": 1024, - "type": "keyword" - }, - "object": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "fields": { - "raw": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "selinux": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "setgid": { - "type": "boolean" - }, - "setuid": { - "type": "boolean" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geoip": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "socket": { - "properties": { - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "system": { - "properties": { - "audit": { - "properties": { - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "boottime": { - "type": "date" - }, - "containerized": { - "type": "boolean" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timezone": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "package": { - "properties": { - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "installtime": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "release": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "summary": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "properties": { - "last_changed": { - "type": "date" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shell": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_information": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "audit": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "effective": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "filesystem": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name_map": { - "type": "object" - }, - "saved": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "selinux": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": "auditbeat", - "rollover_alias": "auditbeat-7.6.0" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "raw", - "file.origin", - "file.selinux.user", - "file.selinux.role", - "file.selinux.domain", - "file.selinux.level", - "user.audit.id", - "user.audit.name", - "user.effective.id", - "user.effective.name", - "user.effective.group.id", - "user.effective.group.name", - "user.filesystem.id", - "user.filesystem.name", - "user.filesystem.group.id", - "user.filesystem.group.name", - "user.saved.id", - "user.saved.name", - "user.saved.group.id", - "user.saved.group.name", - "user.selinux.user", - "user.selinux.role", - "user.selinux.domain", - "user.selinux.level", - "user.selinux.category", - "source.path", - "destination.path", - "auditd.message_type", - "auditd.session", - "auditd.result", - "auditd.summary.actor.primary", - "auditd.summary.actor.secondary", - "auditd.summary.object.type", - "auditd.summary.object.primary", - "auditd.summary.object.secondary", - "auditd.summary.how", - "auditd.paths.inode", - "auditd.paths.dev", - "auditd.paths.obj_user", - "auditd.paths.obj_role", - "auditd.paths.obj_domain", - "auditd.paths.obj_level", - "auditd.paths.objtype", - "auditd.paths.ouid", - "auditd.paths.rdev", - "auditd.paths.nametype", - "auditd.paths.ogid", - "auditd.paths.item", - "auditd.paths.mode", - "auditd.paths.name", - "auditd.data.action", - "auditd.data.minor", - "auditd.data.acct", - "auditd.data.addr", - "auditd.data.cipher", - "auditd.data.id", - "auditd.data.entries", - "auditd.data.kind", - "auditd.data.ksize", - "auditd.data.spid", - "auditd.data.arch", - "auditd.data.argc", - "auditd.data.major", - "auditd.data.unit", - "auditd.data.table", - "auditd.data.terminal", - "auditd.data.grantors", - "auditd.data.direction", - "auditd.data.op", - "auditd.data.tty", - "auditd.data.syscall", - "auditd.data.data", - "auditd.data.family", - "auditd.data.mac", - "auditd.data.pfs", - "auditd.data.items", - "auditd.data.a0", - "auditd.data.a1", - "auditd.data.a2", - "auditd.data.a3", - "auditd.data.hostname", - "auditd.data.lport", - "auditd.data.rport", - "auditd.data.exit", - "auditd.data.fp", - "auditd.data.laddr", - "auditd.data.sport", - "auditd.data.capability", - "auditd.data.nargs", - "auditd.data.new-enabled", - "auditd.data.audit_backlog_limit", - "auditd.data.dir", - "auditd.data.cap_pe", - "auditd.data.model", - "auditd.data.new_pp", - "auditd.data.old-enabled", - "auditd.data.oauid", - "auditd.data.old", - "auditd.data.banners", - "auditd.data.feature", - "auditd.data.vm-ctx", - "auditd.data.opid", - "auditd.data.seperms", - "auditd.data.seresult", - "auditd.data.new-rng", - "auditd.data.old-net", - "auditd.data.sigev_signo", - "auditd.data.ino", - "auditd.data.old_enforcing", - "auditd.data.old-vcpu", - "auditd.data.range", - "auditd.data.res", - "auditd.data.added", - "auditd.data.fam", - "auditd.data.nlnk-pid", - "auditd.data.subj", - "auditd.data.a[0-3]", - "auditd.data.cgroup", - "auditd.data.kernel", - "auditd.data.ocomm", - "auditd.data.new-net", - "auditd.data.permissive", - "auditd.data.class", - "auditd.data.compat", - "auditd.data.fi", - "auditd.data.changed", - "auditd.data.msg", - "auditd.data.dport", - "auditd.data.new-seuser", - "auditd.data.invalid_context", - "auditd.data.dmac", - "auditd.data.ipx-net", - "auditd.data.iuid", - "auditd.data.macproto", - "auditd.data.obj", - "auditd.data.ipid", - "auditd.data.new-fs", - "auditd.data.vm-pid", - "auditd.data.cap_pi", - "auditd.data.old-auid", - "auditd.data.oses", - "auditd.data.fd", - "auditd.data.igid", - "auditd.data.new-disk", - "auditd.data.parent", - "auditd.data.len", - "auditd.data.oflag", - "auditd.data.uuid", - "auditd.data.code", - "auditd.data.nlnk-grp", - "auditd.data.cap_fp", - "auditd.data.new-mem", - "auditd.data.seperm", - "auditd.data.enforcing", - "auditd.data.new-chardev", - "auditd.data.old-rng", - "auditd.data.outif", - "auditd.data.cmd", - "auditd.data.hook", - "auditd.data.new-level", - "auditd.data.sauid", - "auditd.data.sig", - "auditd.data.audit_backlog_wait_time", - "auditd.data.printer", - "auditd.data.old-mem", - "auditd.data.perm", - "auditd.data.old_pi", - "auditd.data.state", - "auditd.data.format", - "auditd.data.new_gid", - "auditd.data.tcontext", - "auditd.data.maj", - "auditd.data.watch", - "auditd.data.device", - "auditd.data.grp", - "auditd.data.bool", - "auditd.data.icmp_type", - "auditd.data.new_lock", - "auditd.data.old_prom", - "auditd.data.acl", - "auditd.data.ip", - "auditd.data.new_pi", - "auditd.data.default-context", - "auditd.data.inode_gid", - "auditd.data.new-log_passwd", - "auditd.data.new_pe", - "auditd.data.selected-context", - "auditd.data.cap_fver", - "auditd.data.file", - "auditd.data.net", - "auditd.data.virt", - "auditd.data.cap_pp", - "auditd.data.old-range", - "auditd.data.resrc", - "auditd.data.new-range", - "auditd.data.obj_gid", - "auditd.data.proto", - "auditd.data.old-disk", - "auditd.data.audit_failure", - "auditd.data.inif", - "auditd.data.vm", - "auditd.data.flags", - "auditd.data.nlnk-fam", - "auditd.data.old-fs", - "auditd.data.old-ses", - "auditd.data.seqno", - "auditd.data.fver", - "auditd.data.qbytes", - "auditd.data.seuser", - "auditd.data.cap_fe", - "auditd.data.new-vcpu", - "auditd.data.old-level", - "auditd.data.old_pp", - "auditd.data.daddr", - "auditd.data.old-role", - "auditd.data.ioctlcmd", - "auditd.data.smac", - "auditd.data.apparmor", - "auditd.data.fe", - "auditd.data.perm_mask", - "auditd.data.ses", - "auditd.data.cap_fi", - "auditd.data.obj_uid", - "auditd.data.reason", - "auditd.data.list", - "auditd.data.old_lock", - "auditd.data.bus", - "auditd.data.old_pe", - "auditd.data.new-role", - "auditd.data.prom", - "auditd.data.uri", - "auditd.data.audit_enabled", - "auditd.data.old-log_passwd", - "auditd.data.old-seuser", - "auditd.data.per", - "auditd.data.scontext", - "auditd.data.tclass", - "auditd.data.ver", - "auditd.data.new", - "auditd.data.val", - "auditd.data.img-ctx", - "auditd.data.old-chardev", - "auditd.data.old_val", - "auditd.data.success", - "auditd.data.inode_uid", - "auditd.data.removed", - "auditd.data.socket.port", - "auditd.data.socket.saddr", - "auditd.data.socket.addr", - "auditd.data.socket.family", - "auditd.data.socket.path", - "geoip.continent_name", - "geoip.city_name", - "geoip.region_name", - "geoip.country_iso_code", - "hash.blake2b_256", - "hash.blake2b_384", - "hash.blake2b_512", - "hash.md5", - "hash.sha1", - "hash.sha224", - "hash.sha256", - "hash.sha384", - "hash.sha3_224", - "hash.sha3_256", - "hash.sha3_384", - "hash.sha3_512", - "hash.sha512", - "hash.sha512_224", - "hash.sha512_256", - "hash.xxh64", - "event.origin", - "user.entity_id", - "user.terminal", - "process.entity_id", - "process.hash.blake2b_256", - "process.hash.blake2b_384", - "process.hash.blake2b_512", - "process.hash.sha224", - "process.hash.sha384", - "process.hash.sha3_224", - "process.hash.sha3_256", - "process.hash.sha3_384", - "process.hash.sha3_512", - "process.hash.sha512_224", - "process.hash.sha512_256", - "process.hash.xxh64", - "socket.entity_id", - "system.audit.host.timezone.name", - "system.audit.host.hostname", - "system.audit.host.id", - "system.audit.host.architecture", - "system.audit.host.mac", - "system.audit.host.os.codename", - "system.audit.host.os.platform", - "system.audit.host.os.name", - "system.audit.host.os.family", - "system.audit.host.os.version", - "system.audit.host.os.kernel", - "system.audit.package.entity_id", - "system.audit.package.name", - "system.audit.package.version", - "system.audit.package.release", - "system.audit.package.arch", - "system.audit.package.license", - "system.audit.package.summary", - "system.audit.package.url", - "system.audit.user.name", - "system.audit.user.uid", - "system.audit.user.gid", - "system.audit.user.dir", - "system.audit.user.shell", - "system.audit.user.user_information", - "system.audit.user.password.type", - "fields.*" - ] - }, - "refresh_interval": "5s" - } - } - } -} diff --git a/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/case_and_timeline/data.json.gz deleted file mode 100644 index 5838d18e1c7dd2c5907cc5f5ef9f55d4cfe2a8d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3687 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 z 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 ahRbA diff --git a/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json b/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json deleted file mode 100644 index a6b171cdfd7d97..00000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/custom_rules/mappings.json +++ /dev/null @@ -1,6243 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "agent_configs": "38abaf89513877745c359e7700c0c66a", - "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", - "agents": "75c0f4a11560dbc38b65e5e1d98fc9da", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "e8619030e08b671291af04c4603b4944", - "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", - "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "cases": "08b8b110dbca273d37e8aef131ecab61", - "cases-comments": "df3c1aa1b3dd5737c94d9e430b13c48a", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "ae24d22d5986d04124cc6568f771066f", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", - "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", - "epm-package": "75d12cd13c867fd713d7dfb27366bc20", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "9ecce5b58867403613d82fe496470b34", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "outputs": "aee9782e0d500b867859650a36280165", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "agent_configs": { - "properties": { - "datasources": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "text" - }, - "namespace": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "updated_on": { - "type": "keyword" - } - } - }, - "agent_events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "actions": { - "properties": { - "created_at": { - "type": "date" - }, - "data": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "active": { - "type": "boolean" - }, - "config_id": { - "type": "keyword" - }, - "config_newest_revision": { - "type": "integer" - }, - "config_revision": { - "type": "integer" - }, - "current_error_events": { - "type": "text" - }, - "default_api_key": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "text" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "text" - }, - "version": { - "type": "keyword" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - }, - "executionStatus": { - "properties": { - "status": { - "type": "keyword" - }, - "lastExecutionDate": { - "type": "date" - }, - "error": { - "properties": { - "reason": { - "type": "keyword" - }, - "message": { - "type": "keyword" - } - } - } - } - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "name": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 256, - "type": "keyword" - }, - "name": { - "ignore_above": 256, - "type": "keyword" - }, - "version": { - "ignore_above": 256, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "cardinality": { - "properties": { - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "application_usage_totals": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - } - } - }, - "application_usage_transactional": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - }, - "timestamp": { - "type": "date" - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "datasources": { - "properties": { - "config_id": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" - }, - "inputs": { - "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "processors": { - "type": "keyword" - }, - "streams": { - "properties": { - "config": { - "type": "flattened" - }, - "dataset": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "processors": { - "type": "keyword" - } - }, - "type": "nested" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "output_id": { - "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "revision": { - "type": "integer" - } - } - }, - "enrollment_api_keys": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "expire_at": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "epm-package": { - "properties": { - "installed": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customMetrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "outputs": { - "properties": { - "api_key": { - "type": "keyword" - }, - "ca_sha256": { - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "bulkCreateTimeDurations": { - "type": "float" - }, - "gap": { - "type": "text" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastLookBackDate": { - "type": "date" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "searchAfterTimeDurations": { - "type": "float" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "spaceId": { - "type": "keyword" - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "uptime-dynamic-settings": { - "properties": { - "heartbeatIndices": { - "type": "keyword" - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/data.json.gz deleted file mode 100644 index 0bec9975031467ec2e01b4dce2cb464ba59748d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42571 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 D_fun! diff --git a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json b/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json deleted file mode 100644 index 7ef00495390ee0..00000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/prebuilt_rules_loaded/mappings.json +++ /dev/null @@ -1,2967 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "6e96ac5e648f57523879661ea72525b7", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "agent_actions": "ed270b46812f0fa1439366c428a2cf17", - "agent_configs": "38abaf89513877745c359e7700c0c66a", - "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", - "agents": "c3eeb7b9d97176f15f6d126370ab23c7", - "alert": "7b44fba6773e37c806ce290ea9b7024e", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", - "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", - "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "cases": "08b8b110dbca273d37e8aef131ecab61", - "cases-comments": "c2061fb929f585df57425102fa928b4b", - "cases-configure": "42711cbb311976c0687853f4c1354572", - "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", - "config": "ae24d22d5986d04124cc6568f771066f", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", - "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", - "epm-package": "0be91c6758421dd5d0f1a58e9e5bc7c3", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "9ecce5b58867403613d82fe496470b34", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "outputs": "aee9782e0d500b867859650a36280165", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-actions": "90eee2e4635260f4be0a1da8f5bc0aa0", - "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "36a616f7026dfa617d6655df850fe16d", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "agent_actions": { - "properties": { - "agent_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "data": { - "type": "flattened" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agent_configs": { - "properties": { - "datasources": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "text" - }, - "namespace": { - "type": "keyword" - }, - "revision": { - "type": "integer" - }, - "status": { - "type": "keyword" - }, - "updated_by": { - "type": "keyword" - }, - "updated_on": { - "type": "keyword" - } - } - }, - "agent_events": { - "properties": { - "action_id": { - "type": "keyword" - }, - "agent_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "data": { - "type": "text" - }, - "message": { - "type": "text" - }, - "payload": { - "type": "text" - }, - "stream_id": { - "type": "keyword" - }, - "subtype": { - "type": "keyword" - }, - "timestamp": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, - "agents": { - "properties": { - "access_api_key_id": { - "type": "keyword" - }, - "active": { - "type": "boolean" - }, - "config_id": { - "type": "keyword" - }, - "config_newest_revision": { - "type": "integer" - }, - "config_revision": { - "type": "integer" - }, - "current_error_events": { - "type": "text" - }, - "default_api_key": { - "type": "keyword" - }, - "enrolled_at": { - "type": "date" - }, - "last_checkin": { - "type": "date" - }, - "last_updated": { - "type": "date" - }, - "local_metadata": { - "type": "text" - }, - "shared_id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "user_provided_metadata": { - "type": "text" - }, - "version": { - "type": "keyword" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "language": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "runtime": { - "properties": { - "composite": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - } - } - }, - "cardinality": { - "properties": { - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "application_usage_totals": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - } - } - }, - "application_usage_transactional": { - "properties": { - "appId": { - "type": "keyword" - }, - "minutesOnScreen": { - "type": "float" - }, - "numberOfClicks": { - "type": "long" - }, - "timestamp": { - "type": "date" - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "cases": { - "properties": { - "closed_at": { - "type": "date" - }, - "closed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "description": { - "type": "text" - }, - "external_service": { - "properties": { - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "external_id": { - "type": "keyword" - }, - "external_title": { - "type": "text" - }, - "external_url": { - "type": "text" - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "status": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-comments": { - "properties": { - "comment": { - "type": "text" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "pushed_at": { - "type": "date" - }, - "pushed_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-configure": { - "properties": { - "closure_type": { - "type": "keyword" - }, - "connector_id": { - "type": "keyword" - }, - "connector_name": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - } - } - }, - "cases-user-actions": { - "properties": { - "action": { - "type": "keyword" - }, - "action_at": { - "type": "date" - }, - "action_by": { - "properties": { - "email": { - "type": "keyword" - }, - "full_name": { - "type": "keyword" - }, - "username": { - "type": "keyword" - } - } - }, - "action_field": { - "type": "keyword" - }, - "new_value": { - "type": "text" - }, - "old_value": { - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "datasources": { - "properties": { - "config_id": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "enabled": { - "type": "boolean" - }, - "inputs": { - "properties": { - "config": { - "type": "flattened" - }, - "enabled": { - "type": "boolean" - }, - "processors": { - "type": "keyword" - }, - "streams": { - "properties": { - "config": { - "type": "flattened" - }, - "dataset": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "processors": { - "type": "keyword" - } - }, - "type": "nested" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "name": { - "type": "keyword" - }, - "namespace": { - "type": "keyword" - }, - "output_id": { - "type": "keyword" - }, - "package": { - "properties": { - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "revision": { - "type": "integer" - } - } - }, - "enrollment_api_keys": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "binary" - }, - "api_key_id": { - "type": "keyword" - }, - "config_id": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "expire_at": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - } - } - }, - "epm-package": { - "properties": { - "installed": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "internal": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customMetrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "label": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "outputs": { - "properties": { - "api_key": { - "type": "keyword" - }, - "ca_sha256": { - "type": "keyword" - }, - "config": { - "type": "flattened" - }, - "fleet_enroll_password": { - "type": "binary" - }, - "fleet_enroll_username": { - "type": "binary" - }, - "hosts": { - "type": "keyword" - }, - "is_default": { - "type": "boolean" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-actions": { - "properties": { - "actions": { - "properties": { - "action_type_id": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "params": { - "dynamic": "true", - "type": "object" - } - } - }, - "alertThrottle": { - "type": "keyword" - }, - "ruleAlertId": { - "type": "keyword" - }, - "ruleThrottle": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "bulkCreateTimeDurations": { - "type": "float" - }, - "gap": { - "type": "text" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastLookBackDate": { - "type": "date" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "searchAfterTimeDurations": { - "type": "float" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "allowChangingOptInStatus": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "type": "keyword" - }, - "reportFailureCount": { - "type": "integer" - }, - "reportFailureVersion": { - "type": "keyword" - }, - "sendUsageFrom": { - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "uptime-dynamic-settings": { - "properties": { - "heartbeatIndices": { - "type": "keyword" - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "0", - "number_of_shards": "1" - } - } - } -} \ No newline at end of file diff --git a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz b/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/data.json.gz deleted file mode 100644 index 485d9868efd21af89ca7145386c7b95142f17205..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225608 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�v;L$hnqumq#%Q&~bCD~dtqmHn?q_jJa+^^T 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%0AdcUA0@M3 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

    b7 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

    6HtE@>Q|5mvQH37=0N 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 RO?c=MA;jMto;NH2{{i5^BW3^q diff --git a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json b/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json deleted file mode 100644 index 4e5683f2f3932c..00000000000000 --- a/x-pack/test/security_solution_cypress/es_archives/timeline_alerts/mappings.json +++ /dev/null @@ -1,9588 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": { - } - }, - "index": ".kibana_1", - "mappings": { - "_meta": { - "migrationMappingPropertyHashes": { - "action": "c0c235fba02ebd2a2412bcda79009b58", - "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", - "alert": "e588043a01d3d43477e7cad7efa0f5d8", - "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-services-telemetry": "07ee1939fa4302c62ddc052ec03fed90", - "canvas-element": "7390014e1091044523666d97247392fc", - "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", - "config": "87aca8fdb053154f11383fce3dbf3edf", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", - "inventory-view": "84b320fd67209906333ffce261128462", - "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", - "map": "23d7aa4a720d4938ccde3983f87bd58d", - "maps-telemetry": "268da3a48066123fc5baf35abaa55014", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "namespace": "2f4316de49999235636386fe51dc06c1", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", - "references": "7997cf5a56cc02bdc9c93361bde732b0", - "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", - "search": "181661168bbadd1eff5902361e2a0d5c", - "server": "ec97f1c5da1a19609a60874e5af1100c", - "siem-detection-engine-rule-status": "0367e4d775814b56a4bee29384f9aafe", - "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", - "telemetry": "358ffaa88ba34a97d55af0933a117de4", - "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", - "type": "2f4316de49999235636386fe51dc06c1", - "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", - "updated_at": "00da57df13e94e9d98437d13ace4bfe0", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "url": "c7f66a0df8b1b52f17c28c4adb111105", - "visualization": "52d7a13ad68a150c4525b292d23e12cc" - } - }, - "dynamic": "strict", - "properties": { - "action": { - "properties": { - "actionTypeId": { - "type": "keyword" - }, - "config": { - "enabled": false, - "type": "object" - }, - "name": { - "type": "text" - }, - "secrets": { - "type": "binary" - } - } - }, - "action_task_params": { - "properties": { - "actionId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "params": { - "enabled": false, - "type": "object" - } - } - }, - "alert": { - "properties": { - "actions": { - "properties": { - "actionRef": { - "type": "keyword" - }, - "actionTypeId": { - "type": "keyword" - }, - "group": { - "type": "keyword" - }, - "params": { - "enabled": false, - "type": "object" - } - }, - "type": "nested" - }, - "alertTypeId": { - "type": "keyword" - }, - "apiKey": { - "type": "binary" - }, - "apiKeyOwner": { - "type": "keyword" - }, - "consumer": { - "type": "keyword" - }, - "createdAt": { - "type": "date" - }, - "createdBy": { - "type": "keyword" - }, - "enabled": { - "type": "boolean" - }, - "muteAll": { - "type": "boolean" - }, - "mutedInstanceIds": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "params": { - "enabled": false, - "type": "object" - }, - "schedule": { - "properties": { - "interval": { - "type": "keyword" - } - } - }, - "scheduledTaskId": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "throttle": { - "type": "keyword" - }, - "updatedAt": { - "type": "date" - }, - "updatedBy": { - "type": "keyword" - } - } - }, - "apm-indices": { - "properties": { - "apm_oss": { - "properties": { - "errorIndices": { - "type": "keyword" - }, - "metricsIndices": { - "type": "keyword" - }, - "onboardingIndices": { - "type": "keyword" - }, - "sourcemapIndices": { - "type": "keyword" - }, - "spanIndices": { - "type": "keyword" - }, - "transactionIndices": { - "type": "keyword" - } - } - } - } - }, - "apm-services-telemetry": { - "properties": { - "has_any_services": { - "type": "boolean" - }, - "services_per_agent": { - "properties": { - "dotnet": { - "null_value": 0, - "type": "long" - }, - "go": { - "null_value": 0, - "type": "long" - }, - "java": { - "null_value": 0, - "type": "long" - }, - "js-base": { - "null_value": 0, - "type": "long" - }, - "nodejs": { - "null_value": 0, - "type": "long" - }, - "python": { - "null_value": 0, - "type": "long" - }, - "ruby": { - "null_value": 0, - "type": "long" - }, - "rum-js": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "canvas-element": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "content": { - "type": "text" - }, - "help": { - "type": "text" - }, - "image": { - "type": "text" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" - }, - "@timestamp": { - "type": "date" - }, - "name": { - "fields": { - "keyword": { - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - } - } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "file-upload-telemetry": { - "properties": { - "filesUploadedTotalCount": { - "type": "long" - } - } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" - } - } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "typeMeta": { - "type": "keyword" - } - } - }, - "infrastructure-ui-source": { - "properties": { - "description": { - "type": "text" - }, - "fields": { - "properties": { - "container": { - "type": "keyword" - }, - "host": { - "type": "keyword" - }, - "pod": { - "type": "keyword" - }, - "tiebreaker": { - "type": "keyword" - }, - "timestamp": { - "type": "keyword" - } - } - }, - "logAlias": { - "type": "keyword" - }, - "logColumns": { - "properties": { - "fieldColumn": { - "properties": { - "field": { - "type": "keyword" - }, - "id": { - "type": "keyword" - } - } - }, - "messageColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - }, - "timestampColumn": { - "properties": { - "id": { - "type": "keyword" - } - } - } - }, - "type": "nested" - }, - "metricAlias": { - "type": "keyword" - }, - "name": { - "type": "text" - } - } - }, - "inventory-view": { - "properties": { - "autoBounds": { - "type": "boolean" - }, - "autoReload": { - "type": "boolean" - }, - "boundsOverride": { - "properties": { - "max": { - "type": "integer" - }, - "min": { - "type": "integer" - } - } - }, - "customOptions": { - "properties": { - "field": { - "type": "keyword" - }, - "text": { - "type": "keyword" - } - }, - "type": "nested" - }, - "filterQuery": { - "properties": { - "expression": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - } - } - }, - "groupBy": { - "properties": { - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - }, - "metric": { - "properties": { - "type": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "nodeType": { - "type": "keyword" - }, - "time": { - "type": "integer" - }, - "view": { - "type": "keyword" - } - } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" - } - } - }, - "lens": { - "properties": { - "expression": { - "index": false, - "type": "keyword" - }, - "state": { - "type": "flattened" - }, - "title": { - "type": "text" - }, - "visualizationType": { - "type": "keyword" - } - } - }, - "lens-ui-telemetry": { - "properties": { - "count": { - "type": "integer" - }, - "date": { - "type": "date" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "map": { - "properties": { - "bounds": { - "type": "geo_shape" - }, - "description": { - "type": "text" - }, - "layerListJSON": { - "type": "text" - }, - "mapStateJSON": { - "type": "text" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "maps-telemetry": { - "properties": { - "attributesPerMap": { - "properties": { - "dataSourcesCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - }, - "emsVectorLayersCount": { - "dynamic": "true", - "type": "object" - }, - "layerTypesCount": { - "dynamic": "true", - "type": "object" - }, - "layersCount": { - "properties": { - "avg": { - "type": "long" - }, - "max": { - "type": "long" - }, - "min": { - "type": "long" - } - } - } - } - }, - "indexPatternsWithGeoFieldCount": { - "type": "long" - }, - "mapsTotalCount": { - "type": "long" - }, - "settings": { - "properties": { - "showMapVisualizationTypes": { - "type": "boolean" - } - } - }, - "timeCaptured": { - "type": "date" - } - } - }, - "metrics-explorer-view": { - "properties": { - "chartOptions": { - "properties": { - "stack": { - "type": "boolean" - }, - "type": { - "type": "keyword" - }, - "yAxisMode": { - "type": "keyword" - } - } - }, - "currentTimerange": { - "properties": { - "from": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "to": { - "type": "keyword" - } - } - }, - "name": { - "type": "keyword" - }, - "options": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "filterQuery": { - "type": "keyword" - }, - "groupBy": { - "type": "keyword" - }, - "limit": { - "type": "integer" - }, - "metrics": { - "properties": { - "aggregation": { - "type": "keyword" - }, - "color": { - "type": "keyword" - }, - "field": { - "type": "keyword" - }, - "label": { - "type": "keyword" - } - }, - "type": "nested" - } - } - } - } - }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "dashboard": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "index-pattern": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "search": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "space": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "visualization": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "ml-telemetry": { - "properties": { - "file_data_visualizer": { - "properties": { - "index_creation_count": { - "type": "long" - } - } - } - } - }, - "namespace": { - "type": "keyword" - }, - "query": { - "properties": { - "description": { - "type": "text" - }, - "filters": { - "enabled": false, - "type": "object" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "index": false, - "type": "keyword" - } - } - }, - "timefilter": { - "enabled": false, - "type": "object" - }, - "title": { - "type": "text" - } - } - }, - "references": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, - "sample-data-telemetry": { - "properties": { - "installCount": { - "type": "long" - }, - "unInstallCount": { - "type": "long" - } - } - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "siem-detection-engine-rule-status": { - "properties": { - "alertId": { - "type": "keyword" - }, - "lastFailureAt": { - "type": "date" - }, - "lastFailureMessage": { - "type": "text" - }, - "lastSuccessAt": { - "type": "date" - }, - "lastSuccessMessage": { - "type": "text" - }, - "status": { - "type": "keyword" - }, - "statusDate": { - "type": "date" - } - } - }, - "siem-ui-timeline": { - "properties": { - "columns": { - "properties": { - "aggregatable": { - "type": "boolean" - }, - "category": { - "type": "keyword" - }, - "columnHeaderType": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "example": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "indexes": { - "type": "keyword" - }, - "name": { - "type": "text" - }, - "placeholder": { - "type": "text" - }, - "searchable": { - "type": "boolean" - }, - "type": { - "type": "keyword" - } - } - }, - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "dataProviders": { - "properties": { - "and": { - "properties": { - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "enabled": { - "type": "boolean" - }, - "excluded": { - "type": "boolean" - }, - "id": { - "type": "keyword" - }, - "kqlQuery": { - "type": "text" - }, - "name": { - "type": "text" - }, - "queryMatch": { - "properties": { - "displayField": { - "type": "text" - }, - "displayValue": { - "type": "text" - }, - "field": { - "type": "text" - }, - "operator": { - "type": "text" - }, - "value": { - "type": "text" - } - } - } - } - }, - "dateRange": { - "properties": { - "end": { - "type": "date" - }, - "start": { - "type": "date" - } - } - }, - "description": { - "type": "text" - }, - "eventType": { - "type": "keyword" - }, - "favorite": { - "properties": { - "favoriteDate": { - "type": "date" - }, - "fullName": { - "type": "text" - }, - "keySearch": { - "type": "text" - }, - "userName": { - "type": "text" - } - } - }, - "filters": { - "properties": { - "exists": { - "type": "text" - }, - "match_all": { - "type": "text" - }, - "meta": { - "properties": { - "alias": { - "type": "text" - }, - "controlledBy": { - "type": "text" - }, - "disabled": { - "type": "boolean" - }, - "field": { - "type": "text" - }, - "formattedValue": { - "type": "text" - }, - "index": { - "type": "keyword" - }, - "key": { - "type": "keyword" - }, - "negate": { - "type": "boolean" - }, - "params": { - "type": "text" - }, - "type": { - "type": "keyword" - }, - "value": { - "type": "text" - } - } - }, - "missing": { - "type": "text" - }, - "query": { - "type": "text" - }, - "range": { - "type": "text" - }, - "script": { - "type": "text" - } - } - }, - "kqlMode": { - "type": "keyword" - }, - "kqlQuery": { - "properties": { - "filterQuery": { - "properties": { - "kuery": { - "properties": { - "expression": { - "type": "text" - }, - "kind": { - "type": "keyword" - } - } - }, - "serializedQuery": { - "type": "text" - } - } - } - } - }, - "savedQueryId": { - "type": "keyword" - }, - "sort": { - "properties": { - "columnId": { - "type": "keyword" - }, - "sortDirection": { - "type": "keyword" - } - } - }, - "title": { - "type": "text" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-note": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "siem-ui-timeline-pinned-event": { - "properties": { - "created": { - "type": "date" - }, - "createdBy": { - "type": "text" - }, - "eventId": { - "type": "keyword" - }, - "timelineId": { - "type": "keyword" - }, - "updated": { - "type": "date" - }, - "updatedBy": { - "type": "text" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "imageUrl": { - "index": false, - "type": "text" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "telemetry": { - "properties": { - "enabled": { - "type": "boolean" - }, - "lastReported": { - "type": "date" - }, - "lastVersionChecked": { - "ignore_above": 256, - "type": "keyword" - }, - "sendUsageFrom": { - "ignore_above": 256, - "type": "keyword" - }, - "userHasSeenNotice": { - "type": "boolean" - } - } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "tsvb-validation-telemetry": { - "properties": { - "failedRequests": { - "type": "long" - } - } - }, - "type": { - "type": "keyword" - }, - "ui-metric": { - "properties": { - "count": { - "type": "integer" - } - } - }, - "updated_at": { - "type": "date" - }, - "upgrade-assistant-reindex-operation": { - "dynamic": "true", - "properties": { - "indexName": { - "type": "keyword" - }, - "status": { - "type": "integer" - } - } - }, - "upgrade-assistant-telemetry": { - "properties": { - "features": { - "properties": { - "deprecation_logging": { - "properties": { - "enabled": { - "null_value": true, - "type": "boolean" - } - } - } - } - }, - "ui_open": { - "properties": { - "cluster": { - "null_value": 0, - "type": "long" - }, - "indices": { - "null_value": 0, - "type": "long" - }, - "overview": { - "null_value": 0, - "type": "long" - } - } - }, - "ui_reindex": { - "properties": { - "close": { - "null_value": 0, - "type": "long" - }, - "open": { - "null_value": 0, - "type": "long" - }, - "start": { - "null_value": 0, - "type": "long" - }, - "stop": { - "null_value": 0, - "type": "long" - } - } - } - } - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchRefName": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "auto_expand_replicas": "0-1", - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "_meta": { - "version": 3 - }, - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "availability_zone": { - "type": "keyword", - "ignore_above": 1024 - }, - "instance": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "machine": { - "properties": { - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "region": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "container": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "image": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "tag": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "runtime": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "destination": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "dll": { - "properties": { - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "data": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "ttl": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "header_flags": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "op_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "question": { - "properties": { - "class": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "subdomain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ecs": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "error": { - "properties": { - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "message": { - "type": "text", - "norms": false - }, - "stack_trace": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "event": { - "properties": { - "action": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "code": { - "type": "keyword", - "ignore_above": 1024 - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword", - "ignore_above": 1024 - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingested": { - "type": "date" - }, - "kind": { - "type": "keyword", - "ignore_above": 1024 - }, - "module": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "outcome": { - "type": "keyword", - "ignore_above": 1024 - }, - "provider": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "url": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "type": "keyword", - "ignore_above": 1024 - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "type": "keyword", - "ignore_above": 1024 - }, - "directory": { - "type": "keyword", - "ignore_above": 1024 - }, - "drive_letter": { - "type": "keyword", - "ignore_above": 1 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "gid": { - "type": "keyword", - "ignore_above": 1024 - }, - "group": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "inode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mime_type": { - "type": "keyword", - "ignore_above": 1024 - }, - "mode": { - "type": "keyword", - "ignore_above": 1024 - }, - "mtime": { - "type": "date" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "owner": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "size": { - "type": "long" - }, - "target_path": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uid": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "host": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "type": "keyword", - "ignore_above": 1024 - }, - "referrer": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "type": "keyword", - "ignore_above": 1024 - }, - "logger": { - "type": "keyword", - "ignore_above": 1024 - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "function": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false, - "ignore_above": 1024 - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "message": { - "type": "text", - "norms": false - }, - "network": { - "properties": { - "application": { - "type": "keyword", - "ignore_above": 1024 - }, - "bytes": { - "type": "long" - }, - "community_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "direction": { - "type": "keyword", - "ignore_above": 1024 - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "inner": { - "properties": { - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "packets": { - "type": "long" - }, - "protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "transport": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "observer": { - "properties": { - "egress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hostname": { - "type": "keyword", - "ignore_above": 1024 - }, - "ingress": { - "properties": { - "interface": { - "properties": { - "alias": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "zone": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - }, - "serial_number": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "vendor": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "organization": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "package": { - "properties": { - "architecture": { - "type": "keyword", - "ignore_above": 1024 - }, - "build_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "checksum": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "install_scope": { - "type": "keyword", - "ignore_above": 1024 - }, - "installed": { - "type": "date" - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "size": { - "type": "long" - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "process": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "parent": { - "properties": { - "args": { - "type": "keyword", - "ignore_above": 1024 - }, - "args_count": { - "type": "long" - }, - "code_signature": { - "properties": { - "exists": { - "type": "boolean" - }, - "status": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "trusted": { - "type": "boolean" - }, - "valid": { - "type": "boolean" - } - } - }, - "command_line": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "entity_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "executable": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha512": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "pe": { - "properties": { - "company": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "file_version": { - "type": "keyword", - "ignore_above": 1024 - }, - "original_file_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "product": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "title": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "type": "keyword", - "ignore_above": 1024 - }, - "strings": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hive": { - "type": "keyword", - "ignore_above": 1024 - }, - "key": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "value": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "related": { - "properties": { - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "ip": { - "type": "ip" - }, - "user": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword", - "ignore_above": 1024 - }, - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "license": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "ruleset": { - "type": "keyword", - "ignore_above": 1024 - }, - "uuid": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "server": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "node": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "state": { - "type": "keyword", - "ignore_above": 1024 - }, - "type": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "depth": { - "type": "integer" - }, - "group": { - "properties": { - "id": { - "type": "keyword" - }, - "index": { - "type": "integer" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "type": "keyword", - "index": false, - "doc_values": false - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_signal": { - "type": "object", - "dynamic": "false", - "enabled": false - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "parents": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "author": { - "type": "keyword" - }, - "building_block_type": { - "type": "keyword" - }, - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "license": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "rule_id": { - "type": "keyword" - }, - "rule_name_override": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "severity_mapping": { - "properties": { - "field": { - "type": "keyword" - }, - "operator": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "value": { - "type": "keyword" - } - } - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "threshold": { - "properties": { - "field": { - "type": "keyword" - }, - "value": { - "type": "float" - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "timestamp_override": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - }, - "threshold_count": { - "type": "float" - }, - "threshold_result": { - "properties": { - "count": { - "type": "long" - }, - "value": { - "type": "keyword" - } - } - } - } - }, - "source": { - "properties": { - "address": { - "type": "keyword", - "ignore_above": 1024 - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "geo": { - "properties": { - "city_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "continent_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "country_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "location": { - "type": "geo_point" - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_iso_code": { - "type": "keyword", - "ignore_above": 1024 - }, - "region_name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "type": "keyword", - "ignore_above": 1024 - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - } - } - }, - "tags": { - "type": "keyword", - "ignore_above": 1024 - }, - "threat": { - "properties": { - "framework": { - "type": "keyword", - "ignore_above": 1024 - }, - "tactic": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "type": "keyword", - "ignore_above": 1024 - }, - "client": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "type": "keyword", - "ignore_above": 1024 - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - }, - "supported_ciphers": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "curve": { - "type": "keyword", - "ignore_above": 1024 - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "type": "keyword", - "ignore_above": 1024 - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "type": "keyword", - "ignore_above": 1024 - }, - "certificate_chain": { - "type": "keyword", - "ignore_above": 1024 - }, - "hash": { - "properties": { - "md5": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha1": { - "type": "keyword", - "ignore_above": 1024 - }, - "sha256": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "issuer": { - "type": "keyword", - "ignore_above": 1024 - }, - "ja3s": { - "type": "keyword", - "ignore_above": 1024 - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - }, - "version_protocol": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "trace": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "transaction": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "url": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "extension": { - "type": "keyword", - "ignore_above": 1024 - }, - "fragment": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "password": { - "type": "keyword", - "ignore_above": 1024 - }, - "path": { - "type": "keyword", - "ignore_above": 1024 - }, - "port": { - "type": "long" - }, - "query": { - "type": "keyword", - "ignore_above": 1024 - }, - "registered_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "scheme": { - "type": "keyword", - "ignore_above": 1024 - }, - "top_level_domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "username": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "user": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "email": { - "type": "keyword", - "ignore_above": 1024 - }, - "full_name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "group": { - "properties": { - "domain": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "hash": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "original": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "os": { - "properties": { - "family": { - "type": "keyword", - "ignore_above": 1024 - }, - "full": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "kernel": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "platform": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vlan": { - "properties": { - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "vulnerability": { - "properties": { - "category": { - "type": "keyword", - "ignore_above": 1024 - }, - "classification": { - "type": "keyword", - "ignore_above": 1024 - }, - "description": { - "type": "keyword", - "ignore_above": 1024, - "fields": { - "text": { - "type": "text", - "norms": false - } - } - }, - "enumeration": { - "type": "keyword", - "ignore_above": 1024 - }, - "id": { - "type": "keyword", - "ignore_above": 1024 - }, - "reference": { - "type": "keyword", - "ignore_above": 1024 - }, - "report_id": { - "type": "keyword", - "ignore_above": 1024 - }, - "scanner": { - "properties": { - "vendor": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "severity": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} - -{ - "type": "index", - "value": { - "aliases": { - "auditbeat-7.6.2": { - "is_write_index": true - } - }, - "index": "auditbeat-7.6.2-2020.03.20-000001", - "mappings": { - "_meta": { - "beat": "auditbeat", - "version": "7.6.2" - }, - "date_detection": false, - "dynamic_templates": [ - { - "labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" - } - }, - { - "container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" - } - }, - { - "dns.answers": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "dns.answers.*" - } - }, - { - "log.syslog": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "log.syslog.*" - } - }, - { - "fields": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "fields.*" - } - }, - { - "docker.container.labels": { - "mapping": { - "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "docker.container.labels.*" - } - }, - { - "kubernetes.labels.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.labels.*" - } - }, - { - "kubernetes.annotations.*": { - "mapping": { - "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" - } - }, - { - "strings_as_keyword": { - "mapping": { - "ignore_above": 1024, - "type": "keyword" - }, - "match_mapping_type": "string" - } - } - ], - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "auditd": { - "properties": { - "data": { - "properties": { - "a0": { - "ignore_above": 1024, - "type": "keyword" - }, - "a1": { - "ignore_above": 1024, - "type": "keyword" - }, - "a2": { - "ignore_above": 1024, - "type": "keyword" - }, - "a3": { - "ignore_above": 1024, - "type": "keyword" - }, - "a[0-3]": { - "ignore_above": 1024, - "type": "keyword" - }, - "acct": { - "ignore_above": 1024, - "type": "keyword" - }, - "acl": { - "ignore_above": 1024, - "type": "keyword" - }, - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "added": { - "ignore_above": 1024, - "type": "keyword" - }, - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "apparmor": { - "ignore_above": 1024, - "type": "keyword" - }, - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "argc": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_limit": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_backlog_wait_time": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "audit_failure": { - "ignore_above": 1024, - "type": "keyword" - }, - "banners": { - "ignore_above": 1024, - "type": "keyword" - }, - "bool": { - "ignore_above": 1024, - "type": "keyword" - }, - "bus": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "cap_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "capability": { - "ignore_above": 1024, - "type": "keyword" - }, - "cgroup": { - "ignore_above": 1024, - "type": "keyword" - }, - "changed": { - "ignore_above": 1024, - "type": "keyword" - }, - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "cmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "compat": { - "ignore_above": 1024, - "type": "keyword" - }, - "daddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "default-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "dmac": { - "ignore_above": 1024, - "type": "keyword" - }, - "dport": { - "ignore_above": 1024, - "type": "keyword" - }, - "enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "entries": { - "ignore_above": 1024, - "type": "keyword" - }, - "exit": { - "ignore_above": 1024, - "type": "keyword" - }, - "fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "fd": { - "ignore_above": 1024, - "type": "keyword" - }, - "fe": { - "ignore_above": 1024, - "type": "keyword" - }, - "feature": { - "ignore_above": 1024, - "type": "keyword" - }, - "fi": { - "ignore_above": 1024, - "type": "keyword" - }, - "file": { - "ignore_above": 1024, - "type": "keyword" - }, - "flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "format": { - "ignore_above": 1024, - "type": "keyword" - }, - "fp": { - "ignore_above": 1024, - "type": "keyword" - }, - "fver": { - "ignore_above": 1024, - "type": "keyword" - }, - "grantors": { - "ignore_above": 1024, - "type": "keyword" - }, - "grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "hook": { - "ignore_above": 1024, - "type": "keyword" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "icmp_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "igid": { - "ignore_above": 1024, - "type": "keyword" - }, - "img-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "inif": { - "ignore_above": 1024, - "type": "keyword" - }, - "ino": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "invalid_context": { - "ignore_above": 1024, - "type": "keyword" - }, - "ioctlcmd": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ipx-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "items": { - "ignore_above": 1024, - "type": "keyword" - }, - "iuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "ksize": { - "ignore_above": 1024, - "type": "keyword" - }, - "laddr": { - "ignore_above": 1024, - "type": "keyword" - }, - "len": { - "ignore_above": 1024, - "type": "keyword" - }, - "list": { - "ignore_above": 1024, - "type": "keyword" - }, - "lport": { - "ignore_above": 1024, - "type": "keyword" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "macproto": { - "ignore_above": 1024, - "type": "keyword" - }, - "maj": { - "ignore_above": 1024, - "type": "keyword" - }, - "major": { - "ignore_above": 1024, - "type": "keyword" - }, - "minor": { - "ignore_above": 1024, - "type": "keyword" - }, - "model": { - "ignore_above": 1024, - "type": "keyword" - }, - "msg": { - "ignore_above": 1024, - "type": "keyword" - }, - "nargs": { - "ignore_above": 1024, - "type": "keyword" - }, - "net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "new-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "new_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-fam": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-grp": { - "ignore_above": 1024, - "type": "keyword" - }, - "nlnk-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ocomm": { - "ignore_above": 1024, - "type": "keyword" - }, - "oflag": { - "ignore_above": 1024, - "type": "keyword" - }, - "old": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-auid": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-chardev": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-disk": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-enabled": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-fs": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-level": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-log_passwd": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-mem": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-net": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-range": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-rng": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-role": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "old-vcpu": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_enforcing": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_lock": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pe": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pi": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_pp": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "old_val": { - "ignore_above": 1024, - "type": "keyword" - }, - "op": { - "ignore_above": 1024, - "type": "keyword" - }, - "opid": { - "ignore_above": 1024, - "type": "keyword" - }, - "oses": { - "ignore_above": 1024, - "type": "keyword" - }, - "outif": { - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "ignore_above": 1024, - "type": "keyword" - }, - "per": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm": { - "ignore_above": 1024, - "type": "keyword" - }, - "perm_mask": { - "ignore_above": 1024, - "type": "keyword" - }, - "permissive": { - "ignore_above": 1024, - "type": "keyword" - }, - "pfs": { - "ignore_above": 1024, - "type": "keyword" - }, - "printer": { - "ignore_above": 1024, - "type": "keyword" - }, - "prom": { - "ignore_above": 1024, - "type": "keyword" - }, - "proto": { - "ignore_above": 1024, - "type": "keyword" - }, - "qbytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "range": { - "ignore_above": 1024, - "type": "keyword" - }, - "reason": { - "ignore_above": 1024, - "type": "keyword" - }, - "removed": { - "ignore_above": 1024, - "type": "keyword" - }, - "res": { - "ignore_above": 1024, - "type": "keyword" - }, - "resrc": { - "ignore_above": 1024, - "type": "keyword" - }, - "rport": { - "ignore_above": 1024, - "type": "keyword" - }, - "sauid": { - "ignore_above": 1024, - "type": "keyword" - }, - "scontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "selected-context": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperm": { - "ignore_above": 1024, - "type": "keyword" - }, - "seperms": { - "ignore_above": 1024, - "type": "keyword" - }, - "seqno": { - "ignore_above": 1024, - "type": "keyword" - }, - "seresult": { - "ignore_above": 1024, - "type": "keyword" - }, - "ses": { - "ignore_above": 1024, - "type": "keyword" - }, - "seuser": { - "ignore_above": 1024, - "type": "keyword" - }, - "sig": { - "ignore_above": 1024, - "type": "keyword" - }, - "sigev_signo": { - "ignore_above": 1024, - "type": "keyword" - }, - "smac": { - "ignore_above": 1024, - "type": "keyword" - }, - "socket": { - "properties": { - "addr": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "ignore_above": 1024, - "type": "keyword" - }, - "saddr": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "spid": { - "ignore_above": 1024, - "type": "keyword" - }, - "sport": { - "ignore_above": 1024, - "type": "keyword" - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "subj": { - "ignore_above": 1024, - "type": "keyword" - }, - "success": { - "ignore_above": 1024, - "type": "keyword" - }, - "syscall": { - "ignore_above": 1024, - "type": "keyword" - }, - "table": { - "ignore_above": 1024, - "type": "keyword" - }, - "tclass": { - "ignore_above": 1024, - "type": "keyword" - }, - "tcontext": { - "ignore_above": 1024, - "type": "keyword" - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - }, - "tty": { - "ignore_above": 1024, - "type": "keyword" - }, - "unit": { - "ignore_above": 1024, - "type": "keyword" - }, - "uri": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "val": { - "ignore_above": 1024, - "type": "keyword" - }, - "ver": { - "ignore_above": 1024, - "type": "keyword" - }, - "virt": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-ctx": { - "ignore_above": 1024, - "type": "keyword" - }, - "vm-pid": { - "ignore_above": 1024, - "type": "keyword" - }, - "watch": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "message_type": { - "ignore_above": 1024, - "type": "keyword" - }, - "paths": { - "properties": { - "dev": { - "ignore_above": 1024, - "type": "keyword" - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "item": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "nametype": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_level": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_role": { - "ignore_above": 1024, - "type": "keyword" - }, - "obj_user": { - "ignore_above": 1024, - "type": "keyword" - }, - "objtype": { - "ignore_above": 1024, - "type": "keyword" - }, - "ogid": { - "ignore_above": 1024, - "type": "keyword" - }, - "ouid": { - "ignore_above": 1024, - "type": "keyword" - }, - "rdev": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "result": { - "ignore_above": 1024, - "type": "keyword" - }, - "sequence": { - "type": "long" - }, - "session": { - "ignore_above": 1024, - "type": "keyword" - }, - "summary": { - "properties": { - "actor": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "how": { - "ignore_above": 1024, - "type": "keyword" - }, - "object": { - "properties": { - "primary": { - "ignore_above": 1024, - "type": "keyword" - }, - "secondary": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "project": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "docker": { - "properties": { - "container": { - "properties": { - "labels": { - "type": "object" - } - } - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "fields": { - "type": "object" - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "fields": { - "raw": { - "ignore_above": 1024, - "type": "keyword" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "selinux": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "setgid": { - "type": "boolean" - }, - "setuid": { - "type": "boolean" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geoip": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "containerized": { - "type": "boolean" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "build": { - "ignore_above": 1024, - "type": "keyword" - }, - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "jolokia": { - "properties": { - "agent": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "secured": { - "type": "boolean" - }, - "server": { - "properties": { - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "kubernetes": { - "properties": { - "annotations": { - "properties": { - "*": { - "type": "object" - } - } - }, - "container": { - "properties": { - "image": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "deployment": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "properties": { - "*": { - "type": "object" - } - } - }, - "namespace": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pod": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "replicaset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "statefulset": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "ignore_above": 1024, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "blake2b_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "blake2b_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_384": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha3_512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_224": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512_256": { - "ignore_above": 1024, - "type": "keyword" - }, - "xxh64": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "socket": { - "properties": { - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "system": { - "properties": { - "audit": { - "properties": { - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "boottime": { - "type": "date" - }, - "containerized": { - "type": "boolean" - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "codename": { - "ignore_above": 1024, - "type": "keyword" - }, - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "timezone": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "offset": { - "properties": { - "sec": { - "type": "long" - } - } - } - } - }, - "uptime": { - "type": "long" - } - } - }, - "package": { - "properties": { - "arch": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "installtime": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "release": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "summary": { - "ignore_above": 1024, - "type": "keyword" - }, - "url": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "dir": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "properties": { - "last_changed": { - "type": "date" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "shell": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - }, - "user_information": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "timeseries": { - "properties": { - "instance": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "tracing": { - "properties": { - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "audit": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "effective": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "entity_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "filesystem": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "name_map": { - "type": "object" - }, - "saved": { - "properties": { - "group": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "selinux": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "role": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "terminal": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": "auditbeat", - "rollover_alias": "auditbeat-7.6.2" - }, - "mapping": { - "total_fields": { - "limit": "10000" - } - }, - "number_of_replicas": "1", - "number_of_shards": "1", - "query": { - "default_field": [ - "message", - "tags", - "agent.ephemeral_id", - "agent.id", - "agent.name", - "agent.type", - "agent.version", - "as.organization.name", - "client.address", - "client.as.organization.name", - "client.domain", - "client.geo.city_name", - "client.geo.continent_name", - "client.geo.country_iso_code", - "client.geo.country_name", - "client.geo.name", - "client.geo.region_iso_code", - "client.geo.region_name", - "client.mac", - "client.registered_domain", - "client.top_level_domain", - "client.user.domain", - "client.user.email", - "client.user.full_name", - "client.user.group.domain", - "client.user.group.id", - "client.user.group.name", - "client.user.hash", - "client.user.id", - "client.user.name", - "cloud.account.id", - "cloud.availability_zone", - "cloud.instance.id", - "cloud.instance.name", - "cloud.machine.type", - "cloud.provider", - "cloud.region", - "container.id", - "container.image.name", - "container.image.tag", - "container.name", - "container.runtime", - "destination.address", - "destination.as.organization.name", - "destination.domain", - "destination.geo.city_name", - "destination.geo.continent_name", - "destination.geo.country_iso_code", - "destination.geo.country_name", - "destination.geo.name", - "destination.geo.region_iso_code", - "destination.geo.region_name", - "destination.mac", - "destination.registered_domain", - "destination.top_level_domain", - "destination.user.domain", - "destination.user.email", - "destination.user.full_name", - "destination.user.group.domain", - "destination.user.group.id", - "destination.user.group.name", - "destination.user.hash", - "destination.user.id", - "destination.user.name", - "dns.answers.class", - "dns.answers.data", - "dns.answers.name", - "dns.answers.type", - "dns.header_flags", - "dns.id", - "dns.op_code", - "dns.question.class", - "dns.question.name", - "dns.question.registered_domain", - "dns.question.subdomain", - "dns.question.top_level_domain", - "dns.question.type", - "dns.response_code", - "dns.type", - "ecs.version", - "error.code", - "error.id", - "error.message", - "error.stack_trace", - "error.type", - "event.action", - "event.category", - "event.code", - "event.dataset", - "event.hash", - "event.id", - "event.kind", - "event.module", - "event.original", - "event.outcome", - "event.provider", - "event.timezone", - "event.type", - "file.device", - "file.directory", - "file.extension", - "file.gid", - "file.group", - "file.hash.md5", - "file.hash.sha1", - "file.hash.sha256", - "file.hash.sha512", - "file.inode", - "file.mode", - "file.name", - "file.owner", - "file.path", - "file.target_path", - "file.type", - "file.uid", - "geo.city_name", - "geo.continent_name", - "geo.country_iso_code", - "geo.country_name", - "geo.name", - "geo.region_iso_code", - "geo.region_name", - "group.domain", - "group.id", - "group.name", - "hash.md5", - "hash.sha1", - "hash.sha256", - "hash.sha512", - "host.architecture", - "host.geo.city_name", - "host.geo.continent_name", - "host.geo.country_iso_code", - "host.geo.country_name", - "host.geo.name", - "host.geo.region_iso_code", - "host.geo.region_name", - "host.hostname", - "host.id", - "host.mac", - "host.name", - "host.os.family", - "host.os.full", - "host.os.kernel", - "host.os.name", - "host.os.platform", - "host.os.version", - "host.type", - "host.user.domain", - "host.user.email", - "host.user.full_name", - "host.user.group.domain", - "host.user.group.id", - "host.user.group.name", - "host.user.hash", - "host.user.id", - "host.user.name", - "http.request.body.content", - "http.request.method", - "http.request.referrer", - "http.response.body.content", - "http.version", - "log.level", - "log.logger", - "log.origin.file.name", - "log.origin.function", - "log.original", - "log.syslog.facility.name", - "log.syslog.severity.name", - "network.application", - "network.community_id", - "network.direction", - "network.iana_number", - "network.name", - "network.protocol", - "network.transport", - "network.type", - "observer.geo.city_name", - "observer.geo.continent_name", - "observer.geo.country_iso_code", - "observer.geo.country_name", - "observer.geo.name", - "observer.geo.region_iso_code", - "observer.geo.region_name", - "observer.hostname", - "observer.mac", - "observer.name", - "observer.os.family", - "observer.os.full", - "observer.os.kernel", - "observer.os.name", - "observer.os.platform", - "observer.os.version", - "observer.product", - "observer.serial_number", - "observer.type", - "observer.vendor", - "observer.version", - "organization.id", - "organization.name", - "os.family", - "os.full", - "os.kernel", - "os.name", - "os.platform", - "os.version", - "package.architecture", - "package.checksum", - "package.description", - "package.install_scope", - "package.license", - "package.name", - "package.path", - "package.version", - "process.args", - "text", - "process.executable", - "process.hash.md5", - "process.hash.sha1", - "process.hash.sha256", - "process.hash.sha512", - "process.name", - "text", - "text", - "text", - "text", - "text", - "process.thread.name", - "process.title", - "process.working_directory", - "server.address", - "server.as.organization.name", - "server.domain", - "server.geo.city_name", - "server.geo.continent_name", - "server.geo.country_iso_code", - "server.geo.country_name", - "server.geo.name", - "server.geo.region_iso_code", - "server.geo.region_name", - "server.mac", - "server.registered_domain", - "server.top_level_domain", - "server.user.domain", - "server.user.email", - "server.user.full_name", - "server.user.group.domain", - "server.user.group.id", - "server.user.group.name", - "server.user.hash", - "server.user.id", - "server.user.name", - "service.ephemeral_id", - "service.id", - "service.name", - "service.node.name", - "service.state", - "service.type", - "service.version", - "source.address", - "source.as.organization.name", - "source.domain", - "source.geo.city_name", - "source.geo.continent_name", - "source.geo.country_iso_code", - "source.geo.country_name", - "source.geo.name", - "source.geo.region_iso_code", - "source.geo.region_name", - "source.mac", - "source.registered_domain", - "source.top_level_domain", - "source.user.domain", - "source.user.email", - "source.user.full_name", - "source.user.group.domain", - "source.user.group.id", - "source.user.group.name", - "source.user.hash", - "source.user.id", - "source.user.name", - "threat.framework", - "threat.tactic.id", - "threat.tactic.name", - "threat.tactic.reference", - "threat.technique.id", - "threat.technique.name", - "threat.technique.reference", - "tracing.trace.id", - "tracing.transaction.id", - "url.domain", - "url.extension", - "url.fragment", - "url.full", - "url.original", - "url.password", - "url.path", - "url.query", - "url.registered_domain", - "url.scheme", - "url.top_level_domain", - "url.username", - "user.domain", - "user.email", - "user.full_name", - "user.group.domain", - "user.group.id", - "user.group.name", - "user.hash", - "user.id", - "user.name", - "user_agent.device.name", - "user_agent.name", - "text", - "user_agent.original", - "user_agent.os.family", - "user_agent.os.full", - "user_agent.os.kernel", - "user_agent.os.name", - "user_agent.os.platform", - "user_agent.os.version", - "user_agent.version", - "text", - "agent.hostname", - "timeseries.instance", - "cloud.project.id", - "cloud.image.id", - "host.os.build", - "host.os.codename", - "kubernetes.pod.name", - "kubernetes.pod.uid", - "kubernetes.namespace", - "kubernetes.node.name", - "kubernetes.replicaset.name", - "kubernetes.deployment.name", - "kubernetes.statefulset.name", - "kubernetes.container.name", - "kubernetes.container.image", - "jolokia.agent.version", - "jolokia.agent.id", - "jolokia.server.product", - "jolokia.server.version", - "jolokia.server.vendor", - "jolokia.url", - "raw", - "file.origin", - "file.selinux.user", - "file.selinux.role", - "file.selinux.domain", - "file.selinux.level", - "user.audit.id", - "user.audit.name", - "user.effective.id", - "user.effective.name", - "user.effective.group.id", - "user.effective.group.name", - "user.filesystem.id", - "user.filesystem.name", - "user.filesystem.group.id", - "user.filesystem.group.name", - "user.saved.id", - "user.saved.name", - "user.saved.group.id", - "user.saved.group.name", - "user.selinux.user", - "user.selinux.role", - "user.selinux.domain", - "user.selinux.level", - "user.selinux.category", - "source.path", - "destination.path", - "auditd.message_type", - "auditd.session", - "auditd.result", - "auditd.summary.actor.primary", - "auditd.summary.actor.secondary", - "auditd.summary.object.type", - "auditd.summary.object.primary", - "auditd.summary.object.secondary", - "auditd.summary.how", - "auditd.paths.inode", - "auditd.paths.dev", - "auditd.paths.obj_user", - "auditd.paths.obj_role", - "auditd.paths.obj_domain", - "auditd.paths.obj_level", - "auditd.paths.objtype", - "auditd.paths.ouid", - "auditd.paths.rdev", - "auditd.paths.nametype", - "auditd.paths.ogid", - "auditd.paths.item", - "auditd.paths.mode", - "auditd.paths.name", - "auditd.data.action", - "auditd.data.minor", - "auditd.data.acct", - "auditd.data.addr", - "auditd.data.cipher", - "auditd.data.id", - "auditd.data.entries", - "auditd.data.kind", - "auditd.data.ksize", - "auditd.data.spid", - "auditd.data.arch", - "auditd.data.argc", - "auditd.data.major", - "auditd.data.unit", - "auditd.data.table", - "auditd.data.terminal", - "auditd.data.grantors", - "auditd.data.direction", - "auditd.data.op", - "auditd.data.tty", - "auditd.data.syscall", - "auditd.data.data", - "auditd.data.family", - "auditd.data.mac", - "auditd.data.pfs", - "auditd.data.items", - "auditd.data.a0", - "auditd.data.a1", - "auditd.data.a2", - "auditd.data.a3", - "auditd.data.hostname", - "auditd.data.lport", - "auditd.data.rport", - "auditd.data.exit", - "auditd.data.fp", - "auditd.data.laddr", - "auditd.data.sport", - "auditd.data.capability", - "auditd.data.nargs", - "auditd.data.new-enabled", - "auditd.data.audit_backlog_limit", - "auditd.data.dir", - "auditd.data.cap_pe", - "auditd.data.model", - "auditd.data.new_pp", - "auditd.data.old-enabled", - "auditd.data.oauid", - "auditd.data.old", - "auditd.data.banners", - "auditd.data.feature", - "auditd.data.vm-ctx", - "auditd.data.opid", - "auditd.data.seperms", - "auditd.data.seresult", - "auditd.data.new-rng", - "auditd.data.old-net", - "auditd.data.sigev_signo", - "auditd.data.ino", - "auditd.data.old_enforcing", - "auditd.data.old-vcpu", - "auditd.data.range", - "auditd.data.res", - "auditd.data.added", - "auditd.data.fam", - "auditd.data.nlnk-pid", - "auditd.data.subj", - "auditd.data.a[0-3]", - "auditd.data.cgroup", - "auditd.data.kernel", - "auditd.data.ocomm", - "auditd.data.new-net", - "auditd.data.permissive", - "auditd.data.class", - "auditd.data.compat", - "auditd.data.fi", - "auditd.data.changed", - "auditd.data.msg", - "auditd.data.dport", - "auditd.data.new-seuser", - "auditd.data.invalid_context", - "auditd.data.dmac", - "auditd.data.ipx-net", - "auditd.data.iuid", - "auditd.data.macproto", - "auditd.data.obj", - "auditd.data.ipid", - "auditd.data.new-fs", - "auditd.data.vm-pid", - "auditd.data.cap_pi", - "auditd.data.old-auid", - "auditd.data.oses", - "auditd.data.fd", - "auditd.data.igid", - "auditd.data.new-disk", - "auditd.data.parent", - "auditd.data.len", - "auditd.data.oflag", - "auditd.data.uuid", - "auditd.data.code", - "auditd.data.nlnk-grp", - "auditd.data.cap_fp", - "auditd.data.new-mem", - "auditd.data.seperm", - "auditd.data.enforcing", - "auditd.data.new-chardev", - "auditd.data.old-rng", - "auditd.data.outif", - "auditd.data.cmd", - "auditd.data.hook", - "auditd.data.new-level", - "auditd.data.sauid", - "auditd.data.sig", - "auditd.data.audit_backlog_wait_time", - "auditd.data.printer", - "auditd.data.old-mem", - "auditd.data.perm", - "auditd.data.old_pi", - "auditd.data.state", - "auditd.data.format", - "auditd.data.new_gid", - "auditd.data.tcontext", - "auditd.data.maj", - "auditd.data.watch", - "auditd.data.device", - "auditd.data.grp", - "auditd.data.bool", - "auditd.data.icmp_type", - "auditd.data.new_lock", - "auditd.data.old_prom", - "auditd.data.acl", - "auditd.data.ip", - "auditd.data.new_pi", - "auditd.data.default-context", - "auditd.data.inode_gid", - "auditd.data.new-log_passwd", - "auditd.data.new_pe", - "auditd.data.selected-context", - "auditd.data.cap_fver", - "auditd.data.file", - "auditd.data.net", - "auditd.data.virt", - "auditd.data.cap_pp", - "auditd.data.old-range", - "auditd.data.resrc", - "auditd.data.new-range", - "auditd.data.obj_gid", - "auditd.data.proto", - "auditd.data.old-disk", - "auditd.data.audit_failure", - "auditd.data.inif", - "auditd.data.vm", - "auditd.data.flags", - "auditd.data.nlnk-fam", - "auditd.data.old-fs", - "auditd.data.old-ses", - "auditd.data.seqno", - "auditd.data.fver", - "auditd.data.qbytes", - "auditd.data.seuser", - "auditd.data.cap_fe", - "auditd.data.new-vcpu", - "auditd.data.old-level", - "auditd.data.old_pp", - "auditd.data.daddr", - "auditd.data.old-role", - "auditd.data.ioctlcmd", - "auditd.data.smac", - "auditd.data.apparmor", - "auditd.data.fe", - "auditd.data.perm_mask", - "auditd.data.ses", - "auditd.data.cap_fi", - "auditd.data.obj_uid", - "auditd.data.reason", - "auditd.data.list", - "auditd.data.old_lock", - "auditd.data.bus", - "auditd.data.old_pe", - "auditd.data.new-role", - "auditd.data.prom", - "auditd.data.uri", - "auditd.data.audit_enabled", - "auditd.data.old-log_passwd", - "auditd.data.old-seuser", - "auditd.data.per", - "auditd.data.scontext", - "auditd.data.tclass", - "auditd.data.ver", - "auditd.data.new", - "auditd.data.val", - "auditd.data.img-ctx", - "auditd.data.old-chardev", - "auditd.data.old_val", - "auditd.data.success", - "auditd.data.inode_uid", - "auditd.data.removed", - "auditd.data.socket.port", - "auditd.data.socket.saddr", - "auditd.data.socket.addr", - "auditd.data.socket.family", - "auditd.data.socket.path", - "geoip.continent_name", - "geoip.city_name", - "geoip.region_name", - "geoip.country_iso_code", - "hash.blake2b_256", - "hash.blake2b_384", - "hash.blake2b_512", - "hash.md5", - "hash.sha1", - "hash.sha224", - "hash.sha256", - "hash.sha384", - "hash.sha3_224", - "hash.sha3_256", - "hash.sha3_384", - "hash.sha3_512", - "hash.sha512", - "hash.sha512_224", - "hash.sha512_256", - "hash.xxh64", - "event.origin", - "user.entity_id", - "user.terminal", - "process.entity_id", - "process.hash.blake2b_256", - "process.hash.blake2b_384", - "process.hash.blake2b_512", - "process.hash.sha224", - "process.hash.sha384", - "process.hash.sha3_224", - "process.hash.sha3_256", - "process.hash.sha3_384", - "process.hash.sha3_512", - "process.hash.sha512_224", - "process.hash.sha512_256", - "process.hash.xxh64", - "socket.entity_id", - "system.audit.host.timezone.name", - "system.audit.host.hostname", - "system.audit.host.id", - "system.audit.host.architecture", - "system.audit.host.mac", - "system.audit.host.os.codename", - "system.audit.host.os.platform", - "system.audit.host.os.name", - "system.audit.host.os.family", - "system.audit.host.os.version", - "system.audit.host.os.kernel", - "system.audit.package.entity_id", - "system.audit.package.name", - "system.audit.package.version", - "system.audit.package.release", - "system.audit.package.arch", - "system.audit.package.license", - "system.audit.package.summary", - "system.audit.package.url", - "system.audit.user.name", - "system.audit.user.uid", - "system.audit.user.gid", - "system.audit.user.dir", - "system.audit.user.shell", - "system.audit.user.user_information", - "system.audit.user.password.type", - "fields.*" - ] - }, - "refresh_interval": "5s" - } - } - } -} From fd81692b98e69615e94f4e6dae89f19b0322d9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Mon, 28 Dec 2020 17:00:42 +0100 Subject: [PATCH 092/100] [Security Solution] Skip failing Cypress tests (#86967) --- .../cypress/integration/timeline_attach_to_case.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts index bbb6f672f11126..a0051eee0a22ef 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_attach_to_case.spec.ts @@ -18,7 +18,8 @@ import { createTimeline } from '../tasks/api_calls/timelines'; import { cleanKibana } from '../tasks/common'; import { createCase } from '../tasks/api_calls/cases'; -describe('attach timeline to case', () => { +// https://github.com/elastic/kibana/issues/86959 +describe.skip('attach timeline to case', () => { const myTimeline = { ...timeline }; context('without cases created', () => { From 83f6440c03d062349caf7dfc42c71baf4fef151d Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 11:10:36 -0600 Subject: [PATCH 093/100] skip "Closes and opens alerts" #83773 --- .../security_solution/cypress/integration/alerts.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index 82e214398f69a1..c409dbc7814fce 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -35,7 +35,7 @@ import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; -describe('Alerts', () => { +describe.skip('Alerts', () => { context('Closing alerts', () => { beforeEach(() => { cleanKibana(); From 3ddc527d500bbf7a49969085518c1d694702b4a0 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 13:29:16 -0600 Subject: [PATCH 094/100] skip "Fields Browser rendering. #60209 --- .../cypress/integration/fields_browser.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts index 98cb7418a08a66..55ded8014db3c7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/fields_browser.spec.ts @@ -46,7 +46,7 @@ const defaultHeaders = [ ]; describe('Fields Browser', () => { - context('Fields Browser rendering', () => { + context.skip('Fields Browser rendering', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); From db2f7e6bbb2152e677c57ece722f6ffbd907ad22 Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 28 Dec 2020 13:45:45 -0700 Subject: [PATCH 095/100] [ftr/flags] improve help text (#86971) Co-authored-by: spalger --- .../src/functional_test_runner/cli.ts | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/kbn-test/src/functional_test_runner/cli.ts b/packages/kbn-test/src/functional_test_runner/cli.ts index 8f53d6f7cf58b3..2dfc9ded662015 100644 --- a/packages/kbn-test/src/functional_test_runner/cli.ts +++ b/packages/kbn-test/src/functional_test_runner/cli.ts @@ -141,22 +141,27 @@ export function runFtrCli() { config: 'test/functional/config.js', }, help: ` - --config=path path to a config file - --bail stop tests after the first failure - --grep pattern used to select which tests to run - --invert invert grep to exclude tests - --include=file a test file to be included, pass multiple times for multiple files - --exclude=file a test file to be excluded, pass multiple times for multiple files - --include-tag=tag a tag to be included, pass multiple times for multiple tags - --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags - --test-stats print the number of tests (included and excluded) to STDERR - --updateBaselines replace baseline screenshots with whatever is generated from the test - --updateSnapshots replace inline and file snapshots with whatever is generated from the test - -u replace both baseline screenshots and snapshots - --kibana-install-dir directory where the Kibana install being tested resides - --throttle enable network throttling in Chrome browser - --headless run browser in headless mode - `, + --config=path path to a config file + --bail stop tests after the first failure + --grep pattern used to select which tests to run + --invert invert grep to exclude tests + --include=file a test file to be included, pass multiple times for multiple files + --exclude=file a test file to be excluded, pass multiple times for multiple files + --include-tag=tag a tag to be included, pass multiple times for multiple tags. Only + suites which have one of the passed include-tag tags will be executed. + When combined with the --exclude-tag flag both conditions must be met + for a suite to run. + --exclude-tag=tag a tag to be excluded, pass multiple times for multiple tags. Any suite + which has any of the exclude-tags will be excluded. When combined with + the --include-tag flag both conditions must be met for a suite to run. + --test-stats print the number of tests (included and excluded) to STDERR + --updateBaselines replace baseline screenshots with whatever is generated from the test + --updateSnapshots replace inline and file snapshots with whatever is generated from the test + -u replace both baseline screenshots and snapshots + --kibana-install-dir directory where the Kibana install being tested resides + --throttle enable network throttling in Chrome browser + --headless run browser in headless mode + `, }, } ); From 4290b58b6a9e44dd8d38228d88dd0555d20b6840 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 14:49:05 -0600 Subject: [PATCH 096/100] skip "adds correctly a filter to the global search bar" #86552 --- .../security_solution/cypress/integration/search_bar.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts index 7fcbc10f88b446..e5e74f6eb0cacb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/search_bar.spec.ts @@ -13,7 +13,7 @@ import { HOSTS_URL } from '../urls/navigation'; import { waitForAllHostsToBeLoaded } from '../tasks/hosts/all_hosts'; import { cleanKibana } from '../tasks/common'; -describe('SearchBar', () => { +describe.skip('SearchBar', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_URL); From 54390f02f98b3cda6890c05b74c3f78d6e6dd822 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 14:55:14 -0600 Subject: [PATCH 097/100] skip network and timeline inspection. #85677, #85678 --- .../security_solution/cypress/integration/inspect.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts index 6321be1e261516..98891e65771ce3 100644 --- a/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/inspect.spec.ts @@ -18,7 +18,7 @@ import { executeTimelineKQL, openTimelineInspectButton } from '../tasks/timeline import { HOSTS_URL, NETWORK_URL } from '../urls/navigation'; -describe('Inspect', () => { +describe.skip('Inspect', () => { context('Hosts stats and tables', () => { before(() => { cleanKibana(); From 92b2b60ad5ddbe8bfbfb0676749e2a7176860adc Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 15:11:01 -0600 Subject: [PATCH 098/100] [logging/json] use merge from kbn/std (#86330) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/logging/layouts/json_layout.ts | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/core/server/logging/layouts/json_layout.ts b/src/core/server/logging/layouts/json_layout.ts index 7573d0b8374161..34c3c325e73282 100644 --- a/src/core/server/logging/layouts/json_layout.ts +++ b/src/core/server/logging/layouts/json_layout.ts @@ -18,7 +18,7 @@ */ import moment from 'moment-timezone'; -import { merge } from 'lodash'; +import { merge } from '@kbn/std'; import { schema } from '@kbn/config-schema'; import { LogRecord, Layout } from '@kbn/logging'; @@ -53,22 +53,19 @@ export class JsonLayout implements Layout { } public format(record: LogRecord): string { - return JSON.stringify( - merge( - { - '@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'), - message: record.message, - error: JsonLayout.errorToSerializableObject(record.error), - log: { - level: record.level.id.toUpperCase(), - logger: record.context, - }, - process: { - pid: record.pid, - }, - }, - record.meta - ) - ); + const log = { + '@timestamp': moment(record.timestamp).format('YYYY-MM-DDTHH:mm:ss.SSSZ'), + message: record.message, + error: JsonLayout.errorToSerializableObject(record.error), + log: { + level: record.level.id.toUpperCase(), + logger: record.context, + }, + process: { + pid: record.pid, + }, + }; + const output = record.meta ? merge(log, record.meta) : log; + return JSON.stringify(output); } } From 62fd430fdf6d32841a72ff088fb38af0dbe2fb90 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 15:24:15 -0600 Subject: [PATCH 099/100] skip "Custom detection rules" #83772 --- .../cypress/integration/alerts_detection_rules_custom.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index d0b0862034a3ba..897f035d23b10b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -212,7 +212,7 @@ describe('Custom detection rules creation', () => { }); }); -describe('Custom detection rules deletion and edition', () => { +describe.skip('Custom detection rules deletion and edition', () => { context('Deletion', () => { beforeEach(() => { cleanKibana(); From d843450620d1016e47142db3ac657c95e3d5edfb Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Mon, 28 Dec 2020 17:34:19 -0600 Subject: [PATCH 100/100] skip "pagination updates results and page number" #86975 --- .../security_solution/cypress/integration/pagination.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts index 2896b2dbc36c6e..95cbf8220402f5 100644 --- a/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/pagination.spec.ts @@ -17,7 +17,7 @@ import { refreshPage } from '../tasks/security_header'; import { HOSTS_PAGE_TAB_URLS } from '../urls/navigation'; -describe('Pagination', () => { +describe.skip('Pagination', () => { before(() => { cleanKibana(); loginAndWaitForPage(HOSTS_PAGE_TAB_URLS.uncommonProcesses);