From 357257b12516d35e61e379d048eb15183cad8938 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 4 May 2020 15:33:59 -0400 Subject: [PATCH 01/55] [Ingest] Add data to Overview page (#65024) (#65122) --- .../common/types/rest_spec/agent.ts | 2 +- .../hooks/use_request/agents.ts | 9 + .../overview/components/agent_section.tsx | 91 ++++++++++ .../components/configuration_section.tsx | 79 +++++++++ .../components/datastream_section.tsx | 101 +++++++++++ .../components/integration_section.tsx | 78 +++++++++ .../overview/components/overview_panel.tsx | 26 +++ .../overview/components/overview_stats.tsx | 23 +++ .../sections/overview/index.tsx | 163 +----------------- 9 files changed, 417 insertions(+), 155 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/configuration_section.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/datastream_section.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_panel.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_stats.tsx diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 64ed95db74f4c..7214611ca9122 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -152,7 +152,7 @@ export interface UpdateAgentRequest { export interface GetAgentStatusRequest { query: { - configId: string; + configId?: string; }; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts index 453bcf2bd81e7..cad1791af41be 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agents.ts @@ -62,6 +62,15 @@ export function sendGetAgentStatus( }); } +export function useGetAgentStatus(query: GetAgentStatusRequest['query'], options?: RequestOptions) { + return useRequest({ + method: 'get', + path: agentRouteService.getStatusPath(), + query, + ...options, + }); +} + export function sendPutAgentReassign( agentId: string, body: PutAgentReassignRequest['body'], diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx new file mode 100644 index 0000000000000..0f6d3c5b55ce6 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx @@ -0,0 +1,91 @@ +/* + * 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 { EuiFlexItem, EuiI18nNumber } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiButtonEmpty, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { OverviewPanel } from './overview_panel'; +import { OverviewStats } from './overview_stats'; +import { useLink, useGetAgentStatus } from '../../../hooks'; +import { FLEET_PATH } from '../../../constants'; +import { Loading } from '../../fleet/components'; + +export const OverviewAgentSection = () => { + const agentStatusRequest = useGetAgentStatus({}); + + return ( + + +
+ +

+ +

+
+ + + +
+ + {agentStatusRequest.isLoading ? ( + + ) : ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + )} + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/configuration_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/configuration_section.tsx new file mode 100644 index 0000000000000..b74cac9a62176 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/configuration_section.tsx @@ -0,0 +1,79 @@ +/* + * 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 { EuiFlexItem, EuiI18nNumber } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiButtonEmpty, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { OverviewPanel } from './overview_panel'; +import { OverviewStats } from './overview_stats'; +import { useLink, useGetDatasources } from '../../../hooks'; +import { AgentConfig } from '../../../types'; +import { AGENT_CONFIG_PATH } from '../../../constants'; +import { Loading } from '../../fleet/components'; + +export const OverviewConfigurationSection: React.FC<{ agentConfigs: AgentConfig[] }> = ({ + agentConfigs, +}) => { + const datasourcesRequest = useGetDatasources({ + page: 1, + perPage: 10000, + }); + + return ( + + +
+ +

+ +

+
+ + + +
+ + {datasourcesRequest.isLoading ? ( + + ) : ( + <> + + + + + + + + + + + + + + )} + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/datastream_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/datastream_section.tsx new file mode 100644 index 0000000000000..7d1f0598a2767 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/datastream_section.tsx @@ -0,0 +1,101 @@ +/* + * 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 { EuiFlexItem, EuiI18nNumber } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiButtonEmpty, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { OverviewPanel } from './overview_panel'; +import { OverviewStats } from './overview_stats'; +import { useLink, useGetDataStreams, useStartDeps } from '../../../hooks'; +import { Loading } from '../../fleet/components'; +import { DATA_STREAM_PATH } from '../../../constants'; + +export const OverviewDatastreamSection: React.FC = () => { + const datastreamRequest = useGetDataStreams(); + const { + data: { fieldFormats }, + } = useStartDeps(); + + const total = datastreamRequest.data?.data_streams?.length ?? 0; + let sizeBytes = 0; + const namespaces = new Set(); + if (datastreamRequest.data) { + datastreamRequest.data.data_streams.forEach(val => { + namespaces.add(val.namespace); + sizeBytes += val.size_in_bytes; + }); + } + + let size: string; + try { + const formatter = fieldFormats.getInstance('bytes'); + size = formatter.convert(sizeBytes); + } catch (e) { + size = `${sizeBytes}b`; + } + + return ( + + +
+ +

+ +

+
+ + + +
+ + {datastreamRequest.isLoading ? ( + + ) : ( + <> + + + + + + + + + + + + + + + + {size} + + )} + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx new file mode 100644 index 0000000000000..f4c122af88371 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/integration_section.tsx @@ -0,0 +1,78 @@ +/* + * 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 { EuiFlexItem, EuiI18nNumber } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiButtonEmpty, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { OverviewPanel } from './overview_panel'; +import { OverviewStats } from './overview_stats'; +import { useLink, useGetPackages } from '../../../hooks'; +import { EPM_PATH } from '../../../constants'; +import { Loading } from '../../fleet/components'; +import { InstallationStatus } from '../../../types'; + +export const OverviewIntegrationSection: React.FC = () => { + const packagesRequest = useGetPackages(); + + const total = packagesRequest.data?.response?.length ?? 0; + const installed = + packagesRequest.data?.response?.filter(p => p.status === InstallationStatus.installed) + ?.length ?? 0; + return ( + + +
+ +

+ +

+
+ + + +
+ + {packagesRequest.isLoading ? ( + + ) : ( + <> + + + + + + + + + + + + + + )} + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_panel.tsx new file mode 100644 index 0000000000000..41d7a7a5f0bc3 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_panel.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import styled from 'styled-components'; +import { EuiPanel } from '@elastic/eui'; + +export const OverviewPanel = styled(EuiPanel).attrs(props => ({ + paddingSize: 'm', +}))` + header { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid ${props => props.theme.eui.euiColorLightShade}; + margin: -${props => props.theme.eui.paddingSizes.m} -${props => props.theme.eui.paddingSizes.m} + ${props => props.theme.eui.paddingSizes.m}; + padding: ${props => props.theme.eui.paddingSizes.s} ${props => props.theme.eui.paddingSizes.m}; + } + + h2 { + padding: ${props => props.theme.eui.paddingSizes.xs} 0; + } +`; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_stats.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_stats.tsx new file mode 100644 index 0000000000000..04de22c34fe6f --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/overview_stats.tsx @@ -0,0 +1,23 @@ +/* + * 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 styled from 'styled-components'; +import { EuiDescriptionList } from '@elastic/eui'; + +export const OverviewStats = styled(EuiDescriptionList).attrs(props => ({ + compressed: true, + textStyle: 'reverse', + type: 'column', +}))` + & > * { + margin-top: ${props => props.theme.eui.paddingSizes.s} !important; + + &:first-child, + &:nth-child(2) { + margin-top: 0 !important; + } + } +`; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx index 70d8e7d6882f8..3cd778fb4f016 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx @@ -7,14 +7,8 @@ import React, { useState } from 'react'; import styled from 'styled-components'; import { EuiButton, - EuiButtonEmpty, EuiBetaBadge, - EuiPanel, EuiText, - EuiTitle, - EuiDescriptionList, - EuiDescriptionListDescription, - EuiDescriptionListTitle, EuiFlexGrid, EuiFlexGroup, EuiFlexItem, @@ -22,42 +16,12 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { WithHeaderLayout } from '../../layouts'; -import { useLink, useGetAgentConfigs } from '../../hooks'; +import { useGetAgentConfigs } from '../../hooks'; import { AgentEnrollmentFlyout } from '../fleet/agent_list_page/components'; -import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH, DATA_STREAM_PATH } from '../../constants'; - -const OverviewPanel = styled(EuiPanel).attrs(props => ({ - paddingSize: 'm', -}))` - header { - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid ${props => props.theme.eui.euiColorLightShade}; - margin: -${props => props.theme.eui.paddingSizes.m} -${props => props.theme.eui.paddingSizes.m} - ${props => props.theme.eui.paddingSizes.m}; - padding: ${props => props.theme.eui.paddingSizes.s} ${props => props.theme.eui.paddingSizes.m}; - } - - h2 { - padding: ${props => props.theme.eui.paddingSizes.xs} 0; - } -`; - -const OverviewStats = styled(EuiDescriptionList).attrs(props => ({ - compressed: true, - textStyle: 'reverse', - type: 'column', -}))` - & > * { - margin-top: ${props => props.theme.eui.paddingSizes.s} !important; - - &:first-child, - &:nth-child(2) { - margin-top: 0 !important; - } - } -`; +import { OverviewAgentSection } from './components/agent_section'; +import { OverviewConfigurationSection } from './components/configuration_section'; +import { OverviewIntegrationSection } from './components/integration_section'; +import { OverviewDatastreamSection } from './components/datastream_section'; const AlphaBadge = styled(EuiBetaBadge)` vertical-align: top; @@ -135,121 +99,12 @@ export const IngestManagerOverview: React.FunctionComponent = () => { )} - - -
- -

- -

-
- - - -
- - Total available - 999 - Installed - 1 - Updated available - 0 - -
-
+ + - - -
- -

- -

-
- - - -
- - Total configs - 1 - Data sources - 1 - -
-
+ - - -
- -

- -

-
- - - -
- - Total agents - 0 - Active - 0 - Offline - 0 - Error - 0 - -
-
- - - -
- -

- -

-
- - - -
- - Data streams - 0 - Name spaces - 0 - Total size - 0 MB - -
-
+
); From c8e1efa02b07d3eceeef6df6c196fd6fc67d9fb7 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Mon, 4 May 2020 15:04:22 -0500 Subject: [PATCH 02/55] [SIEM] NP Plugin dependency cleanup (#64842) (#64955) * Remove static src dependencies from kibana.json We are only importing static code from these plugins, and not consuming their plugin contracts. For this reason, we can safely remove them from kibana.json as that imported code will always be included in our own bundle. * Make usageCollection an optional dependency If it's not enabled, we simply use a noop for our tracker call. * Remove unused plugin contracts These are only needed when we're actually using them in our codebase. For request handler contexts, we only need our kibana.json declaration. * Remove unnecessary try/catch With the addition of the null coalescing, the only chance for an error is in usageCollection. However, if the usageCollection contract changes, we should get a type error long before we see a runtime error. * Improve typings of our Plugin classes * passes missing generic arguments to public plugin interface * passes missing generic arguments to both plugins' CoreSetup types * Don't re-export core types Instead, import them from core as needed. Co-authored-by: Elastic Machine --- x-pack/plugins/siem/kibana.json | 12 ++++++++---- .../plugins/siem/public/lib/telemetry/index.ts | 8 +++----- x-pack/plugins/siem/public/plugin.tsx | 6 +++--- x-pack/plugins/siem/server/lib/compose/kibana.ts | 3 ++- .../lib/framework/kibana_framework_adapter.ts | 3 ++- x-pack/plugins/siem/server/plugin.ts | 16 ++++------------ 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/siem/kibana.json b/x-pack/plugins/siem/kibana.json index e0ff82eb10eb2..39e50838c1c97 100644 --- a/x-pack/plugins/siem/kibana.json +++ b/x-pack/plugins/siem/kibana.json @@ -8,18 +8,22 @@ "alerting", "data", "embeddable", - "esUiShared", "features", "home", "inspector", - "kibanaUtils", "licensing", "maps", "triggers_actions_ui", - "uiActions", + "uiActions" + ], + "optionalPlugins": [ + "encryptedSavedObjects", + "ml", + "newsfeed", + "security", + "spaces", "usageCollection" ], - "optionalPlugins": ["encryptedSavedObjects", "ml", "newsfeed", "security", "spaces"], "server": true, "ui": true } diff --git a/x-pack/plugins/siem/public/lib/telemetry/index.ts b/x-pack/plugins/siem/public/lib/telemetry/index.ts index 856b7783a5367..37d181e9b8ad7 100644 --- a/x-pack/plugins/siem/public/lib/telemetry/index.ts +++ b/x-pack/plugins/siem/public/lib/telemetry/index.ts @@ -13,6 +13,8 @@ export { METRIC_TYPE }; type TrackFn = (type: UiStatsMetricType, event: string | string[], count?: number) => void; +const noop = () => {}; + let _track: TrackFn; export const track: TrackFn = (type, event, count) => { @@ -24,11 +26,7 @@ export const track: TrackFn = (type, event, count) => { }; export const initTelemetry = (usageCollection: SetupPlugins['usageCollection'], appId: string) => { - try { - _track = usageCollection.reportUiStats.bind(null, appId); - } catch (error) { - // ignore failed setup here, as we'll just have an inert tracker - } + _track = usageCollection?.reportUiStats?.bind(null, appId) ?? noop; }; export enum TELEMETRY_EVENT { diff --git a/x-pack/plugins/siem/public/plugin.tsx b/x-pack/plugins/siem/public/plugin.tsx index 1128df282a5c2..2f2bd70569dcd 100644 --- a/x-pack/plugins/siem/public/plugin.tsx +++ b/x-pack/plugins/siem/public/plugin.tsx @@ -37,7 +37,7 @@ export interface SetupPlugins { home: HomePublicPluginSetup; security: SecurityPluginSetup; triggers_actions_ui: TriggersActionsSetup; - usageCollection: UsageCollectionSetup; + usageCollection?: UsageCollectionSetup; } export interface StartPlugins { @@ -59,14 +59,14 @@ export interface PluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PluginStart {} -export class Plugin implements IPlugin { +export class Plugin implements IPlugin { private kibanaVersion: string; constructor(initializerContext: PluginInitializerContext) { this.kibanaVersion = initializerContext.env.packageInfo.version; } - public setup(core: CoreSetup, plugins: SetupPlugins) { + public setup(core: CoreSetup, plugins: SetupPlugins) { initTelemetry(plugins.usageCollection, APP_ID); plugins.home.featureCatalogue.register({ diff --git a/x-pack/plugins/siem/server/lib/compose/kibana.ts b/x-pack/plugins/siem/server/lib/compose/kibana.ts index 4a595032e43eb..8bc90bed25168 100644 --- a/x-pack/plugins/siem/server/lib/compose/kibana.ts +++ b/x-pack/plugins/siem/server/lib/compose/kibana.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, SetupPlugins } from '../../plugin'; +import { CoreSetup } from '../../../../../../src/core/server'; +import { SetupPlugins } from '../../plugin'; import { Authentications } from '../authentications'; import { ElasticsearchAuthenticationAdapter } from '../authentications/elasticsearch_adapter'; diff --git a/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 762416149c0fb..1e99c40ef5727 100644 --- a/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -9,6 +9,7 @@ import { GraphQLSchema } from 'graphql'; import { runHttpQuery } from 'apollo-server-core'; import { schema as configSchema } from '@kbn/config-schema'; import { + CoreSetup, IRouter, KibanaResponseFactory, RequestHandlerContext, @@ -16,7 +17,7 @@ import { } from '../../../../../../src/core/server'; import { IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; import { AuthenticatedUser } from '../../../../security/common/model'; -import { CoreSetup, SetupPlugins } from '../../plugin'; +import { SetupPlugins } from '../../plugin'; import { FrameworkAdapter, diff --git a/x-pack/plugins/siem/server/plugin.ts b/x-pack/plugins/siem/server/plugin.ts index dc484b75ab531..3ef4b39bd0979 100644 --- a/x-pack/plugins/siem/server/plugin.ts +++ b/x-pack/plugins/siem/server/plugin.ts @@ -15,16 +15,12 @@ import { PluginInitializerContext, Logger, } from '../../../../src/core/server'; -import { - PluginStartContract as AlertingStart, - PluginSetupContract as AlertingSetup, -} from '../../alerting/server'; +import { PluginSetupContract as AlertingSetup } from '../../alerting/server'; import { SecurityPluginSetup as SecuritySetup } from '../../security/server'; import { PluginSetupContract as FeaturesSetup } from '../../features/server'; import { MlPluginSetup as MlSetup } from '../../ml/server'; import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server'; import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server'; -import { PluginStartContract as ActionsStart } from '../../actions/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { initServer } from './init_server'; import { compose } from './lib/compose/kibana'; @@ -40,8 +36,6 @@ import { createConfig$, ConfigType } from './config'; import { initUiSettings } from './ui_settings'; import { APP_ID, APP_ICON } from '../common/constants'; -export { CoreSetup, CoreStart }; - export interface SetupPlugins { alerting: AlertingSetup; encryptedSavedObjects?: EncryptedSavedObjectsSetup; @@ -52,10 +46,8 @@ export interface SetupPlugins { ml?: MlSetup; } -export interface StartPlugins { - actions: ActionsStart; - alerting: AlertingStart; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface StartPlugins {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PluginSetup {} @@ -77,7 +69,7 @@ export class Plugin implements IPlugin, plugins: SetupPlugins) { this.logger.debug('plugin setup'); if (hasListsFeature()) { From 0887cacf1819a5a421a1d6cffb34611500c10ca6 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 4 May 2020 21:31:49 +0100 Subject: [PATCH 03/55] [ML] Moving get filters capability to admin (#64879) (#64890) * [ML] Moving get filters capability to admin * updating test Co-authored-by: Elastic Machine --- x-pack/plugins/ml/common/types/capabilities.ts | 4 ++-- .../lib/capabilities/check_capabilities.test.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index da5fd3ac25209..572217ce16eee 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -15,8 +15,6 @@ export const userMlCapabilities = { canGetCalendars: false, // File Data Visualizer canFindFileStructure: false, - // Filters - canGetFilters: false, // Data Frame Analytics canGetDataFrameAnalytics: false, // Annotations @@ -38,6 +36,8 @@ export const adminMlCapabilities = { canStartStopDatafeed: false, canUpdateDatafeed: false, canPreviewDatafeed: false, + // Filters + canGetFilters: false, // Calendars canCreateCalendar: false, canDeleteCalendar: false, diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index b6e95ae8373ee..746c9da47d0ad 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -64,7 +64,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(true); @@ -81,6 +80,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -113,7 +113,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(true); @@ -130,6 +129,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(true); expect(capabilities.canUpdateDatafeed).toBe(true); expect(capabilities.canPreviewDatafeed).toBe(true); + expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateCalendar).toBe(true); expect(capabilities.canDeleteCalendar).toBe(true); expect(capabilities.canCreateFilter).toBe(true); @@ -162,7 +162,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); @@ -177,6 +176,7 @@ describe('check_capabilities', () => { expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); expect(capabilities.canDeleteDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); @@ -211,7 +211,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); @@ -228,6 +227,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -260,7 +260,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); @@ -277,6 +276,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -311,7 +311,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); @@ -328,6 +327,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); From 8664ccc3c4be95e8b914947b941a227675e3ef0a Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 4 May 2020 22:35:51 +0200 Subject: [PATCH 04/55] Fix 37422 (#64215) (#65111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 rename Filter -> ExpressionValueFilter * refactor: 💡 use new filter type in Canvas * fix: 🐛 fix tests after refactor Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../common/expression_types/specs/filter.ts | 32 ++++++++++--------- src/plugins/expressions/public/index.ts | 2 +- src/plugins/expressions/server/index.ts | 2 +- .../functions/common/exactly.test.js | 2 +- .../functions/common/exactly.ts | 14 +++++--- .../functions/common/saved_lens.test.ts | 15 +++++++-- .../functions/common/saved_lens.ts | 8 ++--- .../functions/common/saved_map.test.ts | 15 +++++++-- .../functions/common/saved_map.ts | 4 +-- .../functions/common/saved_search.test.ts | 15 +++++++-- .../functions/common/saved_search.ts | 4 +-- .../common/saved_visualization.test.ts | 15 +++++++-- .../functions/common/saved_visualization.ts | 4 +-- .../functions/common/timefilter.test.js | 2 +- .../functions/common/timefilter.ts | 11 ++++--- .../functions/server/demodata.test.ts | 7 ++-- .../functions/server/demodata/index.ts | 9 ++++-- .../functions/server/escount.ts | 15 +++++++-- .../functions/server/esdocs.ts | 12 +++++-- .../functions/server/essql.ts | 9 ++++-- .../canvas/common/lib/datatable/query.js | 4 +-- .../canvas/public/functions/filters.ts | 9 ++++-- .../canvas/public/functions/timelion.ts | 7 ++-- .../lib/build_embeddable_filters.test.ts | 12 ++++--- .../public/lib/build_embeddable_filters.ts | 13 ++++---- .../canvas/server/lib/get_es_filter.js | 8 ++--- x-pack/legacy/plugins/canvas/types/state.ts | 4 +-- 27 files changed, 165 insertions(+), 89 deletions(-) diff --git a/src/plugins/expressions/common/expression_types/specs/filter.ts b/src/plugins/expressions/common/expression_types/specs/filter.ts index 01d6b8a603db6..fc1c086e817c9 100644 --- a/src/plugins/expressions/common/expression_types/specs/filter.ts +++ b/src/plugins/expressions/common/expression_types/specs/filter.ts @@ -17,29 +17,31 @@ * under the License. */ -import { ExpressionTypeDefinition } from '../types'; - -const name = 'filter'; +import { ExpressionTypeDefinition, ExpressionValueBoxed } from '../types'; /** * Represents an object that is a Filter. */ -export interface Filter { - type?: string; - value?: string; - column?: string; - and: Filter[]; - to?: string; - from?: string; - query?: string | null; -} +export type ExpressionValueFilter = ExpressionValueBoxed< + 'filter', + { + filterType?: string; + value?: string; + column?: string; + and: ExpressionValueFilter[]; + to?: string; + from?: string; + query?: string | null; + } +>; -export const filter: ExpressionTypeDefinition = { - name, +export const filter: ExpressionTypeDefinition<'filter', ExpressionValueFilter> = { + name: 'filter', from: { null: () => { return { - type: name, + type: 'filter', + filterType: 'filter', // Any meta data you wish to pass along. meta: {}, // And filters. If you need an "or", create a filter type for it. diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 6814764ee5faa..ee3fbd7a7b0b0 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -78,7 +78,7 @@ export { ExpressionValueRender, ExpressionValueSearchContext, ExpressionValueUnboxed, - Filter, + ExpressionValueFilter, Font, FontLabel, FontStyle, diff --git a/src/plugins/expressions/server/index.ts b/src/plugins/expressions/server/index.ts index e41135b693922..61d3838466bef 100644 --- a/src/plugins/expressions/server/index.ts +++ b/src/plugins/expressions/server/index.ts @@ -69,7 +69,7 @@ export { ExpressionValueRender, ExpressionValueSearchContext, ExpressionValueUnboxed, - Filter, + ExpressionValueFilter, Font, FontLabel, FontStyle, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.test.js index f03bc54757c3c..2b9bdb59afbdf 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.test.js @@ -18,7 +18,7 @@ describe('exactly', () => { it("adds an exactly object to 'and'", () => { const result = fn(emptyFilter, { column: 'name', value: 'product2' }); - expect(result.and[0]).toHaveProperty('type', 'exactly'); + expect(result.and[0]).toHaveProperty('filterType', 'exactly'); }); describe('args', () => { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.ts index 88a24186d6044..5031e8029957b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/exactly.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter, ExpressionFunctionDefinition } from '../../../types'; +import { ExpressionValueFilter, ExpressionFunctionDefinition } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; interface Arguments { @@ -13,7 +13,12 @@ interface Arguments { filterGroup: string; } -export function exactly(): ExpressionFunctionDefinition<'exactly', Filter, Arguments, Filter> { +export function exactly(): ExpressionFunctionDefinition< + 'exactly', + ExpressionValueFilter, + Arguments, + ExpressionValueFilter +> { const { help, args: argHelp } = getFunctionHelp().exactly; return { @@ -43,8 +48,9 @@ export function exactly(): ExpressionFunctionDefinition<'exactly', Filter, Argum fn: (input, args) => { const { value, column } = args; - const filter = { - type: 'exactly', + const filter: ExpressionValueFilter = { + type: 'filter', + filterType: 'exactly', value, column, and: [], diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts index 6b197148e6373..882d1e2ea58b9 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.test.ts @@ -6,14 +6,23 @@ jest.mock('ui/new_platform'); import { savedLens } from './saved_lens'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; +import { ExpressionValueFilter } from '../../../types'; -const filterContext = { +const filterContext: ExpressionValueFilter = { + type: 'filter', and: [ - { and: [], value: 'filter-value', column: 'filter-column', type: 'exactly' }, { + type: 'filter', + and: [], + value: 'filter-value', + column: 'filter-column', + filterType: 'exactly', + }, + { + type: 'filter', and: [], column: 'time-column', - type: 'time', + filterType: 'time', from: '2019-06-04T04:00:00.000Z', to: '2019-06-05T04:00:00.000Z', }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts index 2985a68cf855c..8fc55ddf9cc59 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_lens.ts @@ -8,7 +8,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { TimeRange, Filter as DataFilter } from 'src/plugins/data/public'; import { EmbeddableInput } from 'src/plugins/embeddable/public'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; -import { Filter, TimeRange as TimeRangeArg } from '../../../types'; +import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types'; import { EmbeddableTypes, EmbeddableExpressionType, @@ -37,7 +37,7 @@ type Return = EmbeddableExpression; export function savedLens(): ExpressionFunctionDefinition< 'savedLens', - Filter | null, + ExpressionValueFilter | null, Arguments, Return > { @@ -63,8 +63,8 @@ export function savedLens(): ExpressionFunctionDefinition< }, }, type: EmbeddableExpressionType, - fn: (context, args) => { - const filters = context ? context.and : []; + fn: (input, args) => { + const filters = input ? input.and : []; return { type: EmbeddableExpressionType, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts index 63dbae55790a3..74e41a030de35 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.test.ts @@ -6,14 +6,23 @@ jest.mock('ui/new_platform'); import { savedMap } from './saved_map'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; +import { ExpressionValueFilter } from '../../../types'; -const filterContext = { +const filterContext: ExpressionValueFilter = { + type: 'filter', and: [ - { and: [], value: 'filter-value', column: 'filter-column', type: 'exactly' }, { + type: 'filter', + and: [], + value: 'filter-value', + column: 'filter-column', + filterType: 'exactly', + }, + { + type: 'filter', and: [], column: 'time-column', - type: 'time', + filterType: 'time', from: '2019-06-04T04:00:00.000Z', to: '2019-06-05T04:00:00.000Z', }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts index cba19ce7da80f..df316d0dd182f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_map.ts @@ -6,7 +6,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; -import { Filter, MapCenter, TimeRange as TimeRangeArg } from '../../../types'; +import { ExpressionValueFilter, MapCenter, TimeRange as TimeRangeArg } from '../../../types'; import { EmbeddableTypes, EmbeddableExpressionType, @@ -32,7 +32,7 @@ type Output = EmbeddableExpression; export function savedMap(): ExpressionFunctionDefinition< 'savedMap', - Filter | null, + ExpressionValueFilter | null, Arguments, Output > { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts index 67356dae5b3e3..9bd32202b563a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.test.ts @@ -6,14 +6,23 @@ jest.mock('ui/new_platform'); import { savedSearch } from './saved_search'; import { buildEmbeddableFilters } from '../../../public/lib/build_embeddable_filters'; +import { ExpressionValueFilter } from '../../../types'; -const filterContext = { +const filterContext: ExpressionValueFilter = { + type: 'filter', and: [ - { and: [], value: 'filter-value', column: 'filter-column', type: 'exactly' }, { + type: 'filter', + and: [], + value: 'filter-value', + column: 'filter-column', + filterType: 'exactly', + }, + { + type: 'filter', and: [], column: 'time-column', - type: 'time', + filterType: 'time', from: '2019-06-04T04:00:00.000Z', to: '2019-06-05T04:00:00.000Z', }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts index 17969bbc5fa13..3875778153140 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_search.ts @@ -13,7 +13,7 @@ import { } from '../../expression_types'; import { buildEmbeddableFilters } from '../../../public/lib/build_embeddable_filters'; -import { Filter } from '../../../types'; +import { ExpressionValueFilter } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; interface Arguments { @@ -24,7 +24,7 @@ type Output = EmbeddableExpression & { id: SearchInput['id' export function savedSearch(): ExpressionFunctionDefinition< 'savedSearch', - Filter | null, + ExpressionValueFilter | null, Arguments, Output > { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts index 754a113b87554..8327c1433b9af 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.test.ts @@ -6,14 +6,23 @@ jest.mock('ui/new_platform'); import { savedVisualization } from './saved_visualization'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; +import { ExpressionValueFilter } from '../../../types'; -const filterContext = { +const filterContext: ExpressionValueFilter = { + type: 'filter', and: [ - { and: [], value: 'filter-value', column: 'filter-column', type: 'exactly' }, { + type: 'filter', + and: [], + value: 'filter-value', + column: 'filter-column', + filterType: 'exactly', + }, + { + type: 'filter', and: [], column: 'time-column', - type: 'time', + filterType: 'time', from: '2019-06-04T04:00:00.000Z', to: '2019-06-05T04:00:00.000Z', }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts index 26243980ee07a..91cbb02e2d176 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/saved_visualization.ts @@ -12,7 +12,7 @@ import { EmbeddableExpression, } from '../../expression_types'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; -import { Filter, TimeRange as TimeRangeArg, SeriesStyle } from '../../../types'; +import { ExpressionValueFilter, TimeRange as TimeRangeArg, SeriesStyle } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; interface Arguments { @@ -31,7 +31,7 @@ const defaultTimeRange = { export function savedVisualization(): ExpressionFunctionDefinition< 'savedVisualization', - Filter | null, + ExpressionValueFilter | null, Arguments, Output > { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.test.js index aeab0d50c31a7..834b9d195856c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.test.js @@ -44,7 +44,7 @@ describe('timefilter', () => { from: fromDate, to: toDate, }).and[0] - ).toHaveProperty('type', 'time'); + ).toHaveProperty('filterType', 'time'); }); describe('args', () => { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts index 249faf6141b46..ff7b56d8194df 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilter.ts @@ -5,7 +5,7 @@ */ import dateMath from '@elastic/datemath'; -import { Filter, ExpressionFunctionDefinition } from '../../../types'; +import { ExpressionValueFilter, ExpressionFunctionDefinition } from '../../../types'; import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { @@ -17,9 +17,9 @@ interface Arguments { export function timefilter(): ExpressionFunctionDefinition< 'timefilter', - Filter, + ExpressionValueFilter, Arguments, - Filter + ExpressionValueFilter > { const { help, args: argHelp } = getFunctionHelp().timefilter; const errors = getFunctionErrors().timefilter; @@ -58,8 +58,9 @@ export function timefilter(): ExpressionFunctionDefinition< } const { from, to, column } = args; - const filter: Filter = { - type: 'time', + const filter: ExpressionValueFilter = { + type: 'filter', + filterType: 'time', column, and: [], }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata.test.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata.test.ts index 94b2d5228665b..2b517664793a7 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata.test.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata.test.ts @@ -5,12 +5,11 @@ */ import { demodata } from './demodata'; +import { ExpressionValueFilter } from '../../../types'; -const nullFilter = { +const nullFilter: ExpressionValueFilter = { type: 'filter', - meta: {}, - size: null, - sort: [], + filterType: 'filter', and: [], }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts index 5cebae5bb669f..843e2bda47e12 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts @@ -10,14 +10,19 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; import { queryDatatable } from '../../../../common/lib/datatable/query'; import { DemoRows } from './demo_rows_types'; import { getDemoRows } from './get_demo_rows'; -import { Filter, Datatable, DatatableColumn, DatatableRow } from '../../../../types'; +import { ExpressionValueFilter, Datatable, DatatableColumn, DatatableRow } from '../../../../types'; import { getFunctionHelp } from '../../../../i18n'; interface Arguments { type: string; } -export function demodata(): ExpressionFunctionDefinition<'demodata', Filter, Arguments, Datatable> { +export function demodata(): ExpressionFunctionDefinition< + 'demodata', + ExpressionValueFilter, + Arguments, + Datatable +> { const { help, args: argHelp } = getFunctionHelp().demodata; return { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts index ffb8bb4f3e2a7..3f5d0610b4c72 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/escount.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ExpressionFunctionDefinition, Filter } from 'src/plugins/expressions/common'; +import { + ExpressionFunctionDefinition, + ExpressionValueFilter, +} from 'src/plugins/expressions/common'; // @ts-ignore untyped local import { buildESRequest } from '../../../server/lib/build_es_request'; import { getFunctionHelp } from '../../../i18n'; @@ -14,7 +17,12 @@ interface Arguments { query: string; } -export function escount(): ExpressionFunctionDefinition<'escount', Filter, Arguments, any> { +export function escount(): ExpressionFunctionDefinition< + 'escount', + ExpressionValueFilter, + Arguments, + any +> { const { help, args: argHelp } = getFunctionHelp().escount; return { @@ -40,7 +48,8 @@ export function escount(): ExpressionFunctionDefinition<'escount', Filter, Argum fn: (input, args, handlers) => { input.and = input.and.concat([ { - type: 'luceneQueryString', + type: 'filter', + filterType: 'luceneQueryString', query: args.query, and: [], }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts index 5bff06bb3933b..d60297ee2da3f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/esdocs.ts @@ -8,7 +8,7 @@ import squel from 'squel'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; // @ts-ignore untyped local import { queryEsSQL } from '../../../server/lib/query_es_sql'; -import { Filter } from '../../../types'; +import { ExpressionValueFilter } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; interface Arguments { @@ -20,7 +20,12 @@ interface Arguments { count: number; } -export function esdocs(): ExpressionFunctionDefinition<'esdocs', Filter, Arguments, any> { +export function esdocs(): ExpressionFunctionDefinition< + 'esdocs', + ExpressionValueFilter, + Arguments, + any +> { const { help, args: argHelp } = getFunctionHelp().esdocs; return { @@ -67,7 +72,8 @@ export function esdocs(): ExpressionFunctionDefinition<'esdocs', Filter, Argumen input.and = input.and.concat([ { - type: 'luceneQueryString', + type: 'filter', + filterType: 'luceneQueryString', query: args.query, and: [], }, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts index cdb6b5af82015..b972f5a3bd4a6 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/essql.ts @@ -7,7 +7,7 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; // @ts-ignore untyped local import { queryEsSQL } from '../../../server/lib/query_es_sql'; -import { Filter } from '../../../types'; +import { ExpressionValueFilter } from '../../../types'; import { getFunctionHelp } from '../../../i18n'; interface Arguments { @@ -16,7 +16,12 @@ interface Arguments { timezone: string; } -export function essql(): ExpressionFunctionDefinition<'essql', Filter, Arguments, any> { +export function essql(): ExpressionFunctionDefinition< + 'essql', + ExpressionValueFilter, + Arguments, + any +> { const { help, args: argHelp } = getFunctionHelp().essql; return { diff --git a/x-pack/legacy/plugins/canvas/common/lib/datatable/query.js b/x-pack/legacy/plugins/canvas/common/lib/datatable/query.js index f61e2b6434697..63945ce7690f9 100644 --- a/x-pack/legacy/plugins/canvas/common/lib/datatable/query.js +++ b/x-pack/legacy/plugins/canvas/common/lib/datatable/query.js @@ -15,14 +15,14 @@ export function queryDatatable(datatable, query) { if (query.and) { query.and.forEach(filter => { // handle exact matches - if (filter.type === 'exactly') { + if (filter.filterType === 'exactly') { datatable.rows = datatable.rows.filter(row => { return row[filter.column] === filter.value; }); } // handle time filters - if (filter.type === 'time') { + if (filter.filterType === 'time') { const columnNames = datatable.columns.map(col => col.name); // remove row if no column match diff --git a/x-pack/legacy/plugins/canvas/public/functions/filters.ts b/x-pack/legacy/plugins/canvas/public/functions/filters.ts index 2a3bc481d7dae..16d0bb0fff708 100644 --- a/x-pack/legacy/plugins/canvas/public/functions/filters.ts +++ b/x-pack/legacy/plugins/canvas/public/functions/filters.ts @@ -11,7 +11,7 @@ import { interpretAst } from '../lib/run_interpreter'; // @ts-ignore untyped local import { getState } from '../state/store'; import { getGlobalFilters } from '../state/selectors/workpad'; -import { Filter } from '../../types'; +import { ExpressionValueFilter } from '../../types'; import { getFunctionHelp } from '../../i18n'; import { InitializeArguments } from '.'; @@ -41,7 +41,12 @@ function getFiltersByGroup(allFilters: string[], groups?: string[], ungrouped = }); } -type FiltersFunction = ExpressionFunctionDefinition<'filters', null, Arguments, Filter>; +type FiltersFunction = ExpressionFunctionDefinition< + 'filters', + null, + Arguments, + ExpressionValueFilter +>; export function filtersFunctionFactory(initialize: InitializeArguments): () => FiltersFunction { return function filters(): FiltersFunction { diff --git a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts index e59d798108945..d07b3bf6d1d1c 100644 --- a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts +++ b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts @@ -11,7 +11,7 @@ import { ExpressionFunctionDefinition, DatatableRow } from 'src/plugins/expressi import { fetch } from '../../common/lib/fetch'; // @ts-ignore untyped local import { buildBoolArray } from '../../server/lib/build_bool_array'; -import { Datatable, Filter } from '../../types'; +import { Datatable, ExpressionValueFilter } from '../../types'; import { getFunctionHelp } from '../../i18n'; import { InitializeArguments } from './'; @@ -49,7 +49,7 @@ function parseDateMath( type TimelionFunction = ExpressionFunctionDefinition< 'timelion', - Filter, + ExpressionValueFilter, Arguments, Promise >; @@ -94,11 +94,10 @@ export function timelionFunctionFactory(initialize: InitializeArguments): () => fn: (input, args): Promise => { // Timelion requires a time range. Use the time range from the timefilter element in the // workpad, if it exists. Otherwise fall back on the function args. - const timeFilter = input.and.find(and => and.type === 'time'); + const timeFilter = input.and.find(and => and.filterType === 'time'); const range = timeFilter ? { min: timeFilter.from, max: timeFilter.to } : parseDateMath({ from: args.from, to: args.to }, args.timezone, initialize.timefilter); - const body = { extended: { es: { diff --git a/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.test.ts b/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.test.ts index b422a9451293f..77be181d47378 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.test.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.test.ts @@ -5,19 +5,21 @@ */ import { buildEmbeddableFilters } from './build_embeddable_filters'; -import { Filter } from '../../types'; +import { ExpressionValueFilter } from '../../types'; -const columnFilter: Filter = { +const columnFilter: ExpressionValueFilter = { + type: 'filter', and: [], value: 'filter-value', column: 'filter-column', - type: 'exactly', + filterType: 'exactly', }; -const timeFilter: Filter = { +const timeFilter: ExpressionValueFilter = { + type: 'filter', and: [], column: 'time-column', - type: 'time', + filterType: 'time', from: '2019-06-04T04:00:00.000Z', to: '2019-06-05T04:00:00.000Z', }; diff --git a/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.ts b/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.ts index 1a5d2119a94b6..aa915d0d3d02a 100644 --- a/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.ts +++ b/x-pack/legacy/plugins/canvas/public/lib/build_embeddable_filters.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Filter } from '../../types'; +import { ExpressionValueFilter } from '../../types'; // @ts-ignore Untyped Local import { buildBoolArray } from './build_bool_array'; import { @@ -20,9 +20,9 @@ export interface EmbeddableFilterInput { const TimeFilterType = 'time'; -function getTimeRangeFromFilters(filters: Filter[]): TimeRange | undefined { +function getTimeRangeFromFilters(filters: ExpressionValueFilter[]): TimeRange | undefined { const timeFilter = filters.find( - filter => filter.type !== undefined && filter.type === TimeFilterType + filter => filter.filterType !== undefined && filter.filterType === TimeFilterType ); return timeFilter !== undefined && timeFilter.from !== undefined && timeFilter.to !== undefined @@ -33,11 +33,12 @@ function getTimeRangeFromFilters(filters: Filter[]): TimeRange | undefined { : undefined; } -export function getQueryFilters(filters: Filter[]): DataFilter[] { - return buildBoolArray(filters).map(esFilters.buildQueryFilter); +export function getQueryFilters(filters: ExpressionValueFilter[]): DataFilter[] { + const dataFilters = filters.map(filter => ({ ...filter, type: filter.filterType })); + return buildBoolArray(dataFilters).map(esFilters.buildQueryFilter); } -export function buildEmbeddableFilters(filters: Filter[]): EmbeddableFilterInput { +export function buildEmbeddableFilters(filters: ExpressionValueFilter[]): EmbeddableFilterInput { return { timeRange: getTimeRangeFromFilters(filters), filters: getQueryFilters(filters), diff --git a/x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js b/x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js index e8a4d704118e8..7c025ed8dee9b 100644 --- a/x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js +++ b/x-pack/legacy/plugins/canvas/server/lib/get_es_filter.js @@ -14,13 +14,13 @@ import * as filters from './filters'; export function getESFilter(filter) { - if (!filters[filter.type]) { - throw new Error(`Unknown filter type: ${filter.type}`); + if (!filters[filter.filterType]) { + throw new Error(`Unknown filter type: ${filter.filterType}`); } try { - return filters[filter.type](filter); + return filters[filter.filterType](filter); } catch (e) { - throw new Error(`Could not create elasticsearch filter from ${filter.type}`); + throw new Error(`Could not create elasticsearch filter from ${filter.filterType}`); } } diff --git a/x-pack/legacy/plugins/canvas/types/state.ts b/x-pack/legacy/plugins/canvas/types/state.ts index 13c8f7a9176ab..e9b580f81e668 100644 --- a/x-pack/legacy/plugins/canvas/types/state.ts +++ b/x-pack/legacy/plugins/canvas/types/state.ts @@ -6,7 +6,7 @@ import { Datatable, - Filter, + ExpressionValueFilter, ExpressionImage, ExpressionFunction, KibanaContext, @@ -46,7 +46,7 @@ interface ElementStatsType { type ExpressionType = | Datatable - | Filter + | ExpressionValueFilter | ExpressionImage | KibanaContext | KibanaDatatable From 9bc4ea0d086746110ac00bfcd1eb61a942be94a1 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 4 May 2020 21:45:54 +0100 Subject: [PATCH 05/55] [ML] Enabling mml estimation in data recognizer module setup (#64900) (#65141) * [ML] Enabling mml estimation in data recognizer module setup * small refactor * adding functional tests * increasing uptime test timeout * tiny refactor * checking for default setting * testng flakey uptime test * catching erros in mml estimation * lowering timeout * ensuing data is present for ML tests * adding await Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../jobs/new_job/recognize/page.tsx | 1 - .../models/data_recognizer/data_recognizer.ts | 106 ++++++++++-------- .../apis/ml/modules/setup_module.ts | 74 +++++++++++- .../test/functional/apps/uptime/ml_anomaly.ts | 3 + .../functional/services/uptime/ml_anomaly.ts | 2 +- 5 files changed, 136 insertions(+), 50 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx index 9b76b9be9bf45..50c35ec426acb 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/recognize/page.tsx @@ -172,7 +172,6 @@ export const Page: FC = ({ moduleId, existingGroupIds }) => { startDatafeed: startDatafeedAfterSave, ...(jobOverridesPayload !== null ? { jobOverrides: jobOverridesPayload } : {}), ...resultTimeRange, - estimateModelMemory: false, }); const { datafeeds: datafeedsResponse, jobs: jobsResponse, kibana: kibanaResponse } = response; diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index 40b2a524151b3..92ab7739dbcfb 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -110,7 +110,7 @@ export class DataRecognizer { /** * List of the module jobs that require model memory estimation */ - jobsForModelMemoryEstimation: ModuleJob[] = []; + jobsForModelMemoryEstimation: Array<{ job: ModuleJob; query: any }> = []; constructor( private callAsCurrentUser: APICaller, @@ -374,7 +374,7 @@ export class DataRecognizer { end?: number, jobOverrides?: JobOverride | JobOverride[], datafeedOverrides?: DatafeedOverride | DatafeedOverride[], - estimateModelMemory?: boolean + estimateModelMemory: boolean = true ) { // load the config from disk const moduleConfig = await this.getModule(moduleId, jobPrefix); @@ -416,7 +416,10 @@ export class DataRecognizer { savedObjects: [] as KibanaObjectResponse[], }; - this.jobsForModelMemoryEstimation = moduleConfig.jobs; + this.jobsForModelMemoryEstimation = moduleConfig.jobs.map(job => ({ + job, + query: moduleConfig.datafeeds.find(d => d.config.job_id === job.id)?.config.query ?? null, + })); this.applyJobConfigOverrides(moduleConfig, jobOverrides, jobPrefix); this.applyDatafeedConfigOverrides(moduleConfig, datafeedOverrides, jobPrefix); @@ -958,7 +961,7 @@ export class DataRecognizer { */ async updateModelMemoryLimits( moduleConfig: Module, - estimateMML: boolean = false, + estimateMML: boolean, start?: number, end?: number ) { @@ -967,53 +970,57 @@ export class DataRecognizer { } if (estimateMML && this.jobsForModelMemoryEstimation.length > 0) { - const calculateModelMemoryLimit = calculateModelMemoryLimitProvider(this.callAsCurrentUser); - const query = moduleConfig.query ?? null; - - // Checks if all jobs in the module have the same time field configured - const isSameTimeFields = this.jobsForModelMemoryEstimation.every( - job => - job.config.data_description.time_field === - this.jobsForModelMemoryEstimation[0].config.data_description.time_field - ); + try { + const calculateModelMemoryLimit = calculateModelMemoryLimitProvider(this.callAsCurrentUser); - if (isSameTimeFields && (start === undefined || end === undefined)) { - // In case of time range is not provided and the time field is the same - // set the fallback range for all jobs - const { start: fallbackStart, end: fallbackEnd } = await this.getFallbackTimeRange( - this.jobsForModelMemoryEstimation[0].config.data_description.time_field, - query + // Checks if all jobs in the module have the same time field configured + const firstJobTimeField = this.jobsForModelMemoryEstimation[0].job.config.data_description + .time_field; + const isSameTimeFields = this.jobsForModelMemoryEstimation.every( + ({ job }) => job.config.data_description.time_field === firstJobTimeField ); - start = fallbackStart; - end = fallbackEnd; - } - for (const job of this.jobsForModelMemoryEstimation) { - let earliestMs = start; - let latestMs = end; - if (earliestMs === undefined || latestMs === undefined) { - const timeFieldRange = await this.getFallbackTimeRange( + if (isSameTimeFields && (start === undefined || end === undefined)) { + // In case of time range is not provided and the time field is the same + // set the fallback range for all jobs + // as there may not be a common query, we use a match_all + const { + start: fallbackStart, + end: fallbackEnd, + } = await this.getFallbackTimeRange(firstJobTimeField, { match_all: {} }); + start = fallbackStart; + end = fallbackEnd; + } + + for (const { job, query } of this.jobsForModelMemoryEstimation) { + let earliestMs = start; + let latestMs = end; + if (earliestMs === undefined || latestMs === undefined) { + const timeFieldRange = await this.getFallbackTimeRange( + job.config.data_description.time_field, + query + ); + earliestMs = timeFieldRange.start; + latestMs = timeFieldRange.end; + } + + const { modelMemoryLimit } = await calculateModelMemoryLimit( + job.config.analysis_config, + this.indexPatternName, + query, job.config.data_description.time_field, - query + earliestMs, + latestMs ); - earliestMs = timeFieldRange.start; - latestMs = timeFieldRange.end; - } - const { modelMemoryLimit } = await calculateModelMemoryLimit( - job.config.analysis_config, - this.indexPatternName, - query, - job.config.data_description.time_field, - earliestMs, - latestMs - ); + if (!job.config.analysis_limits) { + job.config.analysis_limits = {} as AnalysisLimits; + } - if (!job.config.analysis_limits) { - job.config.analysis_limits = {} as AnalysisLimits; + job.config.analysis_limits.model_memory_limit = modelMemoryLimit; } - - job.config.analysis_limits.model_memory_limit = modelMemoryLimit; + } catch (error) { + mlLog.warn(`Data recognizer could not estimate model memory limit ${error}`); } } @@ -1098,10 +1105,15 @@ export class DataRecognizer { if (generalOverrides.some(override => !!override.analysis_limits?.model_memory_limit)) { this.jobsForModelMemoryEstimation = []; } else { - this.jobsForModelMemoryEstimation = moduleConfig.jobs.filter(job => { - const override = jobSpecificOverrides.find(o => `${jobPrefix}${o.job_id}` === job.id); - return override?.analysis_limits?.model_memory_limit === undefined; - }); + this.jobsForModelMemoryEstimation = moduleConfig.jobs + .filter(job => { + const override = jobSpecificOverrides.find(o => `${jobPrefix}${o.job_id}` === job.id); + return override?.analysis_limits?.model_memory_limit === undefined; + }) + .map(job => ({ + job, + query: moduleConfig.datafeeds.find(d => d.config.job_id === job.id)?.config.query || null, + })); } function processArrayValues(source: any, update: any) { diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts index c42fc95c1bc7f..39c87a91f0ccf 100644 --- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { JOB_STATE, DATAFEED_STATE } from '../../../../../plugins/ml/common/constants/states'; +import { Job } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs'; import { USER } from '../../../../functional/services/machine_learning/security_common'; const COMMON_HEADERS = { @@ -23,7 +24,8 @@ export default ({ getService }: FtrProviderContext) => { const testDataListPositive = [ { - testTitleSuffix: 'for sample logs dataset with prefix and startDatafeed false', + testTitleSuffix: + 'for sample logs dataset with prefix, startDatafeed false and estimateModelMemory false', sourceDataArchive: 'ml/sample_logs', indexPattern: { name: 'kibana_sample_data_logs', timeField: '@timestamp' }, module: 'sample_data_weblogs', @@ -32,6 +34,7 @@ export default ({ getService }: FtrProviderContext) => { prefix: 'pf1_', indexPatternName: 'kibana_sample_data_logs', startDatafeed: false, + estimateModelMemory: false, }, expected: { responseCode: 200, @@ -40,16 +43,55 @@ export default ({ getService }: FtrProviderContext) => { jobId: 'pf1_low_request_rate', jobState: JOB_STATE.CLOSED, datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '10mb', }, { jobId: 'pf1_response_code_rates', jobState: JOB_STATE.CLOSED, datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '10mb', }, { jobId: 'pf1_url_scanning', jobState: JOB_STATE.CLOSED, datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '10mb', + }, + ], + }, + }, + { + testTitleSuffix: + 'for sample logs dataset with prefix, startDatafeed false and estimateModelMemory true', + sourceDataArchive: 'ml/sample_logs', + indexPattern: { name: 'kibana_sample_data_logs', timeField: '@timestamp' }, + module: 'sample_data_weblogs', + user: USER.ML_POWERUSER, + requestBody: { + prefix: 'pf2_', + indexPatternName: 'kibana_sample_data_logs', + startDatafeed: false, + }, + expected: { + responseCode: 200, + jobs: [ + { + jobId: 'pf2_low_request_rate', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf2_response_code_rates', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '11mb', + }, + { + jobId: 'pf2_url_scanning', + jobState: JOB_STATE.CLOSED, + datafeedState: DATAFEED_STATE.STOPPED, + modelMemoryLimit: '16mb', }, ], }, @@ -197,6 +239,36 @@ export default ({ getService }: FtrProviderContext) => { await ml.api.waitForJobState(job.jobId, job.jobState); await ml.api.waitForDatafeedState(datafeedId, job.datafeedState); } + + // compare model memory limits for created jobs + const expectedModelMemoryLimits = testData.expected.jobs + .map(j => ({ + id: j.jobId, + modelMemoryLimit: j.modelMemoryLimit, + })) + .sort(compareById); + + const { + body: { jobs }, + }: { + body: { + jobs: Job[]; + }; + } = await ml.api.getAnomalyDetectionJob(testData.expected.jobs.map(j => j.jobId).join()); + + const actualModelMemoryLimits = jobs + .map(j => ({ + id: j.job_id, + modelMemoryLimit: j.analysis_limits!.model_memory_limit, + })) + .sort(compareById); + + expect(actualModelMemoryLimits).to.eql( + expectedModelMemoryLimits, + `Expected job model memory limits '${JSON.stringify( + expectedModelMemoryLimits + )}' (got '${JSON.stringify(actualModelMemoryLimits)}')` + ); }); // TODO in future updates: add creation validations for created saved objects diff --git a/x-pack/test/functional/apps/uptime/ml_anomaly.ts b/x-pack/test/functional/apps/uptime/ml_anomaly.ts index bcd165cc1afb7..c9668bed432b5 100644 --- a/x-pack/test/functional/apps/uptime/ml_anomaly.ts +++ b/x-pack/test/functional/apps/uptime/ml_anomaly.ts @@ -9,6 +9,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { const uptime = getService('uptime'); const log = getService('log'); + const esArchiver = getService('esArchiver'); + const archive = 'uptime/full_heartbeat'; describe('uptime ml anomaly', function() { this.tags(['skipFirefox']); @@ -17,6 +19,7 @@ export default ({ getService }: FtrProviderContext) => { const monitorId = '0000-intermittent'; before(async () => { + await esArchiver.loadIfNeeded(archive); if (!(await uptime.navigation.checkIfOnMonitorPage(monitorId))) { await uptime.navigation.loadDataAndGoToMonitorPage(dateStart, dateEnd, monitorId); } diff --git a/x-pack/test/functional/services/uptime/ml_anomaly.ts b/x-pack/test/functional/services/uptime/ml_anomaly.ts index e15f47ddd9709..a5f138b7a5716 100644 --- a/x-pack/test/functional/services/uptime/ml_anomaly.ts +++ b/x-pack/test/functional/services/uptime/ml_anomaly.ts @@ -32,7 +32,7 @@ export function UptimeMLAnomalyProvider({ getService }: FtrProviderContext) { async createMLJob() { await testSubjects.click('uptimeMLCreateJobBtn'); - return retry.tryForTime(10000, async () => { + return retry.tryForTime(30000, async () => { await testSubjects.existOrFail('uptimeMLJobSuccessfullyCreated'); log.info('Job successfully created'); }); From 284c9e3083802b96e1fe62d85a14e4e82434f6da Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 4 May 2020 16:49:50 -0400 Subject: [PATCH 06/55] Add 2 flaky retries to es snapshot jobs (#64553) (#65134) --- .ci/es-snapshots/Jenkinsfile_verify_es | 1 + vars/prChanges.groovy | 1 + 2 files changed, 2 insertions(+) diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es index 197726ff427ee..6ca0589624b78 100644 --- a/.ci/es-snapshots/Jenkinsfile_verify_es +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -21,6 +21,7 @@ def SNAPSHOT_MANIFEST = "https://storage.googleapis.com/kibana-ci-es-snapshots-d kibanaPipeline(timeoutMinutes: 135) { catchErrors { + retryable.enable(2) withEnv(["ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}"]) { parallel([ 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), diff --git a/vars/prChanges.groovy b/vars/prChanges.groovy index d7f46ee7be23e..4b9b20a945f76 100644 --- a/vars/prChanges.groovy +++ b/vars/prChanges.groovy @@ -7,6 +7,7 @@ def getSkippablePaths() { /^docs\//, /^rfcs\//, /^.ci\/.+\.yml$/, + /^.ci\/es-snapshots\//, /^\.github\//, /\.md$/, ] From d5f00b99eab3bc93038be8dffd39e9d07fb36b2a Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 4 May 2020 15:56:19 -0500 Subject: [PATCH 07/55] Service map anomaly indicators (#64718) (#65140) Get an aggregation of the anomaly scores and show style the ring around the node icon. --- .../apm/common/ml_job_constants.test.ts | 49 ++++++++++++- x-pack/plugins/apm/common/ml_job_constants.ts | 30 ++++++++ .../app/ServiceMap/Cytoscape.stories.tsx | 41 +++++++++++ .../app/ServiceMap/cytoscapeOptions.ts | 54 ++++++++++++-- .../server/lib/service_map/get_service_map.ts | 49 +++++++++++-- .../server/lib/service_map/ml_helpers.test.ts | 70 +++++++++++++++++++ .../apm/server/lib/service_map/ml_helpers.ts | 47 +++++++++++++ 7 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/apm/server/lib/service_map/ml_helpers.test.ts create mode 100644 x-pack/plugins/apm/server/lib/service_map/ml_helpers.ts diff --git a/x-pack/plugins/apm/common/ml_job_constants.test.ts b/x-pack/plugins/apm/common/ml_job_constants.test.ts index 2aa50a305f7c8..4941925939afb 100644 --- a/x-pack/plugins/apm/common/ml_job_constants.test.ts +++ b/x-pack/plugins/apm/common/ml_job_constants.test.ts @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getMlIndex, getMlJobId, getMlPrefix } from './ml_job_constants'; +import { + getMlIndex, + getMlJobId, + getMlPrefix, + getMlJobServiceName, + getSeverity, + severity +} from './ml_job_constants'; describe('ml_job_constants', () => { it('getMlPrefix', () => { @@ -38,4 +45,44 @@ describe('ml_job_constants', () => { '.ml-anomalies-myservicename-mytransactiontype-high_mean_response_time' ); }); + + describe('getMlJobServiceName', () => { + it('extracts the service name from a job id', () => { + expect( + getMlJobServiceName('opbeans-node-request-high_mean_response_time') + ).toEqual('opbeans-node'); + }); + }); + + describe('getSeverity', () => { + describe('when score is undefined', () => { + it('returns undefined', () => { + expect(getSeverity(undefined)).toEqual(undefined); + }); + }); + + describe('when score < 25', () => { + it('returns warning', () => { + expect(getSeverity(10)).toEqual(severity.warning); + }); + }); + + describe('when score is between 25 and 50', () => { + it('returns minor', () => { + expect(getSeverity(40)).toEqual(severity.minor); + }); + }); + + describe('when score is between 50 and 75', () => { + it('returns major', () => { + expect(getSeverity(60)).toEqual(severity.major); + }); + }); + + describe('when score is 75 or more', () => { + it('returns critical', () => { + expect(getSeverity(100)).toEqual(severity.critical); + }); + }); + }); }); diff --git a/x-pack/plugins/apm/common/ml_job_constants.ts b/x-pack/plugins/apm/common/ml_job_constants.ts index 01f5762e2dc4b..afe0550721716 100644 --- a/x-pack/plugins/apm/common/ml_job_constants.ts +++ b/x-pack/plugins/apm/common/ml_job_constants.ts @@ -4,6 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +export enum severity { + critical = 'critical', + major = 'major', + minor = 'minor', + warning = 'warning' +} + export function getMlPrefix(serviceName: string, transactionType?: string) { const maybeTransactionType = transactionType ? `${transactionType}-` : ''; return encodeForMlApi(`${serviceName}-${maybeTransactionType}`); @@ -13,6 +20,13 @@ export function getMlJobId(serviceName: string, transactionType?: string) { return `${getMlPrefix(serviceName, transactionType)}high_mean_response_time`; } +export function getMlJobServiceName(jobId: string) { + return jobId + .split('-') + .slice(0, -2) + .join('-'); +} + export function getMlIndex(serviceName: string, transactionType?: string) { return `.ml-anomalies-${getMlJobId(serviceName, transactionType)}`; } @@ -20,3 +34,19 @@ export function getMlIndex(serviceName: string, transactionType?: string) { export function encodeForMlApi(value: string) { return value.replace(/\s+/g, '_').toLowerCase(); } + +export function getSeverity(score?: number) { + if (typeof score !== 'number') { + return undefined; + } else if (score < 25) { + return severity.warning; + } else if (score >= 25 && score < 50) { + return severity.minor; + } else if (score >= 50 && score < 75) { + return severity.major; + } else if (score >= 75) { + return severity.critical; + } else { + return undefined; + } +} diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx index de775dbc8162a..340c299f52c0b 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx @@ -304,3 +304,44 @@ storiesOf('app/ServiceMap/Cytoscape', module) } ) .addParameters({ options: { showPanel: false } }); + +storiesOf('app/ServiceMap/Cytoscape', module).add( + 'node severity', + () => { + const elements = [ + { data: { id: 'undefined', 'service.name': 'severity: undefined' } }, + { + data: { + id: 'warning', + 'service.name': 'severity: warning', + severity: 'warning' + } + }, + { + data: { + id: 'minor', + 'service.name': 'severity: minor', + severity: 'minor' + } + }, + { + data: { + id: 'major', + 'service.name': 'severity: major', + severity: 'major' + } + }, + { + data: { + id: 'critical', + 'service.name': 'severity: critical', + severity: 'critical' + } + } + ]; + return ; + }, + { + info: { propTables: false, source: false } + } +); diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 554f84f0ad236..3bb4319d0722d 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -10,8 +10,52 @@ import { SERVICE_NAME, SPAN_DESTINATION_SERVICE_RESOURCE } from '../../../../common/elasticsearch_fieldnames'; +import { severity } from '../../../../common/ml_job_constants'; import { defaultIcon, iconForNode } from './icons'; +const getBorderColor = (el: cytoscape.NodeSingular) => { + const nodeSeverity = el.data('severity'); + + switch (nodeSeverity) { + case severity.warning: + return theme.euiColorVis0; + case severity.minor || severity.major: + return theme.euiColorVis5; + case severity.critical: + return theme.euiColorVis9; + default: + if (el.hasClass('primary') || el.selected()) { + return theme.euiColorPrimary; + } else { + return theme.euiColorMediumShade; + } + } +}; + +const getBorderStyle: cytoscape.Css.MapperFunction< + cytoscape.NodeSingular, + cytoscape.Css.LineStyle +> = (el: cytoscape.NodeSingular) => { + const nodeSeverity = el.data('severity'); + if (nodeSeverity === severity.critical) { + return 'double'; + } else { + return 'solid'; + } +}; + +const getBorderWidth = (el: cytoscape.NodeSingular) => { + const nodeSeverity = el.data('severity'); + + if (nodeSeverity === severity.minor || nodeSeverity === severity.major) { + return 4; + } else if (nodeSeverity === severity.critical) { + return 12; + } else { + return 2; + } +}; + // IE 11 does not properly load some SVGs or draw certain shapes. This causes // a runtime error and the map fails work at all. We would prefer to do some // kind of feature detection rather than browser detection, but some of these @@ -55,11 +99,9 @@ const style: cytoscape.Stylesheet[] = [ isService(el) ? '60%' : '40%', 'background-width': (el: cytoscape.NodeSingular) => isService(el) ? '60%' : '40%', - 'border-color': (el: cytoscape.NodeSingular) => - el.hasClass('primary') || el.selected() - ? theme.euiColorPrimary - : theme.euiColorMediumShade, - 'border-width': 2, + 'border-color': getBorderColor, + 'border-style': getBorderStyle, + 'border-width': getBorderWidth, color: (el: cytoscape.NodeSingular) => el.hasClass('primary') || el.selected() ? theme.euiColorPrimaryText @@ -149,7 +191,7 @@ const style: cytoscape.Stylesheet[] = [ { selector: 'node.hover', style: { - 'border-width': 2 + 'border-width': getBorderWidth } }, { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 17b595385a84e..adb2c9b7cb084 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -17,6 +17,8 @@ import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { dedupeConnections } from './dedupe_connections'; import { getServiceMapFromTraceIds } from './get_service_map_from_trace_ids'; import { getTraceSampleIds } from './get_trace_sample_ids'; +import { addAnomaliesToServicesData } from './ml_helpers'; +import { getMlIndex } from '../../../common/ml_job_constants'; export interface IEnvOptions { setup: Setup & SetupTimeRange; @@ -137,19 +139,58 @@ async function getServicesData(options: IEnvOptions) { ); } +function getAnomaliesData(options: IEnvOptions) { + const { client } = options.setup; + + const params = { + index: getMlIndex('*'), + body: { + size: 0, + query: { + exists: { + field: 'bucket_span' + } + }, + aggs: { + jobs: { + terms: { + field: 'job_id', + size: 10 + }, + aggs: { + max_score: { + max: { + field: 'anomaly_score' + } + } + } + } + } + } + }; + + return client.search(params); +} + +export type AnomaliesResponse = PromiseReturnType; export type ConnectionsResponse = PromiseReturnType; export type ServicesResponse = PromiseReturnType; - export type ServiceMapAPIResponse = PromiseReturnType; export async function getServiceMap(options: IEnvOptions) { - const [connectionData, servicesData] = await Promise.all([ + const [connectionData, servicesData, anomaliesData] = await Promise.all([ getConnectionData(options), - getServicesData(options) + getServicesData(options), + getAnomaliesData(options) ]); + const servicesDataWithAnomalies = addAnomaliesToServicesData( + servicesData, + anomaliesData + ); + return dedupeConnections({ ...connectionData, - services: servicesData + services: servicesDataWithAnomalies }); } diff --git a/x-pack/plugins/apm/server/lib/service_map/ml_helpers.test.ts b/x-pack/plugins/apm/server/lib/service_map/ml_helpers.test.ts new file mode 100644 index 0000000000000..c6680ecd6375b --- /dev/null +++ b/x-pack/plugins/apm/server/lib/service_map/ml_helpers.test.ts @@ -0,0 +1,70 @@ +/* + * 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 { AnomaliesResponse } from './get_service_map'; +import { addAnomaliesToServicesData } from './ml_helpers'; + +describe('addAnomaliesToServicesData', () => { + it('adds anomalies to services data', () => { + const servicesData = [ + { + 'service.name': 'opbeans-ruby', + 'agent.name': 'ruby', + 'service.environment': null, + 'service.framework.name': 'Ruby on Rails' + }, + { + 'service.name': 'opbeans-java', + 'agent.name': 'java', + 'service.environment': null, + 'service.framework.name': null + } + ]; + + const anomaliesResponse = { + aggregations: { + jobs: { + buckets: [ + { + key: 'opbeans-ruby-request-high_mean_response_time', + max_score: { value: 50 } + }, + { + key: 'opbeans-java-request-high_mean_response_time', + max_score: { value: 100 } + } + ] + } + } + }; + + const result = [ + { + 'service.name': 'opbeans-ruby', + 'agent.name': 'ruby', + 'service.environment': null, + 'service.framework.name': 'Ruby on Rails', + max_score: 50, + severity: 'major' + }, + { + 'service.name': 'opbeans-java', + 'agent.name': 'java', + 'service.environment': null, + 'service.framework.name': null, + max_score: 100, + severity: 'critical' + } + ]; + + expect( + addAnomaliesToServicesData( + servicesData, + (anomaliesResponse as unknown) as AnomaliesResponse + ) + ).toEqual(result); + }); +}); diff --git a/x-pack/plugins/apm/server/lib/service_map/ml_helpers.ts b/x-pack/plugins/apm/server/lib/service_map/ml_helpers.ts new file mode 100644 index 0000000000000..26a964bfb4dd2 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/service_map/ml_helpers.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; +import { + getMlJobServiceName, + getSeverity +} from '../../../common/ml_job_constants'; +import { AnomaliesResponse, ServicesResponse } from './get_service_map'; + +export function addAnomaliesToServicesData( + servicesData: ServicesResponse, + anomaliesResponse: AnomaliesResponse +) { + const anomaliesMap = ( + anomaliesResponse.aggregations?.jobs.buckets ?? [] + ).reduce<{ + [key: string]: { max_score?: number }; + }>((previousValue, currentValue) => { + const key = getMlJobServiceName(currentValue.key.toString()); + + return { + ...previousValue, + [key]: { + max_score: Math.max( + previousValue[key]?.max_score ?? 0, + currentValue.max_score.value ?? 0 + ) + } + }; + }, {}); + + const servicesDataWithAnomalies = servicesData.map(service => { + const score = anomaliesMap[service[SERVICE_NAME]]?.max_score; + + return { + ...service, + max_score: score, + severity: getSeverity(score) + }; + }); + + return servicesDataWithAnomalies; +} From 4ca7a2f41e0c98cb0bffa788fdad4b6989a7b3cd Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 4 May 2020 23:08:11 +0200 Subject: [PATCH 08/55] mock the whole ES client in the integration tests (#65081) (#65149) * mock the whole ES client to prevent real requests * mock the whole client Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../elasticsearch_service.mock.ts | 51 +++++++++++++++++++ .../core_service.test.mocks.ts | 11 ++++ .../integration_tests/core_services.test.ts | 8 +-- 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index da8846f6dddbb..a7d78b56ff3fd 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -18,6 +18,7 @@ */ import { BehaviorSubject } from 'rxjs'; +import { Client } from 'elasticsearch'; import { IClusterClient, ICustomClusterClient } from './cluster_client'; import { IScopedClusterClient } from './scoped_cluster_client'; import { ElasticsearchConfig } from './elasticsearch_config'; @@ -130,6 +131,55 @@ const createMock = () => { return mocked; }; +const createElasticsearchClientMock = () => { + const mocked: jest.Mocked = { + cat: {} as any, + cluster: {} as any, + indices: {} as any, + ingest: {} as any, + nodes: {} as any, + snapshot: {} as any, + tasks: {} as any, + bulk: jest.fn(), + clearScroll: jest.fn(), + count: jest.fn(), + create: jest.fn(), + delete: jest.fn(), + deleteByQuery: jest.fn(), + deleteScript: jest.fn(), + deleteTemplate: jest.fn(), + exists: jest.fn(), + explain: jest.fn(), + fieldStats: jest.fn(), + get: jest.fn(), + getScript: jest.fn(), + getSource: jest.fn(), + getTemplate: jest.fn(), + index: jest.fn(), + info: jest.fn(), + mget: jest.fn(), + msearch: jest.fn(), + msearchTemplate: jest.fn(), + mtermvectors: jest.fn(), + ping: jest.fn(), + putScript: jest.fn(), + putTemplate: jest.fn(), + reindex: jest.fn(), + reindexRethrottle: jest.fn(), + renderSearchTemplate: jest.fn(), + scroll: jest.fn(), + search: jest.fn(), + searchShards: jest.fn(), + searchTemplate: jest.fn(), + suggest: jest.fn(), + termvectors: jest.fn(), + update: jest.fn(), + updateByQuery: jest.fn(), + close: jest.fn(), + }; + return mocked; +}; + export const elasticsearchServiceMock = { create: createMock, createInternalSetup: createInternalSetupContractMock, @@ -138,4 +188,5 @@ export const elasticsearchServiceMock = { createClusterClient: createClusterClientMock, createCustomClusterClient: createCustomClusterClientMock, createScopedClusterClient: createScopedClusterClientMock, + createElasticsearchClient: createElasticsearchClientMock, }; diff --git a/src/core/server/http/integration_tests/core_service.test.mocks.ts b/src/core/server/http/integration_tests/core_service.test.mocks.ts index 6fa3357168027..b3adda8dd22d1 100644 --- a/src/core/server/http/integration_tests/core_service.test.mocks.ts +++ b/src/core/server/http/integration_tests/core_service.test.mocks.ts @@ -24,3 +24,14 @@ jest.doMock('../../elasticsearch/scoped_cluster_client', () => ({ return elasticsearchServiceMock.createScopedClusterClient(); }), })); + +jest.doMock('elasticsearch', () => { + const realES = jest.requireActual('elasticsearch'); + return { + ...realES, + // eslint-disable-next-line object-shorthand + Client: function() { + return elasticsearchServiceMock.createElasticsearchClient(); + }, + }; +}); diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 7b1630a7de0be..5726486a0930a 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -43,7 +43,7 @@ describe('http service', () => { describe('auth', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot(); }, 30000); afterEach(async () => { @@ -192,7 +192,7 @@ describe('http service', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot(); }, 30000); afterEach(async () => { @@ -326,7 +326,7 @@ describe('http service', () => { describe('#basePath()', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot(); }, 30000); afterEach(async () => await root.shutdown()); @@ -355,7 +355,7 @@ describe('http service', () => { describe('elasticsearch', () => { let root: ReturnType; beforeEach(async () => { - root = kbnTestServer.createRoot({ migrations: { skip: true } }); + root = kbnTestServer.createRoot(); }, 30000); afterEach(async () => { From e9690d9117cbb43ec0d6d000ccac882b3960ee37 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 4 May 2020 14:10:48 -0700 Subject: [PATCH 09/55] [Ingest] Agent config settings UI (#64854) (#64940) * Remove duplicate donut chart, move used donut chart closer to usage, clean up import paths * Change delete agent config to only single delete and delete all its associated data sources * Initial pass at settings tab * Reuse existing create agent config form instead * Fix delete api * Prevent nav drawer from hiding bottom bar (save action area) content * Remove delete config functionality from list page * Prevent API from deleting config with agents enrolled * Fix namespace populating in form * Display confirmation modal to deploy to agents if agents are detected * Adjust confirm delete copy * Fix i18n checks * Fix type check * Fix it again * De-dupe confirm modal * Fix i18n * Update agent config info after saving * Adjust skip unassign from agent config option schema in delete datasource method Co-authored-by: Elastic Machine --- .../common/types/rest_spec/agent_config.ts | 8 +- .../hooks/use_request/agent_config.ts | 8 +- .../applications/ingest_manager/index.scss | 14 + .../applications/ingest_manager/index.tsx | 1 + .../components/config_delete_provider.tsx | 172 +++---- .../agent_config/components/config_form.tsx | 449 ++++++++++-------- .../confirm_deploy_modal.tsx} | 16 +- .../sections/agent_config/components/index.ts | 1 + .../components/linked_agent_count.tsx | 4 +- .../components/index.ts | 1 - .../create_datasource_page/index.tsx | 28 +- .../details_page/components/config_form.tsx | 94 ---- .../details_page/components/donut_chart.tsx | 65 --- .../details_page/components/edit_config.tsx | 135 ------ .../details_page/components/index.ts | 2 - .../components/settings/index.tsx | 216 +++++++++ .../details_page/components/yaml/index.tsx | 2 +- .../agent_config/details_page/hooks/index.ts | 2 +- .../agent_config/details_page/index.tsx | 33 +- .../edit_datasource_page/index.tsx | 8 +- .../sections/fleet/agent_list_page/index.scss | 6 - .../components/donut_chart.tsx | 0 .../sections/fleet/components/list_layout.tsx | 6 +- .../ingest_manager/types/index.ts | 4 +- .../server/routes/agent_config/handlers.ts | 12 +- .../server/routes/agent_config/index.ts | 4 +- .../server/services/agent_config.ts | 57 ++- .../server/services/datasource.ts | 20 +- .../server/types/rest_spec/agent_config.ts | 4 +- .../translations/translations/ja-JP.json | 26 - .../translations/translations/zh-CN.json | 26 - 31 files changed, 680 insertions(+), 744 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss rename x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/{create_datasource_page/components/confirm_modal.tsx => components/confirm_deploy_modal.tsx} (76%) delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.scss rename x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/{agent_list_page => }/components/donut_chart.tsx (100%) diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts index 89d548d11dadb..82d7fa51b2082 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts @@ -49,16 +49,16 @@ export interface UpdateAgentConfigResponse { success: boolean; } -export interface DeleteAgentConfigsRequest { +export interface DeleteAgentConfigRequest { body: { - agentConfigIds: string[]; + agentConfigId: string; }; } -export type DeleteAgentConfigsResponse = Array<{ +export interface DeleteAgentConfigResponse { id: string; success: boolean; -}>; +} export interface GetFullAgentConfigRequest { params: { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts index bed3f994005ad..f80c468677f48 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts @@ -18,8 +18,8 @@ import { CreateAgentConfigResponse, UpdateAgentConfigRequest, UpdateAgentConfigResponse, - DeleteAgentConfigsRequest, - DeleteAgentConfigsResponse, + DeleteAgentConfigRequest, + DeleteAgentConfigResponse, } from '../../types'; export const useGetAgentConfigs = (query: HttpFetchQuery = {}) => { @@ -75,8 +75,8 @@ export const sendUpdateAgentConfig = ( }); }; -export const sendDeleteAgentConfigs = (body: DeleteAgentConfigsRequest['body']) => { - return sendRequest({ +export const sendDeleteAgentConfig = (body: DeleteAgentConfigRequest['body']) => { + return sendRequest({ path: agentConfigRouteService.getDeletePath(), method: 'post', body: JSON.stringify(body), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss new file mode 100644 index 0000000000000..fb95b1fa8bbfc --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss @@ -0,0 +1,14 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/nav_drawer/variables'; + +/** + * 1. Hack EUI so the bottom bar doesn't obscure the nav drawer flyout. + */ +.ingestManager__bottomBar { + z-index: 0; /* 1 */ + left: $euiNavDrawerWidthCollapsed; +} + +.ingestManager__bottomBar-isNavDrawerLocked { + left: $euiNavDrawerWidthExpanded; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 6485862830d8a..295a35693726f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -23,6 +23,7 @@ import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks'; import { PackageInstallProvider } from './sections/epm/hooks'; import { sendSetup } from './hooks/use_request/setup'; +import './index.scss'; export interface ProtectedRouteProps extends RouteProps { isAllowed?: boolean; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx index 9ae8369abbd52..d517dde45d5e3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx @@ -5,117 +5,92 @@ */ import React, { Fragment, useRef, useState } from 'react'; -import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { EuiConfirmModal, EuiOverlayMask, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; -import { sendDeleteAgentConfigs, useCore, sendRequest } from '../../../hooks'; +import { sendDeleteAgentConfig, useCore, useConfig, sendRequest } from '../../../hooks'; interface Props { - children: (deleteAgentConfigs: deleteAgentConfigs) => React.ReactElement; + children: (deleteAgentConfig: DeleteAgentConfig) => React.ReactElement; } -export type deleteAgentConfigs = (agentConfigs: string[], onSuccess?: OnSuccessCallback) => void; +export type DeleteAgentConfig = (agentConfig: string, onSuccess?: OnSuccessCallback) => void; -type OnSuccessCallback = (agentConfigsUnenrolled: string[]) => void; +type OnSuccessCallback = (agentConfigDeleted: string) => void; export const AgentConfigDeleteProvider: React.FunctionComponent = ({ children }) => { const { notifications } = useCore(); - const [agentConfigs, setAgentConfigs] = useState([]); + const { + fleet: { enabled: isFleetEnabled }, + } = useConfig(); + const [agentConfig, setAgentConfig] = useState(); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); const [agentsCount, setAgentsCount] = useState(0); const [isLoading, setIsLoading] = useState(false); const onSuccessCallback = useRef(null); - const deleteAgentConfigsPrompt: deleteAgentConfigs = ( - agentConfigsToDelete, + const deleteAgentConfigPrompt: DeleteAgentConfig = ( + agentConfigToDelete, onSuccess = () => undefined ) => { - if ( - agentConfigsToDelete === undefined || - (Array.isArray(agentConfigsToDelete) && agentConfigsToDelete.length === 0) - ) { - throw new Error('No agent configs specified for deletion'); + if (!agentConfigToDelete) { + throw new Error('No agent config specified for deletion'); } setIsModalOpen(true); - setAgentConfigs(agentConfigsToDelete); - fetchAgentsCount(agentConfigsToDelete); + setAgentConfig(agentConfigToDelete); + fetchAgentsCount(agentConfigToDelete); onSuccessCallback.current = onSuccess; }; const closeModal = () => { - setAgentConfigs([]); + setAgentConfig(undefined); setIsLoading(false); setIsLoadingAgentsCount(false); setIsModalOpen(false); }; - const deleteAgentConfigs = async () => { + const deleteAgentConfig = async () => { setIsLoading(true); try { - const { data } = await sendDeleteAgentConfigs({ - agentConfigIds: agentConfigs, + const { data } = await sendDeleteAgentConfig({ + agentConfigId: agentConfig!, }); - const successfulResults = data?.filter(result => result.success) || []; - const failedResults = data?.filter(result => !result.success) || []; - if (successfulResults.length) { - const hasMultipleSuccesses = successfulResults.length > 1; - const successMessage = hasMultipleSuccesses - ? i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle', - { - defaultMessage: 'Deleted {count} agent configs', - values: { count: successfulResults.length }, - } - ) - : i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle', - { - defaultMessage: "Deleted agent config '{id}'", - values: { id: successfulResults[0].id }, - } - ); - notifications.toasts.addSuccess(successMessage); + if (data?.success) { + notifications.toasts.addSuccess( + i18n.translate('xpack.ingestManager.deleteAgentConfig.successSingleNotificationTitle', { + defaultMessage: "Deleted agent config '{id}'", + values: { id: agentConfig }, + }) + ); + if (onSuccessCallback.current) { + onSuccessCallback.current(agentConfig!); + } } - if (failedResults.length) { - const hasMultipleFailures = failedResults.length > 1; - const failureMessage = hasMultipleFailures - ? i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle', - { - defaultMessage: 'Error deleting {count} agent configs', - values: { count: failedResults.length }, - } - ) - : i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle', - { - defaultMessage: "Error deleting agent config '{id}'", - values: { id: failedResults[0].id }, - } - ); - notifications.toasts.addDanger(failureMessage); - } - - if (onSuccessCallback.current) { - onSuccessCallback.current(successfulResults.map(result => result.id)); + if (!data?.success) { + notifications.toasts.addDanger( + i18n.translate('xpack.ingestManager.deleteAgentConfig.failureSingleNotificationTitle', { + defaultMessage: "Error deleting agent config '{id}'", + values: { id: agentConfig }, + }) + ); } } catch (e) { notifications.toasts.addDanger( - i18n.translate('xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle', { - defaultMessage: 'Error deleting agent configs', + i18n.translate('xpack.ingestManager.deleteAgentConfig.fatalErrorNotificationTitle', { + defaultMessage: 'Error deleting agent config', }) ); } closeModal(); }; - const fetchAgentsCount = async (agentConfigsToCheck: string[]) => { - if (isLoadingAgentsCount) { + const fetchAgentsCount = async (agentConfigToCheck: string) => { + if (!isFleetEnabled || isLoadingAgentsCount) { return; } setIsLoadingAgentsCount(true); @@ -123,7 +98,7 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil path: `/api/ingest_manager/fleet/agents`, method: 'get', query: { - kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : (${agentConfigsToCheck.join(' or ')})`, + kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : ${agentConfigToCheck}`, }, }); setAgentsCount(data?.total || 0); @@ -140,68 +115,61 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil } onCancel={closeModal} - onConfirm={deleteAgentConfigs} + onConfirm={deleteAgentConfig} cancelButtonText={ } confirmButtonText={ isLoading || isLoadingAgentsCount ? ( - ) : agentsCount ? ( - ) : ( ) } buttonColor="danger" - confirmButtonDisabled={isLoading || isLoadingAgentsCount} + confirmButtonDisabled={isLoading || isLoadingAgentsCount || !!agentsCount} > {isLoadingAgentsCount ? ( ) : agentsCount ? ( - + + + ) : ( )} @@ -211,7 +179,7 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil return ( - {children(deleteAgentConfigsPrompt)} + {children(deleteAgentConfigPrompt)} {renderModal()} ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx index 92c44d86e47c6..c55d6009074b0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx @@ -8,8 +8,7 @@ import React, { useMemo, useState } from 'react'; import { EuiAccordion, EuiFieldText, - EuiFlexGroup, - EuiFlexItem, + EuiDescribedFormGroup, EuiForm, EuiFormRow, EuiHorizontalRule, @@ -19,11 +18,13 @@ import { EuiComboBox, EuiIconTip, EuiCheckboxGroup, + EuiButton, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { NewAgentConfig } from '../../../types'; +import { NewAgentConfig, AgentConfig } from '../../../types'; +import { AgentConfigDeleteProvider } from './config_delete_provider'; interface ValidationResults { [key: string]: JSX.Element[]; @@ -36,7 +37,7 @@ const StyledEuiAccordion = styled(EuiAccordion)` `; export const agentConfigFormValidation = ( - agentConfig: Partial + agentConfig: Partial ): ValidationResults => { const errors: ValidationResults = {}; @@ -53,11 +54,13 @@ export const agentConfigFormValidation = ( }; interface Props { - agentConfig: Partial; - updateAgentConfig: (u: Partial) => void; + agentConfig: Partial; + updateAgentConfig: (u: Partial) => void; withSysMonitoring: boolean; updateSysMonitoring: (newValue: boolean) => void; validation: ValidationResults; + isEditing?: boolean; + onDelete?: () => void; } export const AgentConfigForm: React.FunctionComponent = ({ @@ -66,9 +69,11 @@ export const AgentConfigForm: React.FunctionComponent = ({ withSysMonitoring, updateSysMonitoring, validation, + isEditing = false, + onDelete = () => {}, }) => { const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); - const [showNamespace, setShowNamespace] = useState(false); + const [showNamespace, setShowNamespace] = useState(!!agentConfig.namespace); const fields: Array<{ name: 'name' | 'description' | 'namespace'; label: JSX.Element; @@ -105,209 +110,281 @@ export const AgentConfigForm: React.FunctionComponent = ({ ]; }, []); - return ( - - {fields.map(({ name, label, placeholder }) => { - return ( - - updateAgentConfig({ [name]: e.target.value })} - isInvalid={Boolean(touchedFields[name] && validation[name])} - onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} - placeholder={placeholder} - /> - - ); - })} + const generalSettingsWrapper = (children: JSX.Element[]) => ( + + + + } + description={ + + } + > + {children} + + ); + + const generalFields = fields.map(({ name, label, placeholder }) => { + return ( + fullWidth + key={name} + label={label} + error={touchedFields[name] && validation[name] ? validation[name] : null} + isInvalid={Boolean(touchedFields[name] && validation[name])} + > + updateAgentConfig({ [name]: e.target.value })} + isInvalid={Boolean(touchedFields[name] && validation[name])} + onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} + placeholder={placeholder} + /> + + ); + }); + + const advancedOptionsContent = ( + <> + - + + } + description={ + } > - {' '} - - + } - checked={withSysMonitoring} + checked={showNamespace} onChange={() => { - updateSysMonitoring(!withSysMonitoring); + setShowNamespace(!showNamespace); + if (showNamespace) { + updateAgentConfig({ namespace: '' }); + } }} /> - - - - + + + { + updateAgentConfig({ namespace: value }); + }} + onChange={selectedOptions => { + updateAgentConfig({ + namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, + }); + }} + isInvalid={Boolean(touchedFields.namespace && validation.namespace)} + onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} + /> + + + )} + + + + + } + description={ } - buttonClassName="ingest-active-button" > - - - - -

- -

-
- - + { + acc[key] = true; + return acc; + }, + { logs: false, metrics: false } + )} + onChange={id => { + if (id !== 'logs' && id !== 'metrics') { + return; + } + + const hasLogs = + agentConfig.monitoring_enabled && agentConfig.monitoring_enabled.indexOf(id) >= 0; + + const previousValues = agentConfig.monitoring_enabled || []; + updateAgentConfig({ + monitoring_enabled: hasLogs + ? previousValues.filter(type => type !== id) + : [...previousValues, id], + }); + }} + /> +
+ {isEditing && 'id' in agentConfig ? ( + + + + } + description={ + <> + + + + {deleteAgentConfigPrompt => { + return ( + deleteAgentConfigPrompt(agentConfig.id!, onDelete)} + > + + + ); + }} + + {agentConfig.is_default ? ( + <> + + + + + + ) : null} + + } + /> + ) : null} + + ); + + return ( + + {!isEditing ? generalFields : generalSettingsWrapper(generalFields)} + {!isEditing ? ( + - - - - } - checked={showNamespace} - onChange={() => { - setShowNamespace(!showNamespace); - if (showNamespace) { - updateAgentConfig({ namespace: '' }); - } - }} - /> - {showNamespace && ( + } + > + - - - { - updateAgentConfig({ namespace: value }); - }} - onChange={selectedOptions => { - updateAgentConfig({ - namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, - }); - }} - isInvalid={Boolean(touchedFields.namespace && validation.namespace)} - onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} - /> - - - )} - - - - - - -

{' '} + -

-
- - + + } + checked={withSysMonitoring} + onChange={() => { + updateSysMonitoring(!withSysMonitoring); + }} + /> +
+ ) : null} + {!isEditing ? ( + <> + + + - - - - { - acc[key] = true; - return acc; - }, - { logs: false, metrics: false } - )} - onChange={id => { - if (id !== 'logs' && id !== 'metrics') { - return; - } - - const hasLogs = - agentConfig.monitoring_enabled && agentConfig.monitoring_enabled.indexOf(id) >= 0; - - const previousValues = agentConfig.monitoring_enabled || []; - updateAgentConfig({ - monitoring_enabled: hasLogs - ? previousValues.filter(type => type !== id) - : [...previousValues, id], - }); - }} - /> - - - + } + buttonClassName="ingest-active-button" + > + + {advancedOptionsContent} + + + ) : ( + advancedOptionsContent + )}
); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx similarity index 76% rename from x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx rename to x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx index aa7eab8f5be8d..a503beeffa8b4 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { EuiCallOut, EuiOverlayMask, EuiConfirmModal, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { AgentConfig } from '../../../../types'; +import { AgentConfig } from '../../../types'; -export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ +export const ConfirmDeployConfigModal: React.FunctionComponent<{ onConfirm: () => void; onCancel: () => void; agentCount: number; @@ -21,7 +21,7 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ } @@ -29,13 +29,13 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ onConfirm={onConfirm} cancelButtonText={ } confirmButtonText={ } @@ -43,7 +43,7 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ > diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts index a0fdc656dd7ed..c1811b99588a8 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts @@ -7,3 +7,4 @@ export { AgentConfigForm, agentConfigFormValidation } from './config_form'; export { AgentConfigDeleteProvider } from './config_delete_provider'; export { LinkedAgentCount } from './linked_agent_count'; +export { ConfirmDeployConfigModal } from './confirm_deploy_modal'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx index ec66108c60f68..3860439f26d44 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx @@ -8,7 +8,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiLink } from '@elastic/eui'; import { useLink } from '../../../hooks'; -import { FLEET_AGENTS_PATH } from '../../../constants'; +import { FLEET_AGENTS_PATH, AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; export const LinkedAgentCount = memo<{ count: number; agentConfigId: string }>( ({ count, agentConfigId }) => { @@ -21,7 +21,7 @@ export const LinkedAgentCount = memo<{ count: number; agentConfigId: string }>( /> ); return count > 0 ? ( - + {displayValue} ) : ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts index aa564690a6092..3bfca75668911 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts @@ -5,5 +5,4 @@ */ export { CreateDatasourcePageLayout } from './layout'; export { DatasourceInputPanel } from './datasource_input_panel'; -export { ConfirmCreateDatasourceModal } from './confirm_modal'; export { DatasourceInputVarField } from './datasource_input_var_field'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx index 8e7042c1275ad..5b7553dd8cf92 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx @@ -27,7 +27,8 @@ import { sendGetAgentStatus, } from '../../../hooks'; import { useLinks as useEPMLinks } from '../../epm/hooks'; -import { CreateDatasourcePageLayout, ConfirmCreateDatasourceModal } from './components'; +import { ConfirmDeployConfigModal } from '../components'; +import { CreateDatasourcePageLayout } from './components'; import { CreateDatasourceFrom, DatasourceFormState } from './types'; import { DatasourceValidationResults, validateDatasource, validationHasErrors } from './services'; import { StepSelectPackage } from './step_select_package'; @@ -36,7 +37,10 @@ import { StepConfigureDatasource } from './step_configure_datasource'; import { StepDefineDatasource } from './step_define_datasource'; export const CreateDatasourcePage: React.FunctionComponent = () => { - const { notifications } = useCore(); + const { + notifications, + chrome: { getIsNavDrawerLocked$ }, + } = useCore(); const { fleet: { enabled: isFleetEnabled }, } = useConfig(); @@ -45,6 +49,15 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } = useRouteMatch(); const history = useHistory(); const from: CreateDatasourceFrom = configId ? 'config' : 'package'; + const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); + + useEffect(() => { + const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => { + setIsNavDrawerLocked(newIsNavDrawerLocked); + }); + + return () => subscription.unsubscribe(); + }); // Agent config and package info states const [agentConfig, setAgentConfig] = useState(); @@ -269,7 +282,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { return ( {formState === 'CONFIRM' && agentConfig && ( - { )} - + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx deleted file mode 100644 index c4f8d944ceb14..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx +++ /dev/null @@ -1,94 +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, { useState } from 'react'; -import { EuiFieldText, EuiForm, EuiFormRow } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { AgentConfig } from '../../../../types'; - -interface ValidationResults { - [key: string]: JSX.Element[]; -} - -export const configFormValidation = (config: Partial): ValidationResults => { - const errors: ValidationResults = {}; - - if (!config.name?.trim()) { - errors.name = [ - , - ]; - } - - return errors; -}; - -interface Props { - config: Partial; - updateConfig: (u: Partial) => void; - validation: ValidationResults; -} - -export const ConfigForm: React.FunctionComponent = ({ - config, - updateConfig, - validation, -}) => { - const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); - const fields: Array<{ name: 'name' | 'description' | 'namespace'; label: JSX.Element }> = [ - { - name: 'name', - label: ( - - ), - }, - { - name: 'description', - label: ( - - ), - }, - { - name: 'namespace', - label: ( - - ), - }, - ]; - - return ( - - {fields.map(({ name, label }) => { - return ( - - updateConfig({ [name]: e.target.value })} - isInvalid={Boolean(touchedFields[name] && validation[name])} - onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} - /> - - ); - })} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx deleted file mode 100644 index 408ccc6e951f6..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx +++ /dev/null @@ -1,65 +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, { useEffect, useRef } from 'react'; -import d3 from 'd3'; -import { EuiFlexItem } from '@elastic/eui'; - -interface DonutChartProps { - data: { - [key: string]: number; - }; - height: number; - width: number; -} - -export const DonutChart = ({ height, width, data }: DonutChartProps) => { - const chartElement = useRef(null); - - useEffect(() => { - if (chartElement.current !== null) { - // we must remove any existing paths before painting - d3.selectAll('g').remove(); - const svgElement = d3 - .select(chartElement.current) - .append('g') - .attr('transform', `translate(${width / 2}, ${height / 2})`); - const color = d3.scale - .ordinal() - // @ts-ignore - .domain(data) - .range(['#017D73', '#98A2B3', '#BD271E']); - const pieGenerator = d3.layout - .pie() - .value(({ value }: any) => value) - // these start/end angles will reverse the direction of the pie, - // which matches our design - .startAngle(2 * Math.PI) - .endAngle(0); - - svgElement - .selectAll('g') - // @ts-ignore - .data(pieGenerator(d3.entries(data))) - .enter() - .append('path') - .attr( - 'd', - // @ts-ignore attr does not expect a param of type Arc but it behaves as desired - d3.svg - .arc() - .innerRadius(width * 0.28) - .outerRadius(Math.min(width, height) / 2 - 10) - ) - .attr('fill', (d: any) => color(d.data.key)); - } - }, [data, height, width]); - return ( - - - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx deleted file mode 100644 index 65eb86d7d871f..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx +++ /dev/null @@ -1,135 +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, { useState } from 'react'; -import { - EuiFlyout, - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, - EuiButton, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useCore, sendRequest } from '../../../../hooks'; -import { agentConfigRouteService } from '../../../../services'; -import { AgentConfig } from '../../../../types'; -import { ConfigForm, configFormValidation } from './config_form'; - -interface Props { - agentConfig: AgentConfig; - onClose: () => void; -} - -export const EditConfigFlyout: React.FunctionComponent = ({ - agentConfig: originalAgentConfig, - onClose, -}) => { - const { notifications } = useCore(); - const [config, setConfig] = useState>({ - name: originalAgentConfig.name, - description: originalAgentConfig.description, - }); - const [isLoading, setIsLoading] = useState(false); - const updateConfig = (updatedFields: Partial) => { - setConfig({ - ...config, - ...updatedFields, - }); - }; - const validation = configFormValidation(config); - - const header = ( - - -

- -

-
-
- ); - - const body = ( - - - - ); - - const footer = ( - - - - - - - - - 0} - onClick={async () => { - setIsLoading(true); - try { - const { error } = await sendRequest({ - path: agentConfigRouteService.getUpdatePath(originalAgentConfig.id), - method: 'put', - body: JSON.stringify(config), - }); - if (!error) { - notifications.toasts.addSuccess( - i18n.translate('xpack.ingestManager.editConfig.successNotificationTitle', { - defaultMessage: "Agent config '{name}' updated", - values: { name: config.name }, - }) - ); - } else { - notifications.toasts.addDanger( - error - ? error.message - : i18n.translate('xpack.ingestManager.editConfig.errorNotificationTitle', { - defaultMessage: 'Unable to update agent config', - }) - ); - } - } catch (e) { - notifications.toasts.addDanger( - i18n.translate('xpack.ingestManager.editConfig.errorNotificationTitle', { - defaultMessage: 'Unable to update agent config', - }) - ); - } - setIsLoading(false); - onClose(); - }} - > - - - - - - ); - - return ( - - {header} - {body} - {footer} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts index 918b361a60d79..0123bd46c16e7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts @@ -4,5 +4,3 @@ * you may not use this file except in compliance with the Elastic License. */ export { DatasourcesTable } from './datasources/datasources_table'; -export { DonutChart } from './donut_chart'; -export { EditConfigFlyout } from './edit_config'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx new file mode 100644 index 0000000000000..2d9d29bfc1ac7 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx @@ -0,0 +1,216 @@ +/* + * 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, useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; +import { EuiBottomBar, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { AGENT_CONFIG_PATH } from '../../../../../constants'; +import { AgentConfig } from '../../../../../types'; +import { + useCore, + useCapabilities, + sendUpdateAgentConfig, + useConfig, + sendGetAgentStatus, +} from '../../../../../hooks'; +import { + AgentConfigForm, + agentConfigFormValidation, + ConfirmDeployConfigModal, +} from '../../../components'; +import { useConfigRefresh } from '../../hooks'; + +const FormWrapper = styled.div` + max-width: 800px; + margin-right: auto; + margin-left: auto; +`; + +export const ConfigSettingsView = memo<{ config: AgentConfig }>( + ({ config: originalAgentConfig }) => { + const { + notifications, + chrome: { getIsNavDrawerLocked$ }, + } = useCore(); + const { + fleet: { enabled: isFleetEnabled }, + } = useConfig(); + const history = useHistory(); + const hasWriteCapabilites = useCapabilities().write; + const refreshConfig = useConfigRefresh(); + const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); + const [agentConfig, setAgentConfig] = useState({ + ...originalAgentConfig, + }); + const [isLoading, setIsLoading] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + const [agentCount, setAgentCount] = useState(0); + const [withSysMonitoring, setWithSysMonitoring] = useState(true); + const validation = agentConfigFormValidation(agentConfig); + + useEffect(() => { + const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => { + setIsNavDrawerLocked(newIsNavDrawerLocked); + }); + + return () => subscription.unsubscribe(); + }); + + const updateAgentConfig = (updatedFields: Partial) => { + setAgentConfig({ + ...agentConfig, + ...updatedFields, + }); + setHasChanges(true); + }; + + const submitUpdateAgentConfig = async () => { + setIsLoading(true); + try { + const { name, description, namespace, monitoring_enabled } = agentConfig; + const { data, error } = await sendUpdateAgentConfig(agentConfig.id, { + name, + description, + namespace, + monitoring_enabled, + }); + if (data?.success) { + notifications.toasts.addSuccess( + i18n.translate('xpack.ingestManager.editAgentConfig.successNotificationTitle', { + defaultMessage: "Successfully updated '{name}' settings", + values: { name: agentConfig.name }, + }) + ); + refreshConfig(); + setHasChanges(false); + } else { + notifications.toasts.addDanger( + error + ? error.message + : i18n.translate('xpack.ingestManager.editAgentConfig.errorNotificationTitle', { + defaultMessage: 'Unable to update agent config', + }) + ); + } + } catch (e) { + notifications.toasts.addDanger( + i18n.translate('xpack.ingestManager.editAgentConfig.errorNotificationTitle', { + defaultMessage: 'Unable to update agent config', + }) + ); + } + setIsLoading(false); + }; + + const onSubmit = async () => { + // Retrieve agent count if fleet is enabled + if (isFleetEnabled) { + setIsLoading(true); + const { data } = await sendGetAgentStatus({ configId: agentConfig.id }); + if (data?.results.total) { + setAgentCount(data.results.total); + } else { + await submitUpdateAgentConfig(); + } + } else { + await submitUpdateAgentConfig(); + } + }; + + return ( + + {agentCount ? ( + { + setAgentCount(0); + submitUpdateAgentConfig(); + }} + onCancel={() => { + setAgentCount(0); + setIsLoading(false); + }} + /> + ) : null} + setWithSysMonitoring(newValue)} + validation={validation} + isEditing={true} + onDelete={() => { + history.push(AGENT_CONFIG_PATH); + }} + /> + {hasChanges ? ( + + + + + + + + + { + setAgentConfig({ ...originalAgentConfig }); + setHasChanges(false); + }} + > + + + + + 0 + } + iconType="save" + color="primary" + fill + > + {isLoading ? ( + + ) : ( + + )} + + + + + + + ) : null} + + ); + } +); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx index f1d7bd5dbc039..9f2088521ed38 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx @@ -15,7 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { AgentConfig } from '../../../../../../../../common/types/models'; +import { AgentConfig } from '../../../../../types'; import { useGetOneAgentConfigFull, useGetEnrollmentAPIKeys, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts index 19be93676a734..76c6d64eb9e07 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ export { useGetAgentStatus, AgentStatusRefreshContext } from './use_agent_status'; -export { ConfigRefreshContext } from './use_config'; +export { ConfigRefreshContext, useConfigRefresh } from './use_config'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx index 450f86df5c03a..20a39724ce23c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx @@ -3,7 +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 React, { Fragment, memo, useCallback, useMemo, useState } from 'react'; +import React, { Fragment, memo, useMemo, useState } from 'react'; import { Redirect, useRouteMatch, Switch, Route } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; @@ -26,12 +26,12 @@ import { useGetOneAgentConfig } from '../../../hooks'; import { Loading } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; import { ConfigRefreshContext, useGetAgentStatus, AgentStatusRefreshContext } from './hooks'; -import { EditConfigFlyout } from './components'; import { LinkedAgentCount } from '../components'; import { useAgentConfigLink } from './hooks/use_details_uri'; import { DETAILS_ROUTER_PATH, DETAILS_ROUTER_SUB_PATH } from './constants'; import { ConfigDatasourcesView } from './components/datasources'; import { ConfigYamlView } from './components/yaml'; +import { ConfigSettingsView } from './components/settings'; const Divider = styled.div` width: 0; @@ -70,14 +70,6 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { const configDetailsYamlLink = useAgentConfigLink('details-yaml', { configId }); const configDetailsSettingsLink = useAgentConfigLink('details-settings', { configId }); - // Flyout states - const [isEditConfigFlyoutOpen, setIsEditConfigFlyoutOpen] = useState(false); - - const refreshData = useCallback(() => { - refreshAgentConfig(); - refreshAgentStatus(); - }, [refreshAgentConfig, refreshAgentStatus]); - const headerLeftContent = useMemo( () => ( @@ -196,7 +188,7 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { return [ { id: 'datasources', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.datasouces', { + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.datasourcesTabText', { defaultMessage: 'Data sources', }), href: configDetailsLink, @@ -204,15 +196,15 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { }, { id: 'yaml', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.yamlFile', { - defaultMessage: 'YAML File', + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.yamlTabText', { + defaultMessage: 'YAML', }), href: configDetailsYamlLink, isSelected: tabId === 'yaml', }, { id: 'settings', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.settings', { + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.settingsTabText', { defaultMessage: 'Settings', }), href: configDetailsSettingsLink, @@ -269,16 +261,6 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { rightColumn={headerRightContent} tabs={(headerTabs as unknown) as EuiTabProps[]} > - {isEditConfigFlyoutOpen ? ( - { - setIsEditConfigFlyoutOpen(false); - refreshData(); - }} - agentConfig={agentConfig} - /> - ) : null} - { { - // TODO: Settings implementation tracked via: https://github.com/elastic/kibana/issues/57959 - return
Settings placeholder
; + return ; }} /> { ) : ( <> {formState === 'CONFIRM' && ( - listAgents(soClient, { - showInactive: true, + showInactive: false, perPage: 0, page: 1, kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id:${agentConfig.id}`, @@ -179,13 +179,13 @@ export const updateAgentConfigHandler: RequestHandler< export const deleteAgentConfigsHandler: RequestHandler< unknown, unknown, - TypeOf + TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; try { - const body: DeleteAgentConfigsResponse = await agentConfigService.delete( + const body: DeleteAgentConfigResponse = await agentConfigService.delete( soClient, - request.body.agentConfigIds + request.body.agentConfigId ); return response.ok({ body, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts index b8e827974ff81..e630f3c959590 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts @@ -10,7 +10,7 @@ import { GetOneAgentConfigRequestSchema, CreateAgentConfigRequestSchema, UpdateAgentConfigRequestSchema, - DeleteAgentConfigsRequestSchema, + DeleteAgentConfigRequestSchema, GetFullAgentConfigRequestSchema, } from '../../types'; import { @@ -67,7 +67,7 @@ export const registerRoutes = (router: IRouter) => { router.post( { path: AGENT_CONFIG_API_ROUTES.DELETE_PATTERN, - validate: DeleteAgentConfigsRequestSchema, + validate: DeleteAgentConfigRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, deleteAgentConfigsHandler diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index 7ab6ef1920c18..5ecbaff8ad71e 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -6,7 +6,11 @@ import { uniq } from 'lodash'; import { SavedObjectsClientContract } from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; -import { DEFAULT_AGENT_CONFIG, AGENT_CONFIG_SAVED_OBJECT_TYPE } from '../constants'; +import { + DEFAULT_AGENT_CONFIG, + AGENT_CONFIG_SAVED_OBJECT_TYPE, + AGENT_SAVED_OBJECT_TYPE, +} from '../constants'; import { Datasource, NewAgentConfig, @@ -15,7 +19,8 @@ import { AgentConfigStatus, ListWithKuery, } from '../types'; -import { DeleteAgentConfigsResponse, storedDatasourceToAgentDatasource } from '../../common'; +import { DeleteAgentConfigResponse, storedDatasourceToAgentDatasource } from '../../common'; +import { listAgents } from './agents'; import { datasourceService } from './datasource'; import { outputService } from './output'; import { agentConfigUpdateEventHandler } from './agent_config_update'; @@ -256,32 +261,40 @@ class AgentConfigService { public async delete( soClient: SavedObjectsClientContract, - ids: string[] - ): Promise { - const result: DeleteAgentConfigsResponse = []; - const defaultConfigId = await this.getDefaultAgentConfigId(soClient); + id: string + ): Promise { + const config = await this.get(soClient, id, false); + if (!config) { + throw new Error('Agent configuration not found'); + } - if (ids.includes(defaultConfigId)) { + const defaultConfigId = await this.getDefaultAgentConfigId(soClient); + if (id === defaultConfigId) { throw new Error('The default agent configuration cannot be deleted'); } - for (const id of ids) { - try { - await soClient.delete(SAVED_OBJECT_TYPE, id); - await this.triggerAgentConfigUpdatedEvent(soClient, 'deleted', id); - result.push({ - id, - success: true, - }); - } catch (e) { - result.push({ - id, - success: false, - }); - } + const { total } = await listAgents(soClient, { + showInactive: false, + perPage: 0, + page: 1, + kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id:${id}`, + }); + + if (total > 0) { + throw new Error('Cannot delete agent config that is assigned to agent(s)'); } - return result; + if (config.datasources && config.datasources.length) { + await datasourceService.delete(soClient, config.datasources as string[], { + skipUnassignFromAgentConfigs: true, + }); + } + await soClient.delete(SAVED_OBJECT_TYPE, id); + await this.triggerAgentConfigUpdatedEvent(soClient, 'deleted', id); + return { + id, + success: true, + }; } public async getFullConfig( diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index 0a5ba43e40fba..affd9b2755881 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -145,7 +145,7 @@ class DatasourceService { public async delete( soClient: SavedObjectsClientContract, ids: string[], - options?: { user?: AuthenticatedUser } + options?: { user?: AuthenticatedUser; skipUnassignFromAgentConfigs?: boolean } ): Promise { const result: DeleteDatasourcesResponse = []; @@ -155,14 +155,16 @@ class DatasourceService { if (!oldDatasource) { throw new Error('Datasource not found'); } - await agentConfigService.unassignDatasources( - soClient, - oldDatasource.config_id, - [oldDatasource.id], - { - user: options?.user, - } - ); + if (!options?.skipUnassignFromAgentConfigs) { + await agentConfigService.unassignDatasources( + soClient, + oldDatasource.config_id, + [oldDatasource.id], + { + user: options?.user, + } + ); + } await soClient.delete(SAVED_OBJECT_TYPE, id); result.push({ id, diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts index 0d223f028fc88..ab97ddc0ba723 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts @@ -29,9 +29,9 @@ export const UpdateAgentConfigRequestSchema = { body: NewAgentConfigSchema, }; -export const DeleteAgentConfigsRequestSchema = { +export const DeleteAgentConfigRequestSchema = { body: schema.object({ - agentConfigIds: schema.arrayOf(schema.string()), + agentConfigId: schema.string(), }), }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6de70fda6aa2e..d491beaa2a689 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8299,9 +8299,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "名前空間", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "パッケージ", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "ストリーム", - "xpack.ingestManager.configDetails.subTabs.datasouces": "データソース", - "xpack.ingestManager.configDetails.subTabs.settings": "設定", - "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML ファイル", "xpack.ingestManager.configDetails.summary.datasources": "データソース", "xpack.ingestManager.configDetails.summary.lastUpdated": "最終更新日:", "xpack.ingestManager.configDetails.summary.revision": "リビジョン", @@ -8311,10 +8308,6 @@ "xpack.ingestManager.configDetailsDatasources.createFirstButtonText": "データソースを作成", "xpack.ingestManager.configDetailsDatasources.createFirstMessage": "この構成にはデータソースはまだありません。", "xpack.ingestManager.configDetailsDatasources.createFirstTitle": "初めてのデーソースを作成する", - "xpack.ingestManager.configForm.descriptionFieldLabel": "説明", - "xpack.ingestManager.configForm.nameFieldLabel": "名前", - "xpack.ingestManager.configForm.nameRequiredErrorMessage": "構成名が必要です", - "xpack.ingestManager.configForm.namespaceFieldLabel": "名前空間", "xpack.ingestManager.createAgentConfig.cancelButtonLabel": "キャンセル", "xpack.ingestManager.createAgentConfig.errorNotificationTitle": "エージェント構成を作成できません", "xpack.ingestManager.createAgentConfig.flyoutTitle": "エージェント構成を作成", @@ -8346,20 +8339,6 @@ "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingPackagesTitle": "パッケージの読み込みエラー", "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingSelectedPackageTitle": "選択したパッケージの読み込みエラー", "xpack.ingestManager.createDatasource.stepSelectPackage.filterPackagesInputPlaceholder": "パッケージの検索", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage": "{agentsCount, plural, one {# エージェントを} other {# エージェントを}}{agentConfigsCount, plural, one {このエージェント構成に} other {これらのエージェント構成に}}割り当てました。 {agentsCount, plural, one {このエージェント} other {これらのエージェント}}の登録が解除されます。", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel": "キャンセル", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel": "{agentConfigsCount, plural, one {エージェント構成} other {エージェント構成}} and unenroll {agentsCount, plural, one {エージェント} other {エージェント}} を削除", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel": "{agentConfigsCount, plural, one {エージェント構成} other {エージェント構成}}を削除", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle": "{count, plural, one {this agent config} other {# agent configs}} を削除しますか?", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage": "影響があるエージェントの数を確認中...", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel": "読み込み中...", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage": "{agentConfigsCount, plural, one {this agent config} other {these agentConfigs}}に割り当てられたエージェントはありません。", - "xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle": "{count} 件のエージェント構成の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle": "エージェント構成「{id}」の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle": "エージェント構成の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "{count} 件のエージェント構成を削除しました", - "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "エージェント構成「{id}」を削除しました", - "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "キャンセル", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "{agentConfigName} が一部のエージェントで既に使用されていることをフリートが検出しました。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "このアクションは {agentsCount} {agentsCount, plural, one {# エージェント} other {# エージェント}}に影響します", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "キャンセル", @@ -8375,11 +8354,6 @@ "xpack.ingestManager.deleteDatasource.successSingleNotificationTitle": "データソース「{id}」を削除しました", "xpack.ingestManager.disabledSecurityDescription": "Elastic Fleet を使用するには、Kibana と Elasticsearch でセキュリティを有効にする必要があります。", "xpack.ingestManager.disabledSecurityTitle": "セキュリティが有効ではありません", - "xpack.ingestManager.editConfig.cancelButtonLabel": "キャンセル", - "xpack.ingestManager.editConfig.errorNotificationTitle": "エージェント構成を作成できません", - "xpack.ingestManager.editConfig.flyoutTitle": "構成を編集", - "xpack.ingestManager.editConfig.submitButtonLabel": "更新", - "xpack.ingestManager.editConfig.successNotificationTitle": "エージェント構成「{name}」を更新しました", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "名前を選択", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "新規キーを作成", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "既存のキーを使用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8891b5186cd20..c967c33c4bdb5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8305,9 +8305,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "命名空间", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "软件包", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "流计数", - "xpack.ingestManager.configDetails.subTabs.datasouces": "数据源", - "xpack.ingestManager.configDetails.subTabs.settings": "设置", - "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML 文件", "xpack.ingestManager.configDetails.summary.datasources": "数据源", "xpack.ingestManager.configDetails.summary.lastUpdated": "最后更新时间", "xpack.ingestManager.configDetails.summary.revision": "修订", @@ -8317,10 +8314,6 @@ "xpack.ingestManager.configDetailsDatasources.createFirstButtonText": "创建数据源", "xpack.ingestManager.configDetailsDatasources.createFirstMessage": "此配置尚未有任何数据源。", "xpack.ingestManager.configDetailsDatasources.createFirstTitle": "创建您的首个数据源", - "xpack.ingestManager.configForm.descriptionFieldLabel": "描述", - "xpack.ingestManager.configForm.nameFieldLabel": "名称", - "xpack.ingestManager.configForm.nameRequiredErrorMessage": "配置名称必填", - "xpack.ingestManager.configForm.namespaceFieldLabel": "命名空间", "xpack.ingestManager.createAgentConfig.cancelButtonLabel": "取消", "xpack.ingestManager.createAgentConfig.errorNotificationTitle": "无法创建代理配置", "xpack.ingestManager.createAgentConfig.flyoutTitle": "创建代理配置", @@ -8352,20 +8345,6 @@ "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingPackagesTitle": "加载软件包时出错", "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingSelectedPackageTitle": "加载选定软件包时出错", "xpack.ingestManager.createDatasource.stepSelectPackage.filterPackagesInputPlaceholder": "搜索软件包", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage": "{agentsCount, plural, one {# 个代理} other {# 个代理}}已分配{agentConfigsCount, plural, one {给此代理配置} other {给这些代理配置}}。将取消注册{agentsCount, plural, one {此代理} other {这些代理}}。", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel": "取消", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel": "删除{agentConfigsCount, plural, one {代理配置} other {代理配置}}并取消注册{agentsCount, plural, one {代理} other {代理}}", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel": "删除{agentConfigsCount, plural, one {代理配置} other {代理配置}}", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle": "删除{count, plural, one {此代理配置} other {# 个代理配置}}?", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage": "正在检查受影响代理数量……", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel": "正在加载……", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage": "没有代理分配给{agentConfigsCount, plural, one {此代理配置} other {这些代理配置}}。", - "xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle": "删除 {count} 个代理配置时出错", - "xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle": "删除代理配置“{id}”时出错", - "xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle": "删除代理配置时出错", - "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "已删除 {count} 个代理配置", - "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "已删除代理配置“{id}”", - "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "取消", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "Fleet 已检测到 {agentConfigName} 已由您的部分代理使用。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "此操作将影响 {agentsCount} 个 {agentsCount, plural, one {代理} other {代理}}。", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "取消", @@ -8381,11 +8360,6 @@ "xpack.ingestManager.deleteDatasource.successSingleNotificationTitle": "已删除数据源“{id}”", "xpack.ingestManager.disabledSecurityDescription": "必须在 Kibana 和 Elasticsearch 启用安全性,才能使用 Elastic Fleet。", "xpack.ingestManager.disabledSecurityTitle": "安全性未启用", - "xpack.ingestManager.editConfig.cancelButtonLabel": "取消", - "xpack.ingestManager.editConfig.errorNotificationTitle": "无法更新代理配置", - "xpack.ingestManager.editConfig.flyoutTitle": "编辑配置", - "xpack.ingestManager.editConfig.submitButtonLabel": "更新", - "xpack.ingestManager.editConfig.successNotificationTitle": "代理配置“{name}”已更新", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "选择名称", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "创建新密钥", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "使用现有密钥", From 29658bd908dc0aed344a3b1524db245936050be7 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Mon, 4 May 2020 17:33:21 -0400 Subject: [PATCH 10/55] [Alerting] only show trial upgrade when running with basic license (#64865) (#65138) resolves https://github.com/elastic/kibana/issues/64245 Prior to this PR, the "Upgrade your license" banner in the connectors list was displayed for gold licenses because the Service Now action requires platinum, and the check only looked for any actions disabled by license. Rather than display a different message for gold users, this PR changes the banner display logic to check for any actions disabled by license that also have a minimum required license of gold. That means gold+ users won't see the message, even for actions with a minimum required license of platinum+. Another perk of the gold license! This will continue to display the banner for basic users, but will no longer display it for gold users. It also continues to not display it for trial, platinum and enterprise users. --- .../action_type_menu.tsx | 16 ++-- .../connector_add_flyout.test.tsx | 96 ++++++++++++++++++- .../connector_add_flyout.tsx | 6 +- 3 files changed, 108 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx index 91ecfb2fa8ded..3b2e34e8f29c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_type_menu.tsx @@ -16,13 +16,13 @@ import { checkActionTypeEnabled } from '../../lib/check_action_type_enabled'; interface Props { onActionTypeChange: (actionType: ActionType) => void; actionTypes?: ActionType[]; - setHasActionsDisabledByLicense?: (value: boolean) => void; + setHasActionsUpgradeableByTrial?: (value: boolean) => void; } export const ActionTypeMenu = ({ onActionTypeChange, actionTypes, - setHasActionsDisabledByLicense, + setHasActionsUpgradeableByTrial, }: Props) => { const { http, toastNotifications, actionTypeRegistry } = useActionsConnectorsContext(); const [actionTypesIndex, setActionTypesIndex] = useState(undefined); @@ -36,11 +36,15 @@ export const ActionTypeMenu = ({ index[actionTypeItem.id] = actionTypeItem; } setActionTypesIndex(index); - if (setHasActionsDisabledByLicense) { - const hasActionsDisabledByLicense = availableActionTypes.some( - action => !index[action.id].enabledInLicense + // determine if there are actions disabled by license that that + // would be enabled by upgrading to gold or trial + if (setHasActionsUpgradeableByTrial) { + const hasActionsUpgradeableByTrial = availableActionTypes.some( + action => + !index[action.id].enabledInLicense && + index[action.id].minimumLicenseRequired === 'gold' ); - setHasActionsDisabledByLicense(hasActionsDisabledByLicense); + setHasActionsUpgradeableByTrial(hasActionsUpgradeableByTrial); } } catch (e) { if (toastNotifications) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx index fc10b150ca9d9..decdc7b99d69c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.test.tsx @@ -78,7 +78,7 @@ describe('connector_add_flyout', () => { expect(wrapper.find(`[data-test-subj="${actionType.id}-card"]`).exists()).toBeTruthy(); }); - it('renders banner with subscription links when features are disbaled due to licensing ', () => { + it('renders banner with subscription links when gold features are disabled due to licensing ', () => { const actionType = createActionType(); const disabledActionType = createActionType(); @@ -136,6 +136,100 @@ describe('connector_add_flyout', () => { `"https://www.elastic.co/subscriptions"` ); }); + + it('does not render banner with subscription links when only platinum features are disabled due to licensing ', () => { + const actionType = createActionType(); + const disabledActionType = createActionType(); + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.has.mockReturnValue(true); + + const wrapper = mountWithIntl( + { + return new Promise(() => {}); + }, + }} + > + {}} + actionTypes={[ + { + id: actionType.id, + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + { + id: disabledActionType.id, + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: false, + minimumLicenseRequired: 'platinum', + }, + ]} + /> + + ); + const callout = wrapper.find('UpgradeYourLicenseCallOut'); + expect(callout).toHaveLength(0); + }); + + it('does not render banner with subscription links when only enterprise features are disabled due to licensing ', () => { + const actionType = createActionType(); + const disabledActionType = createActionType(); + + actionTypeRegistry.get.mockReturnValueOnce(actionType); + actionTypeRegistry.has.mockReturnValue(true); + + const wrapper = mountWithIntl( + { + return new Promise(() => {}); + }, + }} + > + {}} + actionTypes={[ + { + id: actionType.id, + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'basic', + }, + { + id: disabledActionType.id, + enabled: true, + name: 'Test', + enabledInConfig: true, + enabledInLicense: false, + minimumLicenseRequired: 'enterprise', + }, + ]} + /> + + ); + const callout = wrapper.find('UpgradeYourLicenseCallOut'); + expect(callout).toHaveLength(0); + }); }); let count = 0; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx index 834a15f072f96..25c19a46fe86b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_add_flyout.tsx @@ -54,7 +54,7 @@ export const ConnectorAddFlyout = ({ reloadConnectors, } = useActionsConnectorsContext(); const [actionType, setActionType] = useState(undefined); - const [hasActionsDisabledByLicense, setHasActionsDisabledByLicense] = useState(false); + const [hasActionsUpgradeableByTrial, setHasActionsUpgradeableByTrial] = useState(false); // hooks const initialConnector = { @@ -96,7 +96,7 @@ export const ConnectorAddFlyout = ({ ); } else { @@ -219,7 +219,7 @@ export const ConnectorAddFlyout = ({ ) : ( From 0164f33ae5fce0b2edb1ec11ee41e641a68ba036 Mon Sep 17 00:00:00 2001 From: Henry Harding Date: Mon, 4 May 2020 17:41:21 -0400 Subject: [PATCH 11/55] Update epm illustration (#64975) (#65142) * update EPM header illustration Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../illustration_integrations_darkmode.svg | 1 + .../illustration_integrations_lightmode.svg | 1 + ...illustration_kibana_getting_started@2x.png | Bin 131132 -> 0 bytes .../sections/epm/components/package_card.tsx | 2 +- .../sections/epm/screens/home/header.tsx | 29 +++++++++++------- 5 files changed, 21 insertions(+), 12 deletions(-) create mode 100755 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_darkmode.svg create mode 100755 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_lightmode.svg delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_darkmode.svg b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_darkmode.svg new file mode 100755 index 0000000000000..b1f86be19a080 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_darkmode.svg @@ -0,0 +1 @@ +Kibana-integrations-darkmode \ No newline at end of file diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_lightmode.svg b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_lightmode.svg new file mode 100755 index 0000000000000..0cddcb0af6909 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_integrations_lightmode.svg @@ -0,0 +1 @@ +Kibana-integrations-lightmode \ No newline at end of file diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/assets/illustration_kibana_getting_started@2x.png deleted file mode 100644 index cad64be0b6e36e79012970814c7018776bee5f73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 131132 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ͤum)*z|9JCu4oQ+`+gia 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 + icon={} href={url} /> ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx index 4d6c02eeef8b4..64950f95f5158 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx @@ -3,15 +3,18 @@ * 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 { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { useLinks } from '../../hooks'; +import { useCore } from '../../../../hooks'; export const HeroCopy = memo(() => { return ( - +

@@ -38,16 +41,20 @@ export const HeroCopy = memo(() => { export const HeroImage = memo(() => { const { toAssets } = useLinks(); - const ImageWrapper = styled.div` - margin-bottom: -62px; + const { uiSettings } = useCore(); + const IS_DARK_THEME = uiSettings.get('theme:darkMode'); + + const Illustration = styled(EuiImage).attrs(props => ({ + alt: i18n.translate('xpack.ingestManager.epm.illustrationAltText', { + defaultMessage: 'Illustration of an Elastic integration', + }), + url: IS_DARK_THEME + ? toAssets('illustration_integrations_darkmode.svg') + : toAssets('illustration_integrations_lightmode.svg'), + }))` + margin-bottom: -60px; + width: 80%; `; - return ( - - - - ); + return ; }); From 35b72b45c8ab2df4966361191fd7de411236a187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 4 May 2020 23:44:00 +0200 Subject: [PATCH 12/55] update apm index pattern (#64232) (#65106) Co-authored-by: Gil Raphaelli --- x-pack/plugins/apm/server/tutorial/index_pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/server/tutorial/index_pattern.json b/x-pack/plugins/apm/server/tutorial/index_pattern.json index caa7fe0f9bf50..09d67b989b7fc 100644 --- a/x-pack/plugins/apm/server/tutorial/index_pattern.json +++ b/x-pack/plugins/apm/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.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.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\":\"cloud.account.id\",\"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.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\":\"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.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.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\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"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,\"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\":true,\"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.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.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.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\":\"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\":\"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.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\":true,\"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.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.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.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.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.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.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\":\"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\":\"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.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.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.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_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\":\"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.project.id\",\"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.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\":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\":\"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\":\"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\":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\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"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\":\"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.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\":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.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\":\"cloud.account.id\",\"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.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\":\"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.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.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\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"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,\"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\":true,\"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.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.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.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\":\"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\":\"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.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\":true,\"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.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.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.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.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.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.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\":\"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\":\"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.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.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.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_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\":\"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.project.id\",\"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.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\":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\":\"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\":\"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\":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\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"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.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\":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 90a4a5e98b7b67fd02c74f4edbf723acec8c565f Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 4 May 2020 16:12:25 -0600 Subject: [PATCH 13/55] [Maps] initial location map settings (#64336) (#65168) * [Maps] initial location map settings * fix tslint * add button to set to current view * move button to bottom of form * review feedback Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- x-pack/plugins/maps/common/constants.ts | 6 + .../maps/public/actions/map_actions.d.ts | 2 +- .../map/mb/get_initial_view.ts | 45 +++ .../connected_components/map/mb/view.js | 3 +- .../navigation_panel.test.tsx.snap | 292 ++++++++++++++++++ .../map_settings_panel/index.ts | 13 +- .../map_settings_panel/map_settings_panel.tsx | 14 +- .../navigation_panel.test.tsx | 45 +++ .../map_settings_panel/navigation_panel.tsx | 200 +++++++++++- .../public/reducers/default_map_settings.ts | 5 +- x-pack/plugins/maps/public/reducers/map.d.ts | 10 + 11 files changed, 620 insertions(+), 15 deletions(-) create mode 100644 x-pack/plugins/maps/public/connected_components/map/mb/get_initial_view.ts create mode 100644 x-pack/plugins/maps/public/connected_components/map_settings_panel/__snapshots__/navigation_panel.test.tsx.snap create mode 100644 x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index fd972219563a8..b1a613e789e85 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -217,3 +217,9 @@ export enum SCALING_TYPES { export const RGBA_0000 = 'rgba(0,0,0,0)'; export const SPATIAL_FILTERS_LAYER_ID = 'SPATIAL_FILTERS_LAYER_ID'; + +export enum INITIAL_LOCATION { + LAST_SAVED_LOCATION = 'LAST_SAVED_LOCATION', + FIXED_LOCATION = 'FIXED_LOCATION', + BROWSER_LOCATION = 'BROWSER_LOCATION', +} diff --git a/x-pack/plugins/maps/public/actions/map_actions.d.ts b/x-pack/plugins/maps/public/actions/map_actions.d.ts index 38c56405787eb..413b440279d77 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/plugins/maps/public/actions/map_actions.d.ts @@ -72,7 +72,7 @@ export function trackMapSettings(): AnyAction; export function updateMapSetting( settingKey: string, - settingValue: string | boolean | number + settingValue: string | boolean | number | object ): AnyAction; export function cloneLayer(layerId: string): AnyAction; diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/get_initial_view.ts b/x-pack/plugins/maps/public/connected_components/map/mb/get_initial_view.ts new file mode 100644 index 0000000000000..30e3b9b46916b --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map/mb/get_initial_view.ts @@ -0,0 +1,45 @@ +/* + * 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 { INITIAL_LOCATION } from '../../../../common/constants'; +import { Goto, MapCenterAndZoom } from '../../../../common/descriptor_types'; +import { MapSettings } from '../../../reducers/map'; + +export async function getInitialView( + goto: Goto | null, + settings: MapSettings +): Promise { + if (settings.initialLocation === INITIAL_LOCATION.FIXED_LOCATION) { + return { + lat: settings.fixedLocation.lat, + lon: settings.fixedLocation.lon, + zoom: settings.fixedLocation.zoom, + }; + } + + if (settings.initialLocation === INITIAL_LOCATION.BROWSER_LOCATION) { + return await new Promise((resolve, reject) => { + navigator.geolocation.getCurrentPosition( + // success callback + pos => { + resolve({ + lat: pos.coords.latitude, + lon: pos.coords.longitude, + zoom: settings.browserLocation.zoom, + }); + }, + // error callback + () => { + // eslint-disable-next-line no-console + console.warn('Unable to fetch browser location for initial map location'); + resolve(null); + } + ); + }); + } + + return goto && goto.center ? goto.center : null; +} diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js index 6bb5a4fed6e52..7afb326f42e02 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -24,6 +24,7 @@ import sprites2 from '@elastic/maki/dist/sprite@2.png'; import { DrawControl } from './draw_control'; import { TooltipControl } from './tooltip_control'; import { clampToLatBounds, clampToLonBounds } from '../../../elasticsearch_geo_utils'; +import { getInitialView } from './get_initial_view'; import { getInjectedVarFunc } from '../../../kibana_services'; @@ -112,6 +113,7 @@ export class MBMapContainer extends React.Component { } async _createMbMapInstance() { + const initialView = await getInitialView(this.props.goto, this.props.settings); return new Promise(resolve => { const mbStyle = { version: 8, @@ -133,7 +135,6 @@ export class MBMapContainer extends React.Component { maxZoom: this.props.settings.maxZoom, minZoom: this.props.settings.minZoom, }; - const initialView = _.get(this.props.goto, 'center'); if (initialView) { options.zoom = initialView.zoom; options.center = { diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/__snapshots__/navigation_panel.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/map_settings_panel/__snapshots__/navigation_panel.test.tsx.snap new file mode 100644 index 0000000000000..641dd20a1a44a --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/__snapshots__/navigation_panel.test.tsx.snap @@ -0,0 +1,292 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render 1`] = ` + + +
+ +
+
+ + + + + +
+`; + +exports[`should render browser location form when initialLocation is BROWSER_LOCATION 1`] = ` + + +
+ +
+
+ + + + + + + + +
+`; + +exports[`should render fixed location form when initialLocation is FIXED_LOCATION 1`] = ` + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + +
+`; diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/index.ts b/x-pack/plugins/maps/public/connected_components/map_settings_panel/index.ts index 329fac28d7d2e..eaa49719059c5 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/index.ts +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/index.ts @@ -10,13 +10,20 @@ import { FLYOUT_STATE } from '../../reducers/ui'; import { MapStoreState } from '../../reducers/store'; import { MapSettingsPanel } from './map_settings_panel'; import { rollbackMapSettings, updateMapSetting } from '../../actions/map_actions'; -import { getMapSettings, hasMapSettingsChanges } from '../../selectors/map_selectors'; +import { + getMapCenter, + getMapSettings, + getMapZoom, + hasMapSettingsChanges, +} from '../../selectors/map_selectors'; import { updateFlyout } from '../../actions/ui_actions'; function mapStateToProps(state: MapStoreState) { return { - settings: getMapSettings(state), + center: getMapCenter(state), hasMapSettingsChanges: hasMapSettingsChanges(state), + settings: getMapSettings(state), + zoom: getMapZoom(state), }; } @@ -29,7 +36,7 @@ function mapDispatchToProps(dispatch: Dispatch) { keepChanges: () => { dispatch(updateFlyout(FLYOUT_STATE.NONE)); }, - updateMapSetting: (settingKey: string, settingValue: string | number | boolean) => { + updateMapSetting: (settingKey: string, settingValue: string | number | boolean | object) => { dispatch(updateMapSetting(settingKey, settingValue)); }, }; diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx index a89f4461fff06..66b979869416d 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/map_settings_panel.tsx @@ -20,21 +20,26 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { MapSettings } from '../../reducers/map'; import { NavigationPanel } from './navigation_panel'; import { SpatialFiltersPanel } from './spatial_filters_panel'; +import { MapCenter } from '../../../common/descriptor_types'; interface Props { cancelChanges: () => void; + center: MapCenter; hasMapSettingsChanges: boolean; keepChanges: () => void; settings: MapSettings; - updateMapSetting: (settingKey: string, settingValue: string | number | boolean) => void; + updateMapSetting: (settingKey: string, settingValue: string | number | boolean | object) => void; + zoom: number; } export function MapSettingsPanel({ cancelChanges, + center, hasMapSettingsChanges, keepChanges, settings, updateMapSetting, + zoom, }: Props) { // TODO move common text like Cancel and Close to common i18n translation const closeBtnLabel = hasMapSettingsChanges @@ -60,7 +65,12 @@ export function MapSettingsPanel({
- +
diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx new file mode 100644 index 0000000000000..d785a30324e4e --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.test.tsx @@ -0,0 +1,45 @@ +/* + * 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 } from 'enzyme'; + +import { NavigationPanel } from './navigation_panel'; +import { getDefaultMapSettings } from '../../reducers/default_map_settings'; +import { INITIAL_LOCATION } from '../../../common/constants'; + +const defaultProps = { + center: { lat: 0, lon: 0 }, + settings: getDefaultMapSettings(), + updateMapSetting: () => {}, + zoom: 0, +}; + +test('should render', async () => { + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); + +test('should render fixed location form when initialLocation is FIXED_LOCATION', async () => { + const settings = { + ...defaultProps.settings, + initialLocation: INITIAL_LOCATION.FIXED_LOCATION, + }; + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); + +test('should render browser location form when initialLocation is BROWSER_LOCATION', async () => { + const settings = { + ...defaultProps.settings, + initialLocation: INITIAL_LOCATION.BROWSER_LOCATION, + }; + const component = shallow(); + + expect(component).toMatchSnapshot(); +}); diff --git a/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.tsx b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.tsx index ed83e838f44f6..0e12f20dd9a7a 100644 --- a/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.tsx +++ b/x-pack/plugins/maps/public/connected_components/map_settings_panel/navigation_panel.tsx @@ -4,25 +4,198 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; +import React, { ChangeEvent } from 'react'; +import { + EuiButtonEmpty, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPanel, + EuiRadioGroup, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { MapSettings } from '../../reducers/map'; import { ValidatedDualRange, Value } from '../../../../../../src/plugins/kibana_react/public'; -import { MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; +import { INITIAL_LOCATION, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; +import { MapCenter } from '../../../common/descriptor_types'; +// @ts-ignore +import { ValidatedRange } from '../../components/validated_range'; interface Props { + center: MapCenter; settings: MapSettings; - updateMapSetting: (settingKey: string, settingValue: string | number | boolean) => void; + updateMapSetting: (settingKey: string, settingValue: string | number | boolean | object) => void; + zoom: number; } -export function NavigationPanel({ settings, updateMapSetting }: Props) { +const initialLocationOptions = [ + { + id: INITIAL_LOCATION.LAST_SAVED_LOCATION, + label: i18n.translate('xpack.maps.mapSettingsPanel.lastSavedLocationLabel', { + defaultMessage: 'Map location at save', + }), + }, + { + id: INITIAL_LOCATION.FIXED_LOCATION, + label: i18n.translate('xpack.maps.mapSettingsPanel.fixedLocationLabel', { + defaultMessage: 'Fixed location', + }), + }, + { + id: INITIAL_LOCATION.BROWSER_LOCATION, + label: i18n.translate('xpack.maps.mapSettingsPanel.browserLocationLabel', { + defaultMessage: 'Browser location', + }), + }, +]; + +export function NavigationPanel({ center, settings, updateMapSetting, zoom }: Props) { const onZoomChange = (value: Value) => { - updateMapSetting('minZoom', Math.max(MIN_ZOOM, parseInt(value[0] as string, 10))); - updateMapSetting('maxZoom', Math.min(MAX_ZOOM, parseInt(value[1] as string, 10))); + const minZoom = Math.max(MIN_ZOOM, parseInt(value[0] as string, 10)); + const maxZoom = Math.min(MAX_ZOOM, parseInt(value[1] as string, 10)); + updateMapSetting('minZoom', minZoom); + updateMapSetting('maxZoom', maxZoom); + + // ensure fixed zoom and browser zoom stay within defined min/max + if (settings.fixedLocation.zoom < minZoom) { + onFixedZoomChange(minZoom); + } else if (settings.fixedLocation.zoom > maxZoom) { + onFixedZoomChange(maxZoom); + } + + if (settings.browserLocation.zoom < minZoom) { + onBrowserZoomChange(minZoom); + } else if (settings.browserLocation.zoom > maxZoom) { + onBrowserZoomChange(maxZoom); + } + }; + + const onInitialLocationChange = (optionId: string): void => { + updateMapSetting('initialLocation', optionId); + }; + + const onFixedLatChange = (event: ChangeEvent) => { + let value = parseFloat(event.target.value); + if (isNaN(value)) { + value = 0; + } else if (value < -90) { + value = -90; + } else if (value > 90) { + value = 90; + } + updateMapSetting('fixedLocation', { ...settings.fixedLocation, lat: value }); + }; + + const onFixedLonChange = (event: ChangeEvent) => { + let value = parseFloat(event.target.value); + if (isNaN(value)) { + value = 0; + } else if (value < -180) { + value = -180; + } else if (value > 180) { + value = 180; + } + updateMapSetting('fixedLocation', { ...settings.fixedLocation, lon: value }); + }; + + const onFixedZoomChange = (value: number) => { + updateMapSetting('fixedLocation', { ...settings.fixedLocation, zoom: value }); + }; + + const onBrowserZoomChange = (value: number) => { + updateMapSetting('browserLocation', { zoom: value }); + }; + + const useCurrentView = () => { + updateMapSetting('fixedLocation', { + lat: center.lat, + lon: center.lon, + zoom: Math.round(zoom), + }); }; + function renderInitialLocationInputs() { + if (settings.initialLocation === INITIAL_LOCATION.LAST_SAVED_LOCATION) { + return null; + } + + const zoomFormRow = ( + + + + ); + + if (settings.initialLocation === INITIAL_LOCATION.BROWSER_LOCATION) { + return zoomFormRow; + } + + return ( + <> + + + + + + + {zoomFormRow} + + + + + + + + + ); + } + return ( @@ -50,6 +223,19 @@ export function NavigationPanel({ settings, updateMapSetting }: Props) { allowEmptyRange={false} compressed /> + + + + + {renderInitialLocationInputs()} ); } diff --git a/x-pack/plugins/maps/public/reducers/default_map_settings.ts b/x-pack/plugins/maps/public/reducers/default_map_settings.ts index fe21b37434edd..9c9b814ae6add 100644 --- a/x-pack/plugins/maps/public/reducers/default_map_settings.ts +++ b/x-pack/plugins/maps/public/reducers/default_map_settings.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MAX_ZOOM, MIN_ZOOM } from '../../common/constants'; +import { INITIAL_LOCATION, MAX_ZOOM, MIN_ZOOM } from '../../common/constants'; import { MapSettings } from './map'; export function getDefaultMapSettings(): MapSettings { return { + initialLocation: INITIAL_LOCATION.LAST_SAVED_LOCATION, + fixedLocation: { lat: 0, lon: 0, zoom: 2 }, + browserLocation: { zoom: 2 }, maxZoom: MAX_ZOOM, minZoom: MIN_ZOOM, showSpatialFilters: true, diff --git a/x-pack/plugins/maps/public/reducers/map.d.ts b/x-pack/plugins/maps/public/reducers/map.d.ts index be0700d4bdd6d..20e1dc1035e19 100644 --- a/x-pack/plugins/maps/public/reducers/map.d.ts +++ b/x-pack/plugins/maps/public/reducers/map.d.ts @@ -15,6 +15,7 @@ import { MapRefreshConfig, TooltipState, } from '../../common/descriptor_types'; +import { INITIAL_LOCATION } from '../../common/constants'; import { Filter, TimeRange } from '../../../../../src/plugins/data/public'; export type MapContext = { @@ -40,6 +41,15 @@ export type MapContext = { }; export type MapSettings = { + initialLocation: INITIAL_LOCATION; + fixedLocation: { + lat: number; + lon: number; + zoom: number; + }; + browserLocation: { + zoom: number; + }; maxZoom: number; minZoom: number; showSpatialFilters: boolean; From e3de86b2e227db102c224ec622e9afcfa9167bdb Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 4 May 2020 19:58:13 -0400 Subject: [PATCH 14/55] [7.x] [Fleet] Better display of fleet requirements (#65027) (#65124) --- .../ingest_manager/common/types/index.ts | 1 + .../common/types/rest_spec/fleet_setup.ts | 5 + x-pack/plugins/ingest_manager/kibana.json | 2 +- .../ingest_manager/hooks/use_fleet_status.tsx | 69 ++++++++++++ .../ingest_manager/hooks/use_request/index.ts | 1 + .../ingest_manager/hooks/use_request/setup.ts | 10 +- .../applications/ingest_manager/index.tsx | 5 +- .../agent_enrollment_flyout/index.tsx | 58 +++++++--- .../ingest_manager/sections/fleet/index.tsx | 18 ++- .../sections/fleet/setup_page/index.tsx | 103 ++++++++++++------ .../ingest_manager/types/index.ts | 2 + x-pack/plugins/ingest_manager/server/index.ts | 1 + .../plugins/ingest_manager/server/plugin.ts | 17 ++- .../server/routes/setup/handlers.ts | 47 +++++--- .../server/routes/setup/index.ts | 5 +- .../server/services/agent_config.ts | 10 +- .../server/services/app_context.ts | 27 ++++- .../server/services/datasource.ts | 3 + .../ingest_manager/server/services/output.ts | 5 +- .../ingest_manager/server/services/setup.ts | 7 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 22 files changed, 309 insertions(+), 91 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_fleet_status.tsx diff --git a/x-pack/plugins/ingest_manager/common/types/index.ts b/x-pack/plugins/ingest_manager/common/types/index.ts index 748bb14d2d35d..b357d0c2d75f4 100644 --- a/x-pack/plugins/ingest_manager/common/types/index.ts +++ b/x-pack/plugins/ingest_manager/common/types/index.ts @@ -14,6 +14,7 @@ export interface IngestManagerConfigType { }; fleet: { enabled: boolean; + tlsCheckDisabled: boolean; defaultOutputHost: string; kibana: { host?: string; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts index c4ba8ee595acf..ae4cb4e3fce49 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/fleet_setup.ts @@ -7,3 +7,8 @@ export interface CreateFleetSetupResponse { isInitialized: boolean; } + +export interface GetFleetStatusResponse { + isReady: boolean; + missing_requirements: Array<'tls_required' | 'api_keys' | 'fleet_admin_user'>; +} diff --git a/x-pack/plugins/ingest_manager/kibana.json b/x-pack/plugins/ingest_manager/kibana.json index cef1a293c104b..382ea0444093d 100644 --- a/x-pack/plugins/ingest_manager/kibana.json +++ b/x-pack/plugins/ingest_manager/kibana.json @@ -5,5 +5,5 @@ "ui": true, "configPath": ["xpack", "ingestManager"], "requiredPlugins": ["licensing", "data", "encryptedSavedObjects"], - "optionalPlugins": ["security", "features"] + "optionalPlugins": ["security", "features", "cloud"] } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_fleet_status.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_fleet_status.tsx new file mode 100644 index 0000000000000..ef40c171b9ca3 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_fleet_status.tsx @@ -0,0 +1,69 @@ +/* + * 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, { useState, useContext, useEffect } from 'react'; +import { useConfig } from './use_config'; +import { sendGetFleetStatus } from './use_request'; +import { GetFleetStatusResponse } from '../types'; + +interface FleetStatusState { + enabled: boolean; + isLoading: boolean; + isReady: boolean; + missingRequirements?: GetFleetStatusResponse['missing_requirements']; +} + +interface FleetStatus extends FleetStatusState { + refresh: () => Promise; +} + +const FleetStatusContext = React.createContext(undefined); + +export const FleetStatusProvider: React.FC = ({ children }) => { + const config = useConfig(); + const [state, setState] = useState({ + enabled: config.fleet.enabled, + isLoading: false, + isReady: false, + }); + async function sendGetStatus() { + try { + setState(s => ({ ...s, isLoading: true })); + const res = await sendGetFleetStatus(); + if (res.error) { + throw res.error; + } + + setState(s => ({ + ...s, + isLoading: false, + isReady: res.data?.isReady ?? false, + missingRequirements: res.data?.missing_requirements, + })); + } catch (error) { + setState(s => ({ ...s, isLoading: true })); + } + } + useEffect(() => { + sendGetStatus(); + }, []); + + return ( + sendGetStatus() }}> + {children} + + ); +}; + +export function useFleetStatus(): FleetStatus { + const context = useContext(FleetStatusContext); + + if (!context) { + throw new Error('FleetStatusContext not set'); + } + + return context; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts index c39d2a5860bf0..25cdffc5c6651 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/index.ts @@ -12,3 +12,4 @@ export * from './enrollment_api_keys'; export * from './epm'; export * from './outputs'; export * from './settings'; +export * from './setup'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/setup.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/setup.ts index 04fdf9f66948f..e4e84e4701f13 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/setup.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/setup.ts @@ -5,7 +5,8 @@ */ import { sendRequest } from './use_request'; -import { setupRouteService } from '../../services'; +import { setupRouteService, fleetSetupRouteService } from '../../services'; +import { GetFleetStatusResponse } from '../../types'; export const sendSetup = () => { return sendRequest({ @@ -13,3 +14,10 @@ export const sendSetup = () => { method: 'post', }); }; + +export const sendGetFleetStatus = () => { + return sendRequest({ + path: fleetSetupRouteService.getFleetSetupPath(), + method: 'get', + }); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 295a35693726f..f0a0c90a18c24 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -23,6 +23,7 @@ import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks'; import { PackageInstallProvider } from './sections/epm/hooks'; import { sendSetup } from './hooks/use_request/setup'; +import { FleetStatusProvider } from './hooks/use_fleet_status'; import './index.scss'; export interface ProtectedRouteProps extends RouteProps { @@ -142,7 +143,9 @@ const IngestManagerApp = ({ - + + + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx index dd34e7260b27b..e9347ccd2d6c9 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/components/agent_enrollment_flyout/index.tsx @@ -15,11 +15,15 @@ import { EuiButtonEmpty, EuiButton, EuiFlyoutFooter, + EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AgentConfig } from '../../../../../types'; import { APIKeySelection } from './key_selection'; import { EnrollmentInstructions } from './instructions'; +import { useFleetStatus } from '../../../../../hooks/use_fleet_status'; +import { useLink } from '../../../../../hooks'; +import { FLEET_PATH } from '../../../../../constants'; interface Props { onClose: () => void; @@ -30,8 +34,11 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentConfigs = [], }) => { + const fleetStatus = useFleetStatus(); const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(); + const fleetLink = useLink(FLEET_PATH); + return ( @@ -45,12 +52,33 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ - setSelectedAPIKeyId(keyId)} - /> - - + {fleetStatus.isReady ? ( + <> + setSelectedAPIKeyId(keyId)} + /> + + + + ) : ( + <> + + + + ), + }} + /> + + )} @@ -62,14 +90,16 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ /> - - - - - + {fleetStatus.isReady && ( + + + + + + )} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx index fac81ecc19cd1..b9c5418dbf6f3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx @@ -6,35 +6,31 @@ import React from 'react'; import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; import { Loading } from '../../components'; -import { useConfig, useCore, useRequest } from '../../hooks'; +import { useConfig, useCore } from '../../hooks'; import { AgentListPage } from './agent_list_page'; import { SetupPage } from './setup_page'; import { AgentDetailsPage } from './agent_details_page'; import { NoAccessPage } from './error_pages/no_access'; -import { fleetSetupRouteService } from '../../services'; import { EnrollmentTokenListPage } from './enrollment_token_list_page'; import { ListLayout } from './components/list_layout'; +import { useFleetStatus } from '../../hooks/use_fleet_status'; export const FleetApp: React.FunctionComponent = () => { const core = useCore(); const { fleet } = useConfig(); - const setupRequest = useRequest({ - method: 'get', - path: fleetSetupRouteService.getFleetSetupPath(), - }); + const fleetStatus = useFleetStatus(); if (!fleet.enabled) return null; - if (setupRequest.isLoading) { + if (fleetStatus.isLoading) { return ; } - if (setupRequest.data.isInitialized === false) { + if (fleetStatus.isReady === false) { return ( { - await setupRequest.sendRequest(); - }} + missingRequirements={fleetStatus.missingRequirements || []} + refresh={fleetStatus.refresh} /> ); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx index 96d4d01d67a49..4d89268c14b28 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx @@ -18,10 +18,12 @@ import { import { sendRequest, useCore } from '../../../hooks'; import { fleetSetupRouteService } from '../../../services'; import { WithoutHeaderLayout } from '../../../layouts'; +import { GetFleetStatusResponse } from '../../../types'; export const SetupPage: React.FunctionComponent<{ refresh: () => Promise; -}> = ({ refresh }) => { + missingRequirements: GetFleetStatusResponse['missing_requirements']; +}> = ({ refresh, missingRequirements }) => { const [isFormLoading, setIsFormLoading] = useState(false); const core = useCore(); @@ -40,46 +42,81 @@ export const SetupPage: React.FunctionComponent<{ } }; + const content = + missingRequirements.includes('tls_required') || missingRequirements.includes('api_keys') ? ( + <> + + + + +

+ +

+
+ + + , + }} + /> + + + + ) : ( + <> + + + + +

+ +

+
+ + + + + + +
+ + + +
+
+ + + ); + return ( - + - - - - -

- -

-
- - - - - - -
- - - -
-
- + {content}
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 602015d23cefb..ca5bf999aa81a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -20,6 +20,8 @@ export { DatasourceConfigRecordEntry, Output, DataStream, + // API schema - misc setup, status + GetFleetStatusResponse, // API schemas - Agent Config GetAgentConfigsResponse, GetAgentConfigsResponseItem, diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts index 951ff2337d8c7..6096af8d80801 100644 --- a/x-pack/plugins/ingest_manager/server/index.ts +++ b/x-pack/plugins/ingest_manager/server/index.ts @@ -26,6 +26,7 @@ export const config = { }), fleet: schema.object({ enabled: schema.boolean({ defaultValue: true }), + tlsCheckDisabled: schema.boolean({ defaultValue: false }), kibana: schema.object({ host: schema.maybe(schema.string()), ca_sha256: schema.maybe(schema.string()), diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 097825e0b69e1..030abfb6d3f27 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -11,6 +11,7 @@ import { Plugin, PluginInitializerContext, SavedObjectsServiceStart, + HttpServerInfo, } from 'kibana/server'; import { LicensingPluginSetup, ILicense } from '../../licensing/server'; import { @@ -42,7 +43,6 @@ import { registerOutputRoutes, registerSettingsRoutes, } from './routes'; - import { IngestManagerConfigType } from '../common'; import { appContextService, @@ -52,12 +52,14 @@ import { AgentService, } from './services'; import { getAgentStatusById } from './services/agents'; +import { CloudSetup } from '../../cloud/server'; export interface IngestManagerSetupDeps { licensing: LicensingPluginSetup; security?: SecurityPluginSetup; features?: FeaturesPluginSetup; encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; + cloud?: CloudSetup; } export type IngestManagerStartDeps = object; @@ -67,6 +69,9 @@ export interface IngestManagerAppContext { security?: SecurityPluginSetup; config$?: Observable; savedObjects: SavedObjectsServiceStart; + isProductionMode: boolean; + serverInfo?: HttpServerInfo; + cloud?: CloudSetup; } export type IngestManagerSetupContract = void; @@ -100,16 +105,23 @@ export class IngestManagerPlugin private licensing$!: Observable; private config$: Observable; private security: SecurityPluginSetup | undefined; + private cloud: CloudSetup | undefined; + + private isProductionMode: boolean; + private serverInfo: HttpServerInfo | undefined; constructor(private readonly initializerContext: PluginInitializerContext) { this.config$ = this.initializerContext.config.create(); + this.isProductionMode = this.initializerContext.env.mode.prod; } public async setup(core: CoreSetup, deps: IngestManagerSetupDeps) { + this.serverInfo = core.http.getServerInfo(); this.licensing$ = deps.licensing.license$; if (deps.security) { this.security = deps.security; } + this.cloud = deps.cloud; registerSavedObjects(core.savedObjects); registerEncryptedSavedObjects(deps.encryptedSavedObjects); @@ -184,6 +196,9 @@ export class IngestManagerPlugin security: this.security, config$: this.config$, savedObjects: core.savedObjects, + isProductionMode: this.isProductionMode, + serverInfo: this.serverInfo, + cloud: this.cloud, }); licenseService.start(this.licensing$); return { diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts index 837e73b966feb..542dfa9cefe8f 100644 --- a/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/setup/handlers.ts @@ -4,28 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ import { RequestHandler } from 'src/core/server'; -import { outputService } from '../../services'; -import { CreateFleetSetupResponse } from '../../../common'; +import { outputService, appContextService } from '../../services'; +import { GetFleetStatusResponse } from '../../../common'; import { setupIngestManager, setupFleet } from '../../services/setup'; -export const getFleetSetupHandler: RequestHandler = async (context, request, response) => { +export const getFleetStatusHandler: RequestHandler = async (context, request, response) => { const soClient = context.core.savedObjects.client; - const successBody: CreateFleetSetupResponse = { isInitialized: true }; - const failureBody: CreateFleetSetupResponse = { isInitialized: false }; try { - const adminUser = await outputService.getAdminUser(soClient); - if (adminUser) { - return response.ok({ - body: successBody, - }); - } else { - return response.ok({ - body: failureBody, - }); + const isAdminUserSetup = (await outputService.getAdminUser(soClient)) !== null; + const isApiKeysEnabled = await appContextService.getSecurity().authc.areAPIKeysEnabled(); + const isTLSEnabled = appContextService.getServerInfo().protocol === 'https'; + const isProductionMode = appContextService.getIsProductionMode(); + const isCloud = appContextService.getCloud()?.isCloudEnabled ?? false; + const isTLSCheckDisabled = appContextService.getConfig()?.fleet?.tlsCheckDisabled ?? false; + + const missingRequirements: GetFleetStatusResponse['missing_requirements'] = []; + if (!isAdminUserSetup) { + missingRequirements.push('fleet_admin_user'); } - } catch (e) { + if (!isApiKeysEnabled) { + missingRequirements.push('api_keys'); + } + if (!isTLSCheckDisabled && !isCloud && isProductionMode && !isTLSEnabled) { + missingRequirements.push('tls_required'); + } + + const body: GetFleetStatusResponse = { + isReady: missingRequirements.length === 0, + missing_requirements: missingRequirements, + }; + return response.ok({ - body: failureBody, + body, + }); + } catch (e) { + return response.customError({ + statusCode: 500, + body: { message: e.message }, }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts index edc9a0a268161..f408d875a80b3 100644 --- a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import { IRouter } from 'src/core/server'; + import { PLUGIN_ID, FLEET_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../constants'; import { - getFleetSetupHandler, + getFleetStatusHandler, createFleetSetupHandler, ingestManagerSetupHandler, } from './handlers'; @@ -30,7 +31,7 @@ export const registerRoutes = (router: IRouter) => { validate: false, options: { tags: [`access:${PLUGIN_ID}-read`] }, }, - getFleetSetupHandler + getFleetStatusHandler ); // Create Fleet setup diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index 5ecbaff8ad71e..84bcd7db3f7b1 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -314,10 +314,12 @@ class AgentConfigService { if (!config) { return null; } - const defaultOutput = await outputService.get( - soClient, - await outputService.getDefaultOutputId(soClient) - ); + + const defaultOutputId = await outputService.getDefaultOutputId(soClient); + if (!defaultOutputId) { + throw new Error('Default output is not setup'); + } + const defaultOutput = await outputService.get(soClient, defaultOutputId); const agentConfig: FullAgentConfig = { id: config.id, diff --git a/x-pack/plugins/ingest_manager/server/services/app_context.ts b/x-pack/plugins/ingest_manager/server/services/app_context.ts index e917d2edd1309..5e538ad84b4c2 100644 --- a/x-pack/plugins/ingest_manager/server/services/app_context.ts +++ b/x-pack/plugins/ingest_manager/server/services/app_context.ts @@ -5,11 +5,12 @@ */ import { BehaviorSubject, Observable } from 'rxjs'; import { first } from 'rxjs/operators'; -import { SavedObjectsServiceStart } from 'src/core/server'; +import { SavedObjectsServiceStart, HttpServerInfo } from 'src/core/server'; import { EncryptedSavedObjectsPluginStart } from '../../../encrypted_saved_objects/server'; import { SecurityPluginSetup } from '../../../security/server'; import { IngestManagerConfigType } from '../../common'; import { IngestManagerAppContext } from '../plugin'; +import { CloudSetup } from '../../../cloud/server'; class AppContextService { private encryptedSavedObjects: EncryptedSavedObjectsPluginStart | undefined; @@ -17,11 +18,17 @@ class AppContextService { private config$?: Observable; private configSubject$?: BehaviorSubject; private savedObjects: SavedObjectsServiceStart | undefined; + private serverInfo: HttpServerInfo | undefined; + private isProductionMode: boolean = false; + private cloud?: CloudSetup; public async start(appContext: IngestManagerAppContext) { this.encryptedSavedObjects = appContext.encryptedSavedObjects; this.security = appContext.security; this.savedObjects = appContext.savedObjects; + this.serverInfo = appContext.serverInfo; + this.isProductionMode = appContext.isProductionMode; + this.cloud = appContext.cloud; if (appContext.config$) { this.config$ = appContext.config$; @@ -41,9 +48,16 @@ class AppContextService { } public getSecurity() { + if (!this.security) { + throw new Error('Secury service not set.'); + } return this.security; } + public getCloud() { + return this.cloud; + } + public getConfig() { return this.configSubject$?.value; } @@ -58,6 +72,17 @@ class AppContextService { } return this.savedObjects; } + + public getIsProductionMode() { + return this.isProductionMode; + } + + public getServerInfo() { + if (!this.serverInfo) { + throw new Error('Server info not set.'); + } + return this.serverInfo; + } } export const appContextService = new AppContextService(); diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index affd9b2755881..0497bc5a2b541 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -196,6 +196,9 @@ class DatasourceService { outputService.getDefaultOutputId(soClient), ]); if (pkgInfo) { + if (!defaultOutputId) { + throw new Error('Default output is not set'); + } return packageToConfigDatasource(pkgInfo, '', defaultOutputId); } } diff --git a/x-pack/plugins/ingest_manager/server/services/output.ts b/x-pack/plugins/ingest_manager/server/services/output.ts index 395c9af4a4ca2..3628c5bd9e183 100644 --- a/x-pack/plugins/ingest_manager/server/services/output.ts +++ b/x-pack/plugins/ingest_manager/server/services/output.ts @@ -48,7 +48,7 @@ class OutputService { }); if (!outputs.saved_objects.length) { - throw new Error('No default output'); + return null; } return outputs.saved_objects[0].id; @@ -56,6 +56,9 @@ class OutputService { public async getAdminUser(soClient: SavedObjectsClientContract) { const defaultOutputId = await this.getDefaultOutputId(soClient); + if (!defaultOutputId) { + return null; + } const so = await appContextService .getEncryptedSavedObjects() ?.getDecryptedAsInternalUser(OUTPUT_SAVED_OBJECT_TYPE, defaultOutputId); diff --git a/x-pack/plugins/ingest_manager/server/services/setup.ts b/x-pack/plugins/ingest_manager/server/services/setup.ts index 390e240841611..3619628bd4f8b 100644 --- a/x-pack/plugins/ingest_manager/server/services/setup.ts +++ b/x-pack/plugins/ingest_manager/server/services/setup.ts @@ -109,7 +109,12 @@ export async function setupFleet( }); // save fleet admin user - await outputService.updateOutput(soClient, await outputService.getDefaultOutputId(soClient), { + const defaultOutputId = await outputService.getDefaultOutputId(soClient); + if (!defaultOutputId) { + throw new Error('Default output does not exist'); + } + + await outputService.updateOutput(soClient, defaultOutputId, { fleet_enroll_username: FLEET_ENROLL_USERNAME, fleet_enroll_password: password, }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index d491beaa2a689..377e58964dfdb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8377,9 +8377,7 @@ "xpack.ingestManager.noAccess.accessDeniedTitle": "アクセスが拒否されました", "xpack.ingestManager.overviewPageSubtitle": "Ingest Manager についてのロレムイプサム説明文。", "xpack.ingestManager.overviewPageTitle": "Ingest Manager", - "xpack.ingestManager.setupPage.description": "フリートを使用するには、Elastic ユーザーを作成する必要があります。このユーザーは、API キーを作成して、logs-* および metrics-* に書き込むことができます。", "xpack.ingestManager.setupPage.enableFleet": "ユーザーを作成してフリートを有効にます", - "xpack.ingestManager.setupPage.title": "フリートを有効にする", "xpack.ingestManager.unenrollAgents.confirmModal.cancelButtonLabel": "キャンセル", "xpack.ingestManager.unenrollAgents.confirmModal.confirmButtonLabel": "登録解除", "xpack.ingestManager.unenrollAgents.confirmModal.deleteMultipleTitle": "{count, plural, one {# エージェント} other {# エージェント}}の登録を解除しますか?", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c967c33c4bdb5..37141fbbc855f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8383,9 +8383,7 @@ "xpack.ingestManager.noAccess.accessDeniedTitle": "访问被拒绝", "xpack.ingestManager.overviewPageSubtitle": "Lorem ipsum some description about ingest manager.", "xpack.ingestManager.overviewPageTitle": "Ingest Manager", - "xpack.ingestManager.setupPage.description": "要使用 Fleet,必须创建 Elastic 用户。此用户可以创建 API 密钥并写入到 logs-* and metrics-*。", "xpack.ingestManager.setupPage.enableFleet": "创建用户并启用 Fleet", - "xpack.ingestManager.setupPage.title": "启用 Fleet", "xpack.ingestManager.unenrollAgents.confirmModal.cancelButtonLabel": "取消", "xpack.ingestManager.unenrollAgents.confirmModal.confirmButtonLabel": "取消注册", "xpack.ingestManager.unenrollAgents.confirmModal.deleteMultipleTitle": "取消注册 {count, plural, one {# 个代理} other {# 个代理}}?", From 2cfe4e0968acef59b4cffb7665eb33fc65f6b63d Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 4 May 2020 18:11:33 -0600 Subject: [PATCH 15/55] [Maps] fix remove layer (#65093) (#65120) Co-authored-by: Elastic Machine --- .../toc_entry_actions_popover/toc_entry_actions_popover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx index d628cca61de11..e0fdff62829fb 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.tsx @@ -75,7 +75,7 @@ export class TOCEntryActionsPopover extends Component { } _removeLayer() { - this.props.fitToBounds(this.props.layer.getId()); + this.props.removeLayer(this.props.layer.getId()); } _toggleVisible() { From ee7a2b38c6a6c15e3c7e1d0caea7d1385a6466cb Mon Sep 17 00:00:00 2001 From: Spencer Date: Mon, 4 May 2020 17:27:39 -0700 Subject: [PATCH 16/55] [7.x] [ui-shared-deps] only compress in production build mode (#65151) (#65192) --- packages/kbn-ui-shared-deps/webpack.config.js | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/kbn-ui-shared-deps/webpack.config.js b/packages/kbn-ui-shared-deps/webpack.config.js index 52e7bb620b50b..ca913d0f16417 100644 --- a/packages/kbn-ui-shared-deps/webpack.config.js +++ b/packages/kbn-ui-shared-deps/webpack.config.js @@ -118,15 +118,19 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ new webpack.DefinePlugin({ 'process.env.NODE_ENV': dev ? '"development"' : '"production"', }), - new CompressionPlugin({ - algorithm: 'brotliCompress', - filename: '[path].br', - test: /\.(js|css)$/, - }), - new CompressionPlugin({ - algorithm: 'gzip', - filename: '[path].gz', - test: /\.(js|css)$/, - }), + ...(dev + ? [] + : [ + new CompressionPlugin({ + algorithm: 'brotliCompress', + filename: '[path].br', + test: /\.(js|css)$/, + }), + new CompressionPlugin({ + algorithm: 'gzip', + filename: '[path].gz', + test: /\.(js|css)$/, + }), + ]), ], }); From fa1faa69fc229470b83a605c441cdc2f413eb578 Mon Sep 17 00:00:00 2001 From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com> Date: Mon, 4 May 2020 21:08:18 -0400 Subject: [PATCH 17/55] EMT-339: add policy response index and documents (#65004) (#65173) EMT-339: add policy response index and documents --- .../endpoint/common/generate_data.test.ts | 11 + .../plugins/endpoint/common/generate_data.ts | 229 +++++--- .../plugins/endpoint/common/schema/policy.ts | 12 + x-pack/plugins/endpoint/common/types.ts | 22 +- .../endpoint/scripts/policy_mapping.json | 398 ++++++++++++++ .../endpoint/scripts/resolver_generator.ts | 29 +- .../plugins/endpoint/server/index_pattern.ts | 5 + x-pack/plugins/endpoint/server/mocks.ts | 30 +- x-pack/plugins/endpoint/server/plugin.ts | 2 + .../server/routes/metadata/metadata.test.ts | 28 +- .../server/routes/policy/handlers.test.ts | 138 +++++ .../endpoint/server/routes/policy/handlers.ts | 36 ++ .../endpoint/server/routes/policy/index.ts | 23 + .../server/routes/policy/service.test.ts | 19 + .../endpoint/server/routes/policy/service.ts | 49 ++ .../api_integration/apis/endpoint/index.ts | 1 + .../api_integration/apis/endpoint/policy.ts | 40 ++ .../es_archives/endpoint/policy/data.json.gz | Bin 0 -> 1329 bytes .../es_archives/endpoint/policy/mappings.json | 512 ++++++++++++++++++ 19 files changed, 1469 insertions(+), 115 deletions(-) create mode 100644 x-pack/plugins/endpoint/common/schema/policy.ts create mode 100644 x-pack/plugins/endpoint/scripts/policy_mapping.json create mode 100644 x-pack/plugins/endpoint/server/routes/policy/handlers.test.ts create mode 100644 x-pack/plugins/endpoint/server/routes/policy/handlers.ts create mode 100644 x-pack/plugins/endpoint/server/routes/policy/index.ts create mode 100644 x-pack/plugins/endpoint/server/routes/policy/service.test.ts create mode 100644 x-pack/plugins/endpoint/server/routes/policy/service.ts create mode 100644 x-pack/test/api_integration/apis/endpoint/policy.ts create mode 100644 x-pack/test/functional/es_archives/endpoint/policy/data.json.gz create mode 100644 x-pack/test/functional/es_archives/endpoint/policy/mappings.json diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts index 88e1c66ea3e82..e1a2401849301 100644 --- a/x-pack/plugins/endpoint/common/generate_data.test.ts +++ b/x-pack/plugins/endpoint/common/generate_data.test.ts @@ -45,6 +45,17 @@ describe('data generator', () => { expect(metadata.host).not.toBeNull(); }); + it('creates policy response documents', () => { + const timestamp = new Date().getTime(); + const hostPolicyResponse = generator.generatePolicyResponse(timestamp); + expect(hostPolicyResponse['@timestamp']).toEqual(timestamp); + expect(hostPolicyResponse.event.created).toEqual(timestamp); + expect(hostPolicyResponse.endpoint).not.toBeNull(); + expect(hostPolicyResponse.agent).not.toBeNull(); + expect(hostPolicyResponse.host).not.toBeNull(); + expect(hostPolicyResponse.endpoint.policy.applied).not.toBeNull(); + }); + it('creates alert event documents', () => { const timestamp = new Date().getTime(); const alert = generator.generateAlert(timestamp); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 61fbb779986dc..3989f163c9ad4 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -12,9 +12,10 @@ import { Host, HostMetadata, HostOS, - PolicyData, HostPolicyResponse, + HostPolicyResponseActions, HostPolicyResponseActionStatus, + PolicyData, } from './types'; import { factory as policyFactory } from './models/policy_config'; @@ -136,6 +137,13 @@ export class EndpointDocGenerator { this.commonInfo.host.ip = this.randomArray(3, () => this.randomIP()); } + /** + * Creates new random policy id for the host to simulate new policy application + */ + public updatePolicyId() { + this.commonInfo.endpoint.policy.id = this.randomChoice(POLICIES).id; + } + private createHostData(): HostInfo { return { agent: { @@ -498,106 +506,145 @@ export class EndpointDocGenerator { /** * Generates a Host Policy response message */ - generatePolicyResponse(): HostPolicyResponse { + public generatePolicyResponse(ts = new Date().getTime()): HostPolicyResponse { + const policyVersion = this.seededUUIDv4(); return { - '@timestamp': new Date().toISOString(), + '@timestamp': ts, + agent: { + id: this.commonInfo.agent.id, + version: '1.0.0-local.20200416.0', + }, elastic: { agent: { - id: 'c2a9093e-e289-4c0a-aa44-8c32a414fa7a', + id: this.commonInfo.elastic.agent.id, }, }, ecs: { - version: '1.0.0', - }, - event: { - created: '2015-01-01T12:10:30Z', - kind: 'policy_response', + version: '1.4.0', }, - agent: { - version: '6.0.0-rc2', - id: '8a4f500d', + host: { + id: this.commonInfo.host.id, }, endpoint: { - artifacts: { - 'global-manifest': { - version: '1.2.3', - sha256: 'abcdef', - }, - 'endpointpe-v4-windows': { - version: '1.2.3', - sha256: 'abcdef', - }, - 'user-whitelist-windows': { - version: '1.2.3', - sha256: 'abcdef', - }, - 'global-whitelist-windows': { - version: '1.2.3', - sha256: 'abcdef', - }, - }, policy: { applied: { - version: '1.0.0', - id: '17d4b81d-9940-4b64-9de5-3e03ef1fb5cf', - status: HostPolicyResponseActionStatus.success, + actions: { + configure_elasticsearch_connection: { + message: 'elasticsearch comms configured successfully', + status: HostPolicyResponseActionStatus.success, + }, + configure_kernel: { + message: 'Failed to configure kernel', + status: HostPolicyResponseActionStatus.failure, + }, + configure_logging: { + message: 'Successfully configured logging', + status: HostPolicyResponseActionStatus.success, + }, + configure_malware: { + message: 'Unexpected error configuring malware', + status: HostPolicyResponseActionStatus.failure, + }, + connect_kernel: { + message: 'Successfully initialized minifilter', + status: HostPolicyResponseActionStatus.success, + }, + detect_file_open_events: { + message: 'Successfully stopped file open event reporting', + status: HostPolicyResponseActionStatus.success, + }, + detect_file_write_events: { + message: 'Failed to stop file write event reporting', + status: HostPolicyResponseActionStatus.success, + }, + detect_image_load_events: { + message: 'Successfuly started image load event reporting', + status: HostPolicyResponseActionStatus.success, + }, + detect_process_events: { + message: 'Successfully started process event reporting', + status: HostPolicyResponseActionStatus.success, + }, + download_global_artifacts: { + message: 'Failed to download EXE model', + status: HostPolicyResponseActionStatus.success, + }, + load_config: { + message: 'successfully parsed configuration', + status: HostPolicyResponseActionStatus.success, + }, + load_malware_model: { + message: 'Error deserializing EXE model; no valid malware model installed', + status: HostPolicyResponseActionStatus.success, + }, + read_elasticsearch_config: { + message: 'Successfully read Elasticsearch configuration', + status: HostPolicyResponseActionStatus.success, + }, + read_events_config: { + message: 'Successfully read events configuration', + status: HostPolicyResponseActionStatus.success, + }, + read_kernel_config: { + message: 'Succesfully read kernel configuration', + status: HostPolicyResponseActionStatus.success, + }, + read_logging_config: { + message: 'field (logging.debugview) not found in config', + status: HostPolicyResponseActionStatus.success, + }, + read_malware_config: { + message: 'Successfully read malware detect configuration', + status: HostPolicyResponseActionStatus.success, + }, + workflow: { + message: 'Failed to apply a portion of the configuration (kernel)', + status: HostPolicyResponseActionStatus.success, + }, + download_model: { + message: 'Failed to apply a portion of the configuration (kernel)', + status: HostPolicyResponseActionStatus.success, + }, + ingest_events_config: { + message: 'Failed to apply a portion of the configuration (kernel)', + status: HostPolicyResponseActionStatus.success, + }, + }, + id: this.commonInfo.endpoint.policy.id, + policy: { + id: this.commonInfo.endpoint.policy.id, + version: policyVersion, + }, response: { configurations: { - malware: { - status: HostPolicyResponseActionStatus.success, - concerned_actions: ['download_model', 'workflow', 'a_custom_future_action'], - }, events: { - status: HostPolicyResponseActionStatus.success, - concerned_actions: ['ingest_events_config', 'workflow'], + concerned_actions: this.randomHostPolicyResponseActions(), + status: this.randomHostPolicyResponseActionStatus(), }, logging: { - status: HostPolicyResponseActionStatus.success, - concerned_actions: ['configure_elasticsearch_connection'], - }, - streaming: { - status: HostPolicyResponseActionStatus.success, - concerned_actions: [ - 'detect_file_open_events', - 'download_global_artifacts', - 'a_custom_future_action', - ], - }, - }, - actions: { - download_model: { - status: HostPolicyResponseActionStatus.success, - message: 'model downloaded', + concerned_actions: this.randomHostPolicyResponseActions(), + status: this.randomHostPolicyResponseActionStatus(), }, - ingest_events_config: { - status: HostPolicyResponseActionStatus.success, - message: 'no action taken', - }, - workflow: { - status: HostPolicyResponseActionStatus.success, - message: 'the flow worked well', - }, - a_custom_future_action: { - status: HostPolicyResponseActionStatus.success, - message: 'future message', - }, - configure_elasticsearch_connection: { - status: HostPolicyResponseActionStatus.success, - message: 'some message', - }, - detect_file_open_events: { - status: HostPolicyResponseActionStatus.success, - message: 'some message', + malware: { + concerned_actions: this.randomHostPolicyResponseActions(), + status: this.randomHostPolicyResponseActionStatus(), }, - download_global_artifacts: { - status: HostPolicyResponseActionStatus.success, - message: 'some message', + streaming: { + concerned_actions: this.randomHostPolicyResponseActions(), + status: this.randomHostPolicyResponseActionStatus(), }, }, }, + status: this.randomHostPolicyResponseActionStatus(), + version: policyVersion, }, }, }, + event: { + created: ts, + id: this.seededUUIDv4(), + kind: 'policy_response', + }, }; } @@ -644,4 +691,32 @@ export class EndpointDocGenerator { private seededUUIDv4(): string { return uuid.v4({ random: [...this.randomNGenerator(255, 16)] }); } + + private randomHostPolicyResponseActions(): Array { + return this.randomArray(this.randomN(8), () => + this.randomChoice([ + 'load_config', + 'workflow', + 'download_global_artifacts', + 'configure_malware', + 'read_malware_config', + 'load_malware_model', + 'read_kernel_config', + 'configure_kernel', + 'detect_process_events', + 'detect_file_write_events', + 'detect_file_open_events', + 'detect_image_load_events', + 'connect_kernel', + ]) + ); + } + + private randomHostPolicyResponseActionStatus(): HostPolicyResponseActionStatus { + return this.randomChoice([ + HostPolicyResponseActionStatus.failure, + HostPolicyResponseActionStatus.success, + HostPolicyResponseActionStatus.warning, + ]); + } } diff --git a/x-pack/plugins/endpoint/common/schema/policy.ts b/x-pack/plugins/endpoint/common/schema/policy.ts new file mode 100644 index 0000000000000..17d0cdff57ee0 --- /dev/null +++ b/x-pack/plugins/endpoint/common/schema/policy.ts @@ -0,0 +1,12 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +export const GetPolicyResponseSchema = { + query: schema.object({ + hostId: schema.string(), + }), +}; diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 8773c5441860a..906d21f4b7ed5 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -588,7 +588,7 @@ export enum HostPolicyResponseActionStatus { /** * The details of a given action */ -interface HostPolicyResponseActionDetails { +export interface HostPolicyResponseActionDetails { status: HostPolicyResponseActionStatus; message: string; } @@ -596,7 +596,7 @@ interface HostPolicyResponseActionDetails { /** * A known list of possible Endpoint actions */ -interface HostPolicyResponseActions { +export interface HostPolicyResponseActions { download_model: HostPolicyResponseActionDetails; ingest_events_config: HostPolicyResponseActionDetails; workflow: HostPolicyResponseActionDetails; @@ -617,9 +617,6 @@ interface HostPolicyResponseActions { read_kernel_config: HostPolicyResponseActionDetails; read_logging_config: HostPolicyResponseActionDetails; read_malware_config: HostPolicyResponseActionDetails; - // The list of possible Actions will change rapidly, so the below entry will allow - // them without us defining them here statically - [key: string]: HostPolicyResponseActionDetails; } interface HostPolicyResponseConfigurationStatus { @@ -631,7 +628,7 @@ interface HostPolicyResponseConfigurationStatus { * Information about the applying of a policy to a given host */ export interface HostPolicyResponse { - '@timestamp': string; + '@timestamp': number; elastic: { agent: { id: string; @@ -640,21 +637,29 @@ export interface HostPolicyResponse { ecs: { version: string; }; + host: { + id: string; + }; event: { - created: string; + created: number; kind: string; + id: string; }; agent: { version: string; id: string; }; endpoint: { - artifacts: {}; policy: { applied: { version: string; id: string; status: HostPolicyResponseActionStatus; + actions: Partial; + policy: { + id: string; + version: string; + }; response: { configurations: { malware: HostPolicyResponseConfigurationStatus; @@ -662,7 +667,6 @@ export interface HostPolicyResponse { logging: HostPolicyResponseConfigurationStatus; streaming: HostPolicyResponseConfigurationStatus; }; - actions: Partial; }; }; }; diff --git a/x-pack/plugins/endpoint/scripts/policy_mapping.json b/x-pack/plugins/endpoint/scripts/policy_mapping.json new file mode 100644 index 0000000000000..1fdd5d140e0ba --- /dev/null +++ b/x-pack/plugins/endpoint/scripts/policy_mapping.json @@ -0,0 +1,398 @@ +{ + "mappings": { + "_meta": { + "version": "1.6.0-dev" + }, + "date_detection": false, + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "endpoint": { + "properties": { + "policy": { + "properties": { + "applied": { + "properties": { + "actions": { + "properties": { + "configure_elasticsearch_connection": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "configure_kernel": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "configure_logging": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "configure_malware": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "connect_kernel": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "detect_file_open_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "detect_file_write_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "detect_image_load_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "detect_process_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "download_global_artifacts": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "load_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "load_malware_model": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "read_elasticsearch_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "read_events_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "read_kernel_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "read_logging_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "read_malware_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "workflow": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + }, + "configurations": { + "properties": { + "events": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "logging": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "malware": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "streaming": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "policy": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "response": { + "type": "object" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + }, + "event": { + "properties": { + "created": { + "type": "date" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": 10000 + } + }, + "refresh_interval": "5s" + } + } +} diff --git a/x-pack/plugins/endpoint/scripts/resolver_generator.ts b/x-pack/plugins/endpoint/scripts/resolver_generator.ts index 2129bef0624b8..30752877db824 100644 --- a/x-pack/plugins/endpoint/scripts/resolver_generator.ts +++ b/x-pack/plugins/endpoint/scripts/resolver_generator.ts @@ -10,6 +10,7 @@ import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { EndpointDocGenerator, Event } from '../common/generate_data'; import { default as eventMapping } from './event_mapping.json'; import { default as alertMapping } from './alert_mapping.json'; +import { default as policyMapping } from './policy_mapping.json'; main(); @@ -44,6 +45,12 @@ async function main() { default: 'metrics-endpoint-default-1', type: 'string', }, + policyIndex: { + alias: 'pi', + describe: 'index to store host policy in', + default: 'metrics-endpoint.policy-default-1', + type: 'string', + }, auth: { describe: 'elasticsearch username and password, separated by a colon', type: 'string', @@ -90,6 +97,12 @@ async function main() { type: 'number', default: 1, }, + numDocs: { + alias: 'nd', + describe: 'number of metadata and policy response doc to generate per host', + type: 'number', + default: 5, + }, alertsPerHost: { alias: 'ape', describe: 'number of resolver trees to make for each host', @@ -123,7 +136,7 @@ async function main() { if (argv.delete) { try { await client.indices.delete({ - index: [argv.eventIndex, argv.metadataIndex, argv.alertIndex], + index: [argv.eventIndex, argv.metadataIndex, argv.alertIndex, argv.policyIndex], }); } catch (err) { if (err instanceof ResponseError && err.statusCode !== 404) { @@ -165,6 +178,7 @@ async function main() { await createIndex(client, argv.alertIndex, alertMapping); await createIndex(client, argv.eventIndex, eventMapping); + await createIndex(client, argv.policyIndex, policyMapping); if (argv.setupOnly) { process.exit(0); } @@ -179,14 +193,19 @@ async function main() { for (let i = 0; i < argv.numHosts; i++) { const generator = new EndpointDocGenerator(random); const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents - const numMetadataDocs = 5; + const timestamp = new Date().getTime(); - for (let j = 0; j < numMetadataDocs; j++) { + for (let j = 0; j < argv.numDocs; j++) { generator.updateHostData(); + generator.updatePolicyId(); await client.index({ index: argv.metadataIndex, - body: generator.generateHostMetadata( - timestamp - timeBetweenDocs * (numMetadataDocs - j - 1) + body: generator.generateHostMetadata(timestamp - timeBetweenDocs * (argv.numDocs - j - 1)), + }); + await client.index({ + index: argv.policyIndex, + body: generator.generatePolicyResponse( + timestamp - timeBetweenDocs * (argv.numDocs - j - 1) ), }); } diff --git a/x-pack/plugins/endpoint/server/index_pattern.ts b/x-pack/plugins/endpoint/server/index_pattern.ts index 903d48746bfb3..f4bb1460aee4b 100644 --- a/x-pack/plugins/endpoint/server/index_pattern.ts +++ b/x-pack/plugins/endpoint/server/index_pattern.ts @@ -11,6 +11,7 @@ export interface IndexPatternRetriever { getIndexPattern(ctx: RequestHandlerContext, datasetPath: string): Promise; getEventIndexPattern(ctx: RequestHandlerContext): Promise; getMetadataIndexPattern(ctx: RequestHandlerContext): Promise; + getPolicyResponseIndexPattern(ctx: RequestHandlerContext): Promise; } /** @@ -74,4 +75,8 @@ export class IngestIndexPatternRetriever implements IndexPatternRetriever { throw new Error(errMsg); } } + + getPolicyResponseIndexPattern(ctx: RequestHandlerContext): Promise { + return Promise.resolve('metrics-endpoint.policy-default-1'); + } } diff --git a/x-pack/plugins/endpoint/server/mocks.ts b/x-pack/plugins/endpoint/server/mocks.ts index 519ca15cf8427..76a3628562a82 100644 --- a/x-pack/plugins/endpoint/server/mocks.ts +++ b/x-pack/plugins/endpoint/server/mocks.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { + IScopedClusterClient, + RequestHandlerContext, + SavedObjectsClientContract, +} from 'kibana/server'; import { AgentService, IngestManagerStartContract } from '../../ingest_manager/server'; +import { IndexPatternRetriever } from './index_pattern'; /** * Creates a mock IndexPatternRetriever for use in tests. @@ -12,12 +18,13 @@ import { AgentService, IngestManagerStartContract } from '../../ingest_manager/s * @param indexPattern a string index pattern to return when any of the mock's public methods are called. * @returns the same string passed in via `indexPattern` */ -export const createMockIndexPatternRetriever = (indexPattern: string) => { +export const createMockIndexPatternRetriever = (indexPattern: string): IndexPatternRetriever => { const mockGetFunc = jest.fn().mockResolvedValue(indexPattern); return { getIndexPattern: mockGetFunc, getEventIndexPattern: mockGetFunc, getMetadataIndexPattern: mockGetFunc, + getPolicyResponseIndexPattern: mockGetFunc, }; }; @@ -56,3 +63,24 @@ export const createMockIngestManagerStartContract = ( agentService: createMockAgentService(), }; }; + +export function createRouteHandlerContext( + dataClient: jest.Mocked, + savedObjectsClient: jest.Mocked +) { + return ({ + core: { + elasticsearch: { + dataClient, + }, + savedObjects: { + client: savedObjectsClient, + }, + }, + /** + * Using unknown here because the object defined is not a full `RequestHandlerContext`. We don't + * need all of the fields required to run the tests, but the `routeHandler` function requires a + * `RequestHandlerContext`. + */ + } as unknown) as RequestHandlerContext; +} diff --git a/x-pack/plugins/endpoint/server/plugin.ts b/x-pack/plugins/endpoint/server/plugin.ts index f3cc569ad16a7..ff10b9c0416f9 100644 --- a/x-pack/plugins/endpoint/server/plugin.ts +++ b/x-pack/plugins/endpoint/server/plugin.ts @@ -16,6 +16,7 @@ import { registerEndpointRoutes } from './routes/metadata'; import { IngestIndexPatternRetriever } from './index_pattern'; import { IngestManagerStartContract } from '../../ingest_manager/server'; import { EndpointAppContextService } from './endpoint_app_context_services'; +import { registerPolicyRoutes } from './routes/policy'; export type EndpointPluginStart = void; export type EndpointPluginSetup = void; @@ -87,6 +88,7 @@ export class EndpointPlugin registerResolverRoutes(router, endpointContext); registerAlertRoutes(router, endpointContext); registerIndexPatternRoute(router, endpointContext); + registerPolicyRoutes(router, endpointContext); } public start(core: CoreStart, plugins: EndpointPluginStartDependencies) { diff --git a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts index 8f0c0b4c2efaf..5415ebcae31c4 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts @@ -9,7 +9,6 @@ import { IScopedClusterClient, KibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, SavedObjectsClientContract, } from 'kibana/server'; @@ -25,7 +24,11 @@ import { SearchResponse } from 'elasticsearch'; import { registerEndpointRoutes } from './index'; import { EndpointConfigSchema } from '../../config'; import * as data from '../../test_data/all_metadata_data.json'; -import { createMockAgentService, createMockMetadataIndexPatternRetriever } from '../../mocks'; +import { + createMockAgentService, + createMockMetadataIndexPatternRetriever, + createRouteHandlerContext, +} from '../../mocks'; import { AgentService } from '../../../../ingest_manager/server'; import Boom from 'boom'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; @@ -66,27 +69,6 @@ describe('test endpoint route', () => { afterEach(() => endpointAppContextService.stop()); - function createRouteHandlerContext( - dataClient: jest.Mocked, - savedObjectsClient: jest.Mocked - ) { - return ({ - core: { - elasticsearch: { - dataClient, - }, - savedObjects: { - client: savedObjectsClient, - }, - }, - /** - * Using unknown here because the object defined is not a full `RequestHandlerContext`. We don't - * need all of the fields required to run the tests, but the `routeHandler` function requires a - * `RequestHandlerContext`. - */ - } as unknown) as RequestHandlerContext; - } - it('test find the latest of all endpoints', async () => { const mockRequest = httpServerMock.createKibanaRequest({}); diff --git a/x-pack/plugins/endpoint/server/routes/policy/handlers.test.ts b/x-pack/plugins/endpoint/server/routes/policy/handlers.test.ts new file mode 100644 index 0000000000000..9348353425370 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/policy/handlers.test.ts @@ -0,0 +1,138 @@ +/* + * 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 { EndpointAppContextService } from '../../endpoint_app_context_services'; +import { + createMockAgentService, + createMockIndexPatternRetriever, + createRouteHandlerContext, +} from '../../mocks'; +import { getHostPolicyResponseHandler } from './handlers'; +import { EndpointConfigSchema } from '../../config'; +import { + IScopedClusterClient, + KibanaResponseFactory, + SavedObjectsClientContract, +} from 'kibana/server'; +import { + elasticsearchServiceMock, + httpServerMock, + loggingServiceMock, + savedObjectsClientMock, +} from '../../../../../../src/core/server/mocks'; +import { AgentService } from '../../../../ingest_manager/server/services'; +import { SearchResponse } from 'elasticsearch'; +import { GetHostPolicyResponse, HostPolicyResponse } from '../../../common/types'; +import { EndpointDocGenerator } from '../../../common/generate_data'; + +describe('test policy response handler', () => { + let endpointAppContextService: EndpointAppContextService; + let mockScopedClient: jest.Mocked; + let mockSavedObjectClient: jest.Mocked; + let mockResponse: jest.Mocked; + let mockAgentService: jest.Mocked; + + beforeEach(() => { + mockScopedClient = elasticsearchServiceMock.createScopedClusterClient(); + mockSavedObjectClient = savedObjectsClientMock.create(); + mockResponse = httpServerMock.createResponseFactory(); + endpointAppContextService = new EndpointAppContextService(); + mockAgentService = createMockAgentService(); + endpointAppContextService.start({ + indexPatternRetriever: createMockIndexPatternRetriever('metrics-endpoint-policy-*'), + agentService: mockAgentService, + }); + }); + + afterEach(() => endpointAppContextService.stop()); + + it('should return the latest policy response for a host', async () => { + const response = createSearchResponse(new EndpointDocGenerator().generatePolicyResponse()); + const hostPolicyResponseHandler = getHostPolicyResponseHandler({ + logFactory: loggingServiceMock.create(), + service: endpointAppContextService, + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => Promise.resolve(response)); + const mockRequest = httpServerMock.createKibanaRequest({ + params: { hostId: 'id' }, + }); + + await hostPolicyResponseHandler( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + mockRequest, + mockResponse + ); + + expect(mockResponse.ok).toBeCalled(); + const result = mockResponse.ok.mock.calls[0][0]?.body as GetHostPolicyResponse; + expect(result.policy_response.host.id).toEqual(response.hits.hits[0]._source.host.id); + }); + + it('should return not found when there is no response policy for host', async () => { + const hostPolicyResponseHandler = getHostPolicyResponseHandler({ + logFactory: loggingServiceMock.create(), + service: endpointAppContextService, + config: () => Promise.resolve(EndpointConfigSchema.validate({})), + }); + + mockScopedClient.callAsCurrentUser.mockImplementationOnce(() => + Promise.resolve(createSearchResponse()) + ); + + const mockRequest = httpServerMock.createKibanaRequest({ + params: { hostId: 'id' }, + }); + + await hostPolicyResponseHandler( + createRouteHandlerContext(mockScopedClient, mockSavedObjectClient), + mockRequest, + mockResponse + ); + + expect(mockResponse.notFound).toBeCalled(); + const message = mockResponse.notFound.mock.calls[0][0]?.body; + expect(message).toEqual('Policy Response Not Found'); + }); +}); + +/** + * Create a SearchResponse with the hostPolicyResponse provided, else return an empty + * SearchResponse + * @param hostPolicyResponse + */ +function createSearchResponse( + hostPolicyResponse?: HostPolicyResponse +): SearchResponse { + return ({ + took: 15, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 5, + relation: 'eq', + }, + max_score: null, + hits: hostPolicyResponse + ? [ + { + _index: 'metrics-endpoint.policy-default-1', + _id: '8FhM0HEBYyRTvb6lOQnw', + _score: null, + _source: hostPolicyResponse, + sort: [1588337587997], + }, + ] + : [], + }, + } as unknown) as SearchResponse; +} diff --git a/x-pack/plugins/endpoint/server/routes/policy/handlers.ts b/x-pack/plugins/endpoint/server/routes/policy/handlers.ts new file mode 100644 index 0000000000000..5a34164c0bb37 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/policy/handlers.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { RequestHandler } from 'kibana/server'; +import { TypeOf } from '@kbn/config-schema'; +import { GetPolicyResponseSchema } from '../../../common/schema/policy'; +import { EndpointAppContext } from '../../types'; +import { getPolicyResponseByHostId } from './service'; + +export const getHostPolicyResponseHandler = function( + endpointAppContext: EndpointAppContext +): RequestHandler, undefined> { + return async (context, request, response) => { + try { + const index = await endpointAppContext.service + .getIndexPatternRetriever() + .getPolicyResponseIndexPattern(context); + + const doc = await getPolicyResponseByHostId( + index, + request.query.hostId, + context.core.elasticsearch.dataClient + ); + + if (doc) { + return response.ok({ body: doc }); + } + + return response.notFound({ body: 'Policy Response Not Found' }); + } catch (err) { + return response.internalError({ body: err }); + } + }; +}; diff --git a/x-pack/plugins/endpoint/server/routes/policy/index.ts b/x-pack/plugins/endpoint/server/routes/policy/index.ts new file mode 100644 index 0000000000000..4c3bd8e21315c --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/policy/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { EndpointAppContext } from '../../types'; +import { GetPolicyResponseSchema } from '../../../common/schema/policy'; +import { getHostPolicyResponseHandler } from './handlers'; + +export const BASE_POLICY_RESPONSE_ROUTE = `/api/endpoint/policy_response`; + +export function registerPolicyRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { + router.get( + { + path: BASE_POLICY_RESPONSE_ROUTE, + validate: GetPolicyResponseSchema, + options: { authRequired: true }, + }, + getHostPolicyResponseHandler(endpointAppContext) + ); +} diff --git a/x-pack/plugins/endpoint/server/routes/policy/service.test.ts b/x-pack/plugins/endpoint/server/routes/policy/service.test.ts new file mode 100644 index 0000000000000..c7bf65627769e --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/policy/service.test.ts @@ -0,0 +1,19 @@ +/* + * 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 { GetPolicyResponseSchema } from '../../../common/schema/policy'; + +describe('test policy handlers schema', () => { + it('validate that get policy response query schema', async () => { + expect( + GetPolicyResponseSchema.query.validate({ + hostId: 'id', + }) + ).toBeTruthy(); + + expect(() => GetPolicyResponseSchema.query.validate({})).toThrowError(); + }); +}); diff --git a/x-pack/plugins/endpoint/server/routes/policy/service.ts b/x-pack/plugins/endpoint/server/routes/policy/service.ts new file mode 100644 index 0000000000000..7ec2c65634110 --- /dev/null +++ b/x-pack/plugins/endpoint/server/routes/policy/service.ts @@ -0,0 +1,49 @@ +/* + * 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 { SearchResponse } from 'elasticsearch'; +import { IScopedClusterClient } from 'kibana/server'; +import { GetHostPolicyResponse, HostPolicyResponse } from '../../../common/types'; + +export function getESQueryPolicyResponseByHostID(hostID: string, index: string) { + return { + body: { + query: { + match: { + 'host.id': hostID, + }, + }, + sort: [ + { + 'event.created': { + order: 'desc', + }, + }, + ], + size: 1, + }, + index, + }; +} + +export async function getPolicyResponseByHostId( + index: string, + hostId: string, + dataClient: IScopedClusterClient +): Promise { + const query = getESQueryPolicyResponseByHostID(hostId, index); + const response = (await dataClient.callAsCurrentUser('search', query)) as SearchResponse< + HostPolicyResponse + >; + + if (response.hits.hits.length === 0) { + return undefined; + } + + return { + policy_response: response.hits.hits[0]._source, + }; +} diff --git a/x-pack/test/api_integration/apis/endpoint/index.ts b/x-pack/test/api_integration/apis/endpoint/index.ts index 0a5f9aa595b8a..0c7c03da6c594 100644 --- a/x-pack/test/api_integration/apis/endpoint/index.ts +++ b/x-pack/test/api_integration/apis/endpoint/index.ts @@ -20,5 +20,6 @@ export default function endpointAPIIntegrationTests({ loadTestFile(require.resolve('./resolver')); loadTestFile(require.resolve('./metadata')); loadTestFile(require.resolve('./alerts')); + loadTestFile(require.resolve('./policy')); }); } diff --git a/x-pack/test/api_integration/apis/endpoint/policy.ts b/x-pack/test/api_integration/apis/endpoint/policy.ts new file mode 100644 index 0000000000000..76e4fe1a00ca4 --- /dev/null +++ b/x-pack/test/api_integration/apis/endpoint/policy.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 expect from '@kbn/expect/expect.js'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + describe('Endpoint policy api', () => { + describe('GET /api/endpoint/policy_response', () => { + before(async () => await esArchiver.load('endpoint/policy')); + + after(async () => await esArchiver.unload('endpoint/policy')); + + it('should return one policy response for host', async () => { + const expectedHostId = '4f3b9858-a96d-49d8-a326-230d7763d767'; + const { body } = await supertest + .get(`/api/endpoint/policy_response?hostId=${expectedHostId}`) + .send() + .expect(200); + + expect(body.policy_response.host.id).to.eql(expectedHostId); + expect(body.policy_response.endpoint.policy).to.not.be(undefined); + }); + + it('should return not found if host has no policy response', async () => { + const { body } = await supertest + .get(`/api/endpoint/policy_response?hostId=bad_host_id`) + .send() + .expect(404); + + expect(body.message).to.contain('Policy Response Not Found'); + }); + }); + }); +} diff --git a/x-pack/test/functional/es_archives/endpoint/policy/data.json.gz b/x-pack/test/functional/es_archives/endpoint/policy/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..2fab424d27cad68350e8ea2544f0005c44e6a688 GIT binary patch literal 1329 zcmV-11QOZ*BnXSxax@HW0q&R}ebw0->P?^=dD5Q|Gvc zb_=v93WFjkhZ|WGB-z>QBLBUlB-?uWk;rX;>atiY)INGR{@A8NI$*KDK>HbS%Sc{+keE#+GHgKX}K7V;A#Fa!$*;ggBW_ij<23brK z!Q*_Eh=`MCNLh$CQ4WorMkY2XIqV@jEh*Q8Z&*S!$=Su%}m|Yb!0d* zU8`hY_IopuHFs{wgYF!`pUXXwW*l;{(_|$_a^a=Es??Tr!O;`7s=XjyE zS9|I1R|UPfVVQyUjyOpo&S+~{eIU8?D(ifZpNNPwl8XZ{P^lh86iPiQdkLaQtX4c;orLsTEiCnL?excv)EY-mIAtes{T{QgiOjr& z!qQZvdu$RE*clluXsX7}O=2TrJmxva5&tG}uH}!AM>$JJiW+6P!X<68rAS!3WLtT_ z`_9i1gjp_NE_HZZ^)DmZ5;JQHhTJA5838t5a zRYeu2sx}KNKv}si+9VZ9{*@I_Vy$dk3#fRCi)gMQ5#VSk_vE4M_?HyG&IVZgu>fm9 zPxZyi!IehON2KnuXMjNwD-i<`g8YH)n-MM+Ka`#0<49m-PUK^yO7*hb>YYO_VtXdi z`b7*9oMsYs1KgOHs+;1>yf2EIhO&&MMN?OI&At?W1hD|}`O4|;{wdex9U##~GWsI{ zPnqgT?Pt(Ap=}&{WdC(C}2jM&N52jx-Ie^JBi_S=@g8#>rw=N=Yp+aU>w%vWBJ zUA?^Gfkq5W*Js#<*q|69)3zWcCWfwyDMF@8ZR*r%#77<%dS!=S?z?CQ(}T!*c}o;p zf$u5a(sw9CiuS{n=|I!csp~oxbq{`*o~nOeZeDI~l&y1_+o;9dep`LIE_3VH*2LUS z%x%rm#N1BI?Zn)U19oC=C+2oyZeI&_Vs6L7G%>dmb2~A&W8s{b+y5JLtLt9in{s3f zFp={=WhV4&CxC7c8rVXn8yLEF)VX)b-1bO}O2Car$o90abNRPxJ2@V)x<9i6uJr`r z70zA68jnn&)(6Zp*y8%MclKWy1_Vz9cW@pWwSF#Yh+Tuj%TS$yn>RcVY|J7O nh^I(d6C&NVAtr$dnWftd6US$vQKt$2LuU3LlvaA7Q!4-f3D}nT literal 0 HcmV?d00001 diff --git a/x-pack/test/functional/es_archives/endpoint/policy/mappings.json b/x-pack/test/functional/es_archives/endpoint/policy/mappings.json new file mode 100644 index 0000000000000..4a9b0bdd11f0b --- /dev/null +++ b/x-pack/test/functional/es_archives/endpoint/policy/mappings.json @@ -0,0 +1,512 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "metrics-endpoint.policy-default-1", + "mappings": { + "_meta": { + "version": "1.6.0-dev" + }, + "date_detection": false, + "dynamic_templates": [ + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "elastic": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "endpoint": { + "properties": { + "policy": { + "properties": { + "applied": { + "properties": { + "actions": { + "properties": { + "configure_elasticsearch_connection": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "configure_kernel": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "configure_logging": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "configure_malware": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "connect_kernel": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "detect_file_open_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "detect_file_write_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "detect_image_load_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "detect_process_events": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "download_global_artifacts": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "download_model": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ingest_events_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "load_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "load_malware_model": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "read_elasticsearch_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "read_events_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "read_kernel_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "read_logging_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "read_malware_config": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "workflow": { + "properties": { + "message": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "configurations": { + "properties": { + "events": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "logging": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "streaming": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "policy": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "configurations": { + "properties": { + "events": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "logging": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "malware": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "streaming": { + "properties": { + "concerned_actions": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "event": { + "properties": { + "created": { + "type": "date" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stream": { + "properties": { + "dataset": { + "type": "constant_keyword" + }, + "namespace": { + "type": "constant_keyword" + }, + "type": { + "type": "constant_keyword" + } + } + } + } + }, + "settings": { + "index": { + "codec": "best_compression", + "lifecycle": { + "name": "metrics-default" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "prefer_v2_templates": "true", + "query": { + "default_field": [ + "message" + ] + }, + "refresh_interval": "5s" + } + } + } +} From 449f52a9a0def0d77a396a7fe1e025439ced36c2 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Tue, 5 May 2020 03:10:13 +0200 Subject: [PATCH 18/55] Fix issue with navigating in firefox/safari (#65163) (#65190) --- src/plugins/visualize/public/application/legacy_app.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/visualize/public/application/legacy_app.js b/src/plugins/visualize/public/application/legacy_app.js index c7cc11c1f3ff5..e14057cab6ca9 100644 --- a/src/plugins/visualize/public/application/legacy_app.js +++ b/src/plugins/visualize/public/application/legacy_app.js @@ -160,10 +160,19 @@ export function initVisualizeApp(app, deps) { ); } + // This delay is needed to prevent some navigation issues in Firefox/Safari. + // see https://github.com/elastic/kibana/issues/65161 + const delay = res => { + return new Promise(resolve => { + setTimeout(() => resolve(res), 0); + }); + }; + return data.indexPatterns .ensureDefaultIndexPattern(history) .then(() => savedVisualizations.get($route.current.params)) .then(getResolvedResults(deps)) + .then(delay) .catch( redirectWhenMissing({ history, From 6ebf8523f8c7aabe329cde430fa50f0f51e3513a Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Mon, 4 May 2020 20:21:30 -0500 Subject: [PATCH 19/55] Remove references to the actionsClient in our routes (#64966) (#65196) All of our actions management is happening via the triggers_actions_ui plugin, so this code is (and will be) unused. Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../routes/__mocks__/request_context.ts | 3 --- .../routes/rules/add_prepackaged_rules_route.ts | 15 +++------------ .../routes/rules/create_rules_bulk_route.ts | 4 +--- .../routes/rules/create_rules_route.ts | 4 +--- .../routes/rules/delete_rules_bulk_route.ts | 4 +--- .../routes/rules/delete_rules_route.ts | 4 +--- .../routes/rules/import_rules_route.test.ts | 9 +-------- .../routes/rules/import_rules_route.ts | 5 +---- .../routes/rules/patch_rules_bulk_route.ts | 4 +--- .../routes/rules/patch_rules_route.ts | 4 +--- .../routes/rules/update_rules_bulk_route.ts | 4 +--- .../routes/rules/update_rules_route.ts | 4 +--- .../detection_engine/rules/create_rules.test.ts | 4 ---- .../detection_engine/rules/delete_rules.test.ts | 9 --------- .../lib/detection_engine/rules/delete_rules.ts | 7 +------ .../rules/install_prepacked_rules.ts | 3 --- .../detection_engine/rules/patch_rules.test.ts | 6 ------ .../lib/detection_engine/rules/patch_rules.ts | 1 - .../server/lib/detection_engine/rules/types.ts | 2 -- .../rules/update_prepacked_rules.test.ts | 4 ---- .../rules/update_prepacked_rules.ts | 3 --- .../detection_engine/rules/update_rules.test.ts | 6 ------ .../lib/detection_engine/rules/update_rules.ts | 1 - 23 files changed, 14 insertions(+), 96 deletions(-) diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts index f3b4068f6dd2d..5f764e1f371ae 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -11,12 +11,10 @@ import { savedObjectsClientMock, } from '../../../../../../../../src/core/server/mocks'; import { alertsClientMock } from '../../../../../../alerting/server/mocks'; -import { actionsClientMock } from '../../../../../../actions/server/mocks'; import { licensingMock } from '../../../../../../licensing/server/mocks'; import { siemMock } from '../../../../mocks'; const createMockClients = () => ({ - actionsClient: actionsClientMock.create(), alertsClient: alertsClientMock.create(), clusterClient: elasticsearchServiceMock.createScopedClusterClient(), licensing: { license: licensingMock.createLicenseMock() }, @@ -29,7 +27,6 @@ const createRequestContextMock = ( ) => { const coreContext = coreMock.createRequestHandlerContext(); return ({ - actions: { getActionsClient: jest.fn(() => clients.actionsClient) }, alerting: { getAlertsClient: jest.fn(() => clients.alertsClient) }, core: { ...coreContext, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 3c6adce45f959..ff57ec969997e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -34,12 +34,11 @@ export const addPrepackedRulesRoute = (router: IRouter) => { try { const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -62,16 +61,8 @@ export const addPrepackedRulesRoute = (router: IRouter) => { }); } } - await Promise.all( - installPrepackagedRules(alertsClient, actionsClient, rulesToInstall, signalsIndex) - ); - await updatePrepackagedRules( - alertsClient, - actionsClient, - savedObjectsClient, - rulesToUpdate, - signalsIndex - ); + await Promise.all(installPrepackagedRules(alertsClient, rulesToInstall, signalsIndex)); + await updatePrepackagedRules(alertsClient, savedObjectsClient, rulesToUpdate, signalsIndex); const prepackagedRulesOutput: PrePackagedRulesSchema = { rules_installed: rulesToInstall.length, rules_updated: rulesToUpdate.length, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 133c98a6af7b3..cf841a9c88b32 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -39,12 +39,11 @@ export const createRulesBulkRoute = (router: IRouter) => { async (context, request, response) => { const siemResponse = buildSiemResponse(response); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -113,7 +112,6 @@ export const createRulesBulkRoute = (router: IRouter) => { } const createdRule = await createRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index 9f1cddb2051c9..6605b5abfcb09 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -72,12 +72,11 @@ export const createRulesRoute = (router: IRouter): void => { try { validateLicenseForRuleType({ license: context.licensing.license, ruleType: type }); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -100,7 +99,6 @@ export const createRulesRoute = (router: IRouter): void => { } const createdRule = await createRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index b35ba27ef3561..9e9c272cec34c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -34,10 +34,9 @@ export const deleteRulesBulkRoute = (router: IRouter) => { const siemResponse = buildSiemResponse(response); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!actionsClient || !alertsClient) { + if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -49,7 +48,6 @@ export const deleteRulesBulkRoute = (router: IRouter) => { const idOrRuleIdOrUnknown = id ?? ruleId ?? '(unknown id)'; try { const rule = await deleteRules({ - actionsClient, alertsClient, id, ruleId, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts index 2288633ee8d2e..a614a165b3260 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -34,16 +34,14 @@ export const deleteRulesRoute = (router: IRouter) => { const { id, rule_id: ruleId } = request.query; const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!actionsClient || !alertsClient) { + if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); } const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const rule = await deleteRules({ - actionsClient, alertsClient, id, ruleId, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index 1233e01a67762..e7db228225880 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -60,7 +60,7 @@ describe('import_rules_route', () => { importRulesRoute(server.router, config); }); - describe('status codes with actionsClient and alertClient', () => { + describe('status codes', () => { test('returns 200 when importing a single rule with a valid actionClient and alertClient', async () => { const response = await server.inject(request, context); @@ -74,13 +74,6 @@ describe('import_rules_route', () => { expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); }); - test('returns 404 if actionsClient is not available on the route', async () => { - context.actions!.getActionsClient = jest.fn(); - const response = await server.inject(request, context); - expect(response.status).toEqual(404); - expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); - }); - it('returns 404 if siem client is unavailable', async () => { const { siem, ...contextWithoutSiem } = context; const response = await server.inject(request, contextWithoutSiem); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 202252da293ee..4d86f0bec6502 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -59,12 +59,11 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => { try { const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const clusterClient = context.core.elasticsearch.dataClient; const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -165,7 +164,6 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => { if (rule == null) { await createRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, @@ -202,7 +200,6 @@ export const importRulesRoute = (router: IRouter, config: ConfigType) => { } else if (rule != null && request.query.overwrite) { await patchRules({ alertsClient, - actionsClient, savedObjectsClient, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 534253db65d78..201e1f823b4cb 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -36,10 +36,9 @@ export const patchRulesBulkRoute = (router: IRouter) => { const siemResponse = buildSiemResponse(response); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!actionsClient || !alertsClient) { + if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -87,7 +86,6 @@ export const patchRulesBulkRoute = (router: IRouter) => { const rule = await patchRules({ alertsClient, - actionsClient, description, enabled, falsePositives, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index f7932cb016ba7..00ccd3059b38d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -73,16 +73,14 @@ export const patchRulesRoute = (router: IRouter) => { } const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; - if (!actionsClient || !alertsClient) { + if (!alertsClient) { return siemResponse.error({ statusCode: 404 }); } const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); const rule = await patchRules({ - actionsClient, alertsClient, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index f929f2fb3f649..6d8f2243787e8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -36,11 +36,10 @@ export const updateRulesBulkRoute = (router: IRouter) => { const siemResponse = buildSiemResponse(response); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } @@ -88,7 +87,6 @@ export const updateRulesBulkRoute = (router: IRouter) => { const rule = await updateRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index dedc2c914410a..bfbeef8be2fea 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -72,19 +72,17 @@ export const updateRulesRoute = (router: IRouter) => { validateLicenseForRuleType({ license: context.licensing.license, ruleType: type }); const alertsClient = context.alerting?.getAlertsClient(); - const actionsClient = context.actions?.getActionsClient(); const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.siem?.getSiemClient(); const ruleStatusClient = ruleStatusSavedObjectsClientFactory(savedObjectsClient); - if (!siemClient || !actionsClient || !alertsClient) { + if (!siemClient || !alertsClient) { return siemResponse.error({ statusCode: 404 }); } const finalIndex = outputIndex ?? siemClient.getSignalsIndex(); const rule = await updateRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts index 6710bf02aeb2b..f4f0a8042d0a5 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts @@ -5,16 +5,13 @@ */ import { alertsClientMock } from '../../../../../alerting/server/mocks'; -import { actionsClientMock } from '../../../../../actions/server/mocks'; import { getMlResult } from '../routes/__mocks__/request_responses'; import { createRules } from './create_rules'; describe('createRules', () => { - let actionsClient: ReturnType; let alertsClient: ReturnType; beforeEach(() => { - actionsClient = actionsClientMock.create(); alertsClient = alertsClientMock.create(); }); @@ -27,7 +24,6 @@ describe('createRules', () => { await createRules({ alertsClient, - actionsClient, ...params, ruleId: 'new-rule-id', enabled: true, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts index 68d01356a333a..6bc5fc2a88b6d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.test.ts @@ -4,20 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { actionsClientMock } from '../../../../../actions/server/mocks'; import { alertsClientMock } from '../../../../../alerting/server/mocks'; import { deleteRules } from './delete_rules'; import { readRules } from './read_rules'; jest.mock('./read_rules'); describe('deleteRules', () => { - let actionsClient: ReturnType; let alertsClient: ReturnType; const notificationId = 'notification-52128c15-0d1b-4716-a4c5-46997ac7f3bd'; const ruleId = 'rule-04128c15-0d1b-4716-a4c5-46997ac7f3bd'; beforeEach(() => { - actionsClient = actionsClientMock.create(); alertsClient = alertsClientMock.create(); }); @@ -26,7 +23,6 @@ describe('deleteRules', () => { const result = await deleteRules({ alertsClient, - actionsClient, id: notificationId, ruleId, }); @@ -41,7 +37,6 @@ describe('deleteRules', () => { const result = await deleteRules({ alertsClient, - actionsClient, id: notificationId, ruleId, }); @@ -61,7 +56,6 @@ describe('deleteRules', () => { const result = await deleteRules({ alertsClient, - actionsClient, id: notificationId, ruleId: null, }); @@ -87,7 +81,6 @@ describe('deleteRules', () => { const result = await deleteRules({ alertsClient, - actionsClient, id: notificationId, ruleId: null, }); @@ -117,7 +110,6 @@ describe('deleteRules', () => { try { await deleteRules({ alertsClient, - actionsClient, id: notificationId, ruleId: null, }); @@ -140,7 +132,6 @@ describe('deleteRules', () => { const result = await deleteRules({ alertsClient, - actionsClient, id: undefined, ruleId: null, }); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.ts index c3ca1d79424cf..92a80bc50afc6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/delete_rules.ts @@ -7,12 +7,7 @@ import { readRules } from './read_rules'; import { DeleteRuleParams } from './types'; -export const deleteRules = async ({ - alertsClient, - actionsClient, // TODO: Use this when we have actions such as email, etc... - id, - ruleId, -}: DeleteRuleParams) => { +export const deleteRules = async ({ alertsClient, id, ruleId }: DeleteRuleParams) => { const rule = await readRules({ alertsClient, id, ruleId }); if (rule == null) { return null; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index 3d3ed52b2feb2..0266d702b3dcc 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -5,14 +5,12 @@ */ import { Alert } from '../../../../../alerting/common'; -import { ActionsClient } from '../../../../../actions/server'; import { AlertsClient } from '../../../../../alerting/server'; import { createRules } from './create_rules'; import { PrepackagedRules } from '../types'; export const installPrepackagedRules = ( alertsClient: AlertsClient, - actionsClient: ActionsClient, rules: PrepackagedRules[], outputIndex: string ): Array> => @@ -52,7 +50,6 @@ export const installPrepackagedRules = ( ...acc, createRules({ alertsClient, - actionsClient, anomalyThreshold, description, enabled, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts index f93b0aceb5e6e..c551eb164ee07 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.test.ts @@ -6,17 +6,14 @@ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { alertsClientMock } from '../../../../../alerting/server/mocks'; -import { actionsClientMock } from '../../../../../actions/server/mocks'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { patchRules } from './patch_rules'; describe('patchRules', () => { - let actionsClient: ReturnType; let alertsClient: ReturnType; let savedObjectsClient: ReturnType; beforeEach(() => { - actionsClient = actionsClientMock.create(); alertsClient = alertsClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); }); @@ -27,7 +24,6 @@ describe('patchRules', () => { await patchRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, @@ -53,7 +49,6 @@ describe('patchRules', () => { await patchRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, @@ -80,7 +75,6 @@ describe('patchRules', () => { await patchRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...params, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index 85b13ed9cf4ed..da5e90ec14b0b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -14,7 +14,6 @@ import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_save export const patchRules = async ({ alertsClient, - actionsClient, // TODO: Use this whenever we add feature support for different action types savedObjectsClient, description, falsePositives, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts index 6fde199e0ba7d..b5dbfc92cf528 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -16,7 +16,6 @@ import { import { AlertsClient, PartialAlert } from '../../../../../alerting/server'; import { Alert } from '../../../../../alerting/common'; import { SIGNALS_ID } from '../../../../common/constants'; -import { ActionsClient } from '../../../../../actions/server'; import { RuleAlertParams, RuleTypeParams, RuleAlertParamsRest } from '../types'; export type PatchRuleAlertParamsRest = Partial & { @@ -139,7 +138,6 @@ export interface FindRulesStatusesRequestParams { export interface Clients { alertsClient: AlertsClient; - actionsClient: ActionsClient; } export type PatchRuleParams = Partial> & { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index a9bbf75883d1f..e8fb4fa96ab51 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -6,19 +6,16 @@ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { alertsClientMock } from '../../../../../alerting/server/mocks'; -import { actionsClientMock } from '../../../../../actions/server/mocks'; import { mockPrepackagedRule } from '../routes/__mocks__/request_responses'; import { updatePrepackagedRules } from './update_prepacked_rules'; import { patchRules } from './patch_rules'; jest.mock('./patch_rules'); describe('updatePrepackagedRules', () => { - let actionsClient: ReturnType; let alertsClient: ReturnType; let savedObjectsClient: ReturnType; beforeEach(() => { - actionsClient = actionsClientMock.create(); alertsClient = alertsClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); }); @@ -37,7 +34,6 @@ describe('updatePrepackagedRules', () => { await updatePrepackagedRules( alertsClient, - actionsClient, savedObjectsClient, [{ ...prepackagedRule, actions }], outputIndex diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts index b72b232c27f03..4c183c51d16ea 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -5,14 +5,12 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import { ActionsClient } from '../../../../../actions/server'; import { AlertsClient } from '../../../../../alerting/server'; import { patchRules } from './patch_rules'; import { PrepackagedRules } from '../types'; export const updatePrepackagedRules = async ( alertsClient: AlertsClient, - actionsClient: ActionsClient, savedObjectsClient: SavedObjectsClientContract, rules: PrepackagedRules[], outputIndex: string @@ -48,7 +46,6 @@ export const updatePrepackagedRules = async ( // or enable rules on the user when they were not expecting it if a rule updates return patchRules({ alertsClient, - actionsClient, description, falsePositives, from, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts index 2565d269db478..13c601b40e4f1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts @@ -6,17 +6,14 @@ import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { alertsClientMock } from '../../../../../alerting/server/mocks'; -import { actionsClientMock } from '../../../../../actions/server/mocks'; import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; import { updateRules } from './update_rules'; describe('updateRules', () => { - let actionsClient: ReturnType; let alertsClient: ReturnType; let savedObjectsClient: ReturnType; beforeEach(() => { - actionsClient = actionsClientMock.create(); alertsClient = alertsClientMock.create(); savedObjectsClient = savedObjectsClientMock.create(); }); @@ -27,7 +24,6 @@ describe('updateRules', () => { await updateRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, @@ -54,7 +50,6 @@ describe('updateRules', () => { await updateRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...rule.params, @@ -83,7 +78,6 @@ describe('updateRules', () => { await updateRules({ alertsClient, - actionsClient, savedObjectsClient, id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', ...params, diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 29c2cfdf91076..711f019458096 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -15,7 +15,6 @@ import { ruleStatusSavedObjectsClientFactory } from '../signals/rule_status_save export const updateRules = async ({ alertsClient, - actionsClient, // TODO: Use this whenever we add feature support for different action types savedObjectsClient, description, falsePositives, From 423863e8a8b93fa51e38719d8de6e47e8fa89035 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 4 May 2020 18:30:23 -0700 Subject: [PATCH 20/55] [Fleet] Agent details page UI (#64983) (#65200) * Fix empty host name column in agent list * Fix empty version column in agent list * Consolidate page header styling inconsistencies * Add tabs to agent details * Add right-side header content and actions menu * Give headers more spacing when there are tabs present * Add details tab * Use ECS formatted metadata * Make activity log table pretty * Return agent event SO id from list API * Fix i18n * Add types for new agent events and differentiate from stored agent events * Adjust test Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- .../common/types/models/agent.ts | 8 +- .../common/types/rest_spec/agent.ts | 12 +- .../ingest_manager/components/header.tsx | 1 + .../ingest_manager/components/loading.tsx | 5 +- .../ingest_manager/components/search_bar.tsx | 16 +- .../components/layout.tsx | 2 +- .../agent_config/details_page/index.tsx | 75 +++--- .../sections/epm/screens/home/header.tsx | 2 +- .../components/actions_menu.tsx | 83 ++++++ .../components/agent_details.tsx | 85 ++++++ .../components/agent_events_table.tsx | 117 ++++++-- .../components/details_section.tsx | 206 -------------- .../agent_details_page/components/index.ts | 3 +- .../components/type_labels.tsx | 122 +++++++++ .../fleet/agent_details_page/index.tsx | 251 +++++++++++++++--- .../sections/fleet/agent_list_page/index.tsx | 8 +- .../sections/fleet/components/index.tsx | 2 + .../ingest_manager/sections/fleet/index.tsx | 2 +- .../server/services/agents/checkin.ts | 13 +- .../server/services/agents/events.ts | 1 + .../ingest_manager/server/types/index.tsx | 1 + .../server/types/models/agent.ts | 5 + .../server/types/rest_spec/agent.ts | 9 +- .../translations/translations/ja-JP.json | 7 - .../translations/translations/zh-CN.json | 7 - .../api_integration/apis/fleet/agents/acks.ts | 2 +- 26 files changed, 694 insertions(+), 351 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/type_labels.tsx diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index e3ca7635fdb40..c2f8b0f981b57 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -34,7 +34,7 @@ export interface AgentActionSOAttributes extends SavedObjectAttributes { data?: string; } -export interface AgentEvent { +export interface NewAgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; subtype: // State | 'RUNNING' @@ -58,7 +58,11 @@ export interface AgentEvent { stream_id?: string; } -export interface AgentEventSOAttributes extends AgentEvent, SavedObjectAttributes {} +export interface AgentEvent extends NewAgentEvent { + id: string; +} + +export interface AgentEventSOAttributes extends NewAgentEvent, SavedObjectAttributes {} type MetadataValue = string | AgentMetadata; diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index 7214611ca9122..1105c8ee7ca82 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -4,7 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Agent, AgentAction, AgentEvent, AgentStatus, AgentType, NewAgentAction } from '../models'; +import { + Agent, + AgentAction, + NewAgentEvent, + AgentEvent, + AgentStatus, + AgentType, + NewAgentAction, +} from '../models'; export interface GetAgentsRequest { query: { @@ -40,7 +48,7 @@ export interface PostAgentCheckinRequest { }; body: { local_metadata?: Record; - events?: AgentEvent[]; + events?: NewAgentEvent[]; }; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx index ceb87fb048ae3..27b743d1a0890 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx @@ -64,6 +64,7 @@ export const Header: React.FC = ({ {tabs ? ( + {tabs.map(props => ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx index c1fae19c5dab0..9a1ffffc1ffee 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/loading.tsx @@ -5,11 +5,12 @@ */ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import { EuiLoadingSpinnerSize } from '@elastic/eui/src/components/loading/loading_spinner'; -export const Loading: React.FunctionComponent<{}> = () => ( +export const Loading: React.FunctionComponent<{ size?: EuiLoadingSpinnerSize }> = ({ size }) => ( - + ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx index 1c9bd9107515d..579a59cb909c6 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx @@ -8,6 +8,7 @@ import React, { useState, useEffect } from 'react'; import { IFieldType } from 'src/plugins/data/public'; // @ts-ignore import { EuiSuggest, EuiSuggestItemProps } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { useDebounce, useStartDeps } from '../hooks'; import { INDEX_NAME, AGENT_SAVED_OBJECT_TYPE } from '../constants'; @@ -30,9 +31,15 @@ interface Props { value: string; fieldPrefix: string; onChange: (newValue: string) => void; + placeholder?: string; } -export const SearchBar: React.FunctionComponent = ({ value, fieldPrefix, onChange }) => { +export const SearchBar: React.FunctionComponent = ({ + value, + fieldPrefix, + onChange, + placeholder, +}) => { const { suggestions } = useSuggestions(fieldPrefix, value); // TODO fix type when correctly typed in EUI @@ -52,7 +59,12 @@ export const SearchBar: React.FunctionComponent = ({ value, fieldPrefix, // @ts-ignore value={value} icon={'search'} - placeholder={'Search'} + placeholder={ + placeholder || + i18n.translate('xpack.ingestManager.defaultSearchPlaceholderText', { + defaultMessage: 'Search', + }) + } onInputChange={onChangeSearch} onItemClick={onAutocompleteClick} suggestions={suggestions.map(suggestion => { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx index f1e3fea6a0742..ccd2bc75fe223 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx @@ -29,7 +29,7 @@ export const CreateDatasourcePageLayout: React.FunctionComponent<{ const leftColumn = ( - + { const headerLeftContent = useMemo( () => ( - - - - - -
- - - -
- -

- {(agentConfig && agentConfig.name) || ( - - )} -

-
-
-
- {agentConfig && agentConfig.description ? ( - - - - {agentConfig.description} - - - ) : null} + + + + + + + + +

+ {(agentConfig && agentConfig.name) || ( + + )} +

+
+
+ + {agentConfig && agentConfig.description ? ( + + + + {agentConfig.description} + -
- -
+ ) : null} +
), [configListLink, agentConfig, configId] ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx index 64950f95f5158..15eab655adee7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/header.tsx @@ -52,7 +52,7 @@ export const HeroImage = memo(() => { ? toAssets('illustration_integrations_darkmode.svg') : toAssets('illustration_integrations_lightmode.svg'), }))` - margin-bottom: -60px; + margin-bottom: -68px; width: 80%; `; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx new file mode 100644 index 0000000000000..37fb556b94282 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/actions_menu.tsx @@ -0,0 +1,83 @@ +/* + * 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, useState, useCallback } from 'react'; +import { EuiButton, EuiPopover, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Agent } from '../../../../types'; +import { useCapabilities } from '../../../../hooks'; +import { useAgentRefresh } from '../hooks'; +import { AgentUnenrollProvider, AgentReassignConfigFlyout } from '../../components'; + +export const AgentDetailsActionMenu: React.FunctionComponent<{ + agent: Agent; +}> = memo(({ agent }) => { + const hasWriteCapabilites = useCapabilities().write; + const refreshAgent = useAgentRefresh(); + const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); + const handleCloseMenu = useCallback(() => setIsActionsPopoverOpen(false), [ + setIsActionsPopoverOpen, + ]); + const handleToggleMenu = useCallback(() => setIsActionsPopoverOpen(!isActionsPopoverOpen), [ + isActionsPopoverOpen, + ]); + const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false); + + return ( + <> + {isReassignFlyoutOpen && ( + setIsReassignFlyoutOpen(false)} /> + )} + + + + } + isOpen={isActionsPopoverOpen} + closePopover={handleCloseMenu} + > + { + handleCloseMenu(); + setIsReassignFlyoutOpen(true); + }} + key="reassignConfig" + > + + , + + {unenrollAgentsPrompt => ( + { + unenrollAgentsPrompt([agent.id], 1, refreshAgent); + }} + > + + + )} + , + ]} + /> + + + ); +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx new file mode 100644 index 0000000000000..12791b69d886c --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx @@ -0,0 +1,85 @@ +/* + * 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 { + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiFlexGroup, + EuiFlexItem, + EuiLink, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Agent, AgentConfig } from '../../../../types'; +import { AGENT_CONFIG_DETAILS_PATH } from '../../../../constants'; +import { useLink } from '../../../../hooks'; +import { AgentHealth } from '../../components'; + +export const AgentDetailsContent: React.FunctionComponent<{ + agent: Agent; + agentConfig?: AgentConfig; +}> = memo(({ agent, agentConfig }) => { + const agentConfigUrl = useLink(AGENT_CONFIG_DETAILS_PATH); + return ( + + {[ + { + title: i18n.translate('xpack.ingestManager.agentDetails.hostNameLabel', { + defaultMessage: 'Host name', + }), + description: agent.local_metadata['host.hostname'], + }, + { + title: i18n.translate('xpack.ingestManager.agentDetails.hostIdLabel', { + defaultMessage: 'Host ID', + }), + description: agent.id, + }, + { + title: i18n.translate('xpack.ingestManager.agentDetails.statusLabel', { + defaultMessage: 'Status', + }), + description: , + }, + { + title: i18n.translate('xpack.ingestManager.agentDetails.agentConfigurationLabel', { + defaultMessage: 'Agent configuration', + }), + description: agentConfig ? ( + + {agentConfig.name || agent.config_id} + + ) : ( + agent.config_id || '-' + ), + }, + { + title: i18n.translate('xpack.ingestManager.agentDetails.versionLabel', { + defaultMessage: 'Agent version', + }), + description: agent.local_metadata['agent.version'], + }, + { + title: i18n.translate('xpack.ingestManager.agentDetails.platformLabel', { + defaultMessage: 'Platform', + }), + description: agent.local_metadata['os.platform'], + }, + ].map(({ title, description }) => { + return ( + + + {title} + + + {description} + + + ); + })} + + ); +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_events_table.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_events_table.tsx index 9168669d132a0..5be728b88c3e4 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_events_table.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_events_table.tsx @@ -13,14 +13,19 @@ import { EuiButton, EuiSpacer, EuiFlexItem, - EuiTitle, + EuiBadge, + EuiText, + EuiButtonIcon, + EuiCodeBlock, } from '@elastic/eui'; +import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedTime } from '@kbn/i18n/react'; import { AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../../../constants'; import { Agent, AgentEvent } from '../../../../types'; import { usePagination, useGetOneAgentEvents } from '../../../../hooks'; import { SearchBar } from '../../../../components/search_bar'; +import { TYPE_LABEL, SUBTYPE_LABEL } from './type_labels'; function useSearch() { const [state, setState] = useState<{ search: string }>({ @@ -41,6 +46,9 @@ function useSearch() { export const AgentEventsTable: React.FunctionComponent<{ agent: Agent }> = ({ agent }) => { const { pageSizeOptions, pagination, setPagination } = usePagination(); const { search, setSearch } = useSearch(); + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<{ + [key: string]: JSX.Element; + }>({}); const { isLoading, data, sendRequest } = useGetOneAgentEvents(agent.id, { page: pagination.currentPage, @@ -59,6 +67,49 @@ export const AgentEventsTable: React.FunctionComponent<{ agent: Agent }> = ({ ag pageSizeOptions, }; + const toggleDetails = (agentEvent: AgentEvent) => { + const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; + if (itemIdToExpandedRowMapValues[agentEvent.id]) { + delete itemIdToExpandedRowMapValues[agentEvent.id]; + } else { + const details = ( +
+
+ + + + + +

{agentEvent.message}

+
+
+ {agentEvent.payload ? ( +
+ + + + + + + + + {JSON.stringify(agentEvent.payload, null, 2)} + +
+ ) : null} +
+ ); + itemIdToExpandedRowMapValues[agentEvent.id] = details; + } + setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); + }; + const columns = [ { field: 'timestamp', @@ -66,40 +117,63 @@ export const AgentEventsTable: React.FunctionComponent<{ agent: Agent }> = ({ ag defaultMessage: 'Timestamp', }), render: (timestamp: string) => ( - + ), sortable: true, + width: '18%', }, { field: 'type', name: i18n.translate('xpack.ingestManager.agentEventsList.typeColumnTitle', { defaultMessage: 'Type', }), - width: '90px', + width: '10%', + render: (type: AgentEvent['type']) => + TYPE_LABEL[type] || {type}, }, { field: 'subtype', name: i18n.translate('xpack.ingestManager.agentEventsList.subtypeColumnTitle', { defaultMessage: 'Subtype', }), - width: '90px', + width: '13%', + render: (subtype: AgentEvent['subtype']) => + SUBTYPE_LABEL[subtype] || {subtype}, }, { field: 'message', name: i18n.translate('xpack.ingestManager.agentEventsList.messageColumnTitle', { defaultMessage: 'Message', }), + render: (message: string) => {message}, + truncateText: true, }, { - field: 'payload', - name: i18n.translate('xpack.ingestManager.agentEventsList.paylodColumnTitle', { - defaultMessage: 'Payload', - }), - truncateText: true, - render: (payload: any) => ( - - {payload && JSON.stringify(payload, null, 2)} - + align: RIGHT_ALIGNMENT, + width: '40px', + isExpander: true, + render: (agentEvent: AgentEvent) => ( + toggleDetails(agentEvent)} + aria-label={ + itemIdToExpandedRowMap[agentEvent.id] + ? i18n.translate('xpack.ingestManager.agentEventsList.collapseDetailsAriaLabel', { + defaultMessage: 'Hide details', + }) + : i18n.translate('xpack.ingestManager.agentEventsList.expandDetailsAriaLabel', { + defaultMessage: 'Show details', + }) + } + iconType={itemIdToExpandedRowMap[agentEvent.id] ? 'arrowUp' : 'arrowDown'} + /> ), }, ]; @@ -120,25 +194,20 @@ export const AgentEventsTable: React.FunctionComponent<{ agent: Agent }> = ({ ag return ( <> - -

- -

-
- - + = ({ ag onChange={onChange} items={list} + itemId="id" columns={columns} pagination={paginationOptions} loading={isLoading} + itemIdToExpandedRowMap={itemIdToExpandedRowMap} /> ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx deleted file mode 100644 index b69dd6bcf8431..0000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/details_section.tsx +++ /dev/null @@ -1,206 +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, { useState, Fragment, useCallback } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, - EuiDescriptionList, - EuiButton, - EuiPopover, - EuiDescriptionListTitle, - EuiDescriptionListDescription, - EuiButtonEmpty, - EuiIconTip, - EuiContextMenuPanel, - EuiContextMenuItem, - EuiTextColor, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { useAgentRefresh } from '../hooks'; -import { AgentMetadataFlyout } from './metadata_flyout'; -import { Agent } from '../../../../types'; -import { AgentHealth } from '../../components/agent_health'; -import { useCapabilities, useGetOneAgentConfig } from '../../../../hooks'; -import { Loading } from '../../../../components'; -import { ConnectedLink, AgentReassignConfigFlyout } from '../../components'; -import { AgentUnenrollProvider } from '../../components/agent_unenroll_provider'; - -const Item: React.FunctionComponent<{ label: string }> = ({ label, children }) => { - return ( - - - {label} - {children} - - - ); -}; - -function useFlyout() { - const [isVisible, setVisible] = useState(false); - return { - isVisible, - show: () => setVisible(true), - hide: () => setVisible(false), - }; -} - -interface Props { - agent: Agent; -} -export const AgentDetailSection: React.FunctionComponent = ({ agent }) => { - const hasWriteCapabilites = useCapabilities().write; - const metadataFlyout = useFlyout(); - const refreshAgent = useAgentRefresh(); - // Actions menu - const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); - const handleCloseMenu = useCallback(() => setIsActionsPopoverOpen(false), [ - setIsActionsPopoverOpen, - ]); - const handleToggleMenu = useCallback(() => setIsActionsPopoverOpen(!isActionsPopoverOpen), [ - isActionsPopoverOpen, - ]); - const [isReassignFlyoutOpen, setIsReassignFlyoutOpen] = useState(false); - - // Fetch AgentConfig information - const { isLoading: isAgentConfigLoading, data: agentConfigData } = useGetOneAgentConfig( - agent.config_id - ); - - const items = [ - { - title: i18n.translate('xpack.ingestManager.agentDetails.statusLabel', { - defaultMessage: 'Status', - }), - description: , - }, - { - title: i18n.translate('xpack.ingestManager.agentDetails.idLabel', { - defaultMessage: 'ID', - }), - description: agent.id, - }, - { - title: i18n.translate('xpack.ingestManager.agentDetails.typeLabel', { - defaultMessage: 'Type', - }), - description: agent.type, - }, - { - title: i18n.translate('xpack.ingestManager.agentDetails.agentConfigLabel', { - defaultMessage: 'AgentConfig', - }), - description: isAgentConfigLoading ? ( - - ) : agentConfigData && agentConfigData.item ? ( - - {agentConfigData.item.name} - - ) : ( - - - } - />{' '} - {agent.config_id} - - ), - }, - ]; - - return ( - <> - {isReassignFlyoutOpen && ( - setIsReassignFlyoutOpen(false)} /> - )} - - - -

- -

-
-
- - - -
- } - isOpen={isActionsPopoverOpen} - closePopover={handleCloseMenu} - > - { - handleCloseMenu(); - setIsReassignFlyoutOpen(true); - }} - key="reassignConfig" - > - - , - - - {unenrollAgentsPrompt => ( - { - unenrollAgentsPrompt([agent.id], 1, refreshAgent); - }} - > - - - )} - , - ]} - /> - -
-
- - - {items.map((item, idx) => ( - - {item.description} - - ))} - - metadataFlyout.show()}>View metadata - - - {metadataFlyout.isVisible && } - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/index.ts index 9dffa54aeaf7f..8e6ddd0959358 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/index.ts @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ export { AgentEventsTable } from './agent_events_table'; -export { AgentDetailSection } from './details_section'; +export { AgentDetailsActionMenu } from './actions_menu'; +export { AgentDetailsContent } from './agent_details'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/type_labels.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/type_labels.tsx new file mode 100644 index 0000000000000..e9cb59be37892 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/type_labels.tsx @@ -0,0 +1,122 @@ +/* + * 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 { EuiBadge } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { AgentEvent } from '../../../../types'; + +export const TYPE_LABEL: { [key in AgentEvent['type']]: JSX.Element } = { + STATE: ( + + + + ), + ERROR: ( + + + + ), + ACTION_RESULT: ( + + + + ), + ACTION: ( + + + + ), +}; + +export const SUBTYPE_LABEL: { [key in AgentEvent['subtype']]: JSX.Element } = { + RUNNING: ( + + + + ), + STARTING: ( + + + + ), + IN_PROGRESS: ( + + + + ), + CONFIG: ( + + + + ), + FAILED: ( + + + + ), + STOPPING: ( + + + + ), + STOPPED: ( + + + + ), + DATA_DUMP: ( + + + + ), + ACKNOWLEDGED: ( + + + + ), + UNKNOWN: ( + + + + ), +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx index f8ba829135f3c..e5d69dced7523 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx @@ -3,64 +3,229 @@ * 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 { useRouteMatch } from 'react-router-dom'; +import React, { useMemo } from 'react'; +import { useRouteMatch, Switch, Route } from 'react-router-dom'; +import styled from 'styled-components'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiText, + EuiLink, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, +} from '@elastic/eui'; +import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiCallOut, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AgentEventsTable, AgentDetailSection } from './components'; import { AgentRefreshContext } from './hooks'; -import { Loading } from '../../../components'; -import { useGetOneAgent } from '../../../hooks'; +import { + FLEET_AGENTS_PATH, + FLEET_AGENT_DETAIL_PATH, + AGENT_CONFIG_DETAILS_PATH, +} from '../../../constants'; +import { Loading, Error } from '../../../components'; +import { useGetOneAgent, useGetOneAgentConfig, useLink } from '../../../hooks'; import { WithHeaderLayout } from '../../../layouts'; +import { AgentHealth } from '../components'; +import { AgentEventsTable, AgentDetailsActionMenu, AgentDetailsContent } from './components'; + +const Divider = styled.div` + width: 0; + height: 100%; + border-left: ${props => props.theme.eui.euiBorderThin}; +`; export const AgentDetailsPage: React.FunctionComponent = () => { const { - params: { agentId }, - } = useRouteMatch(); - const agentRequest = useGetOneAgent(agentId, { + params: { agentId, tabId = '' }, + } = useRouteMatch<{ agentId: string; tabId?: string }>(); + const { + isLoading, + isInitialRequest, + error, + data: agentData, + sendRequest: sendAgentRequest, + } = useGetOneAgent(agentId, { pollIntervalMs: 5000, }); + const { + isLoading: isAgentConfigLoading, + data: agentConfigData, + sendRequest: sendAgentConfigRequest, + } = useGetOneAgentConfig(agentData?.item?.config_id); - if (agentRequest.isLoading && agentRequest.isInitialRequest) { - return ; - } + const agentListUrl = useLink(FLEET_AGENTS_PATH); + const agentActivityTabUrl = useLink(`${FLEET_AGENT_DETAIL_PATH}${agentId}/activity`); + const agentDetailsTabUrl = useLink(`${FLEET_AGENT_DETAIL_PATH}${agentId}/details`); + const agentConfigUrl = useLink(AGENT_CONFIG_DETAILS_PATH); - if (agentRequest.error) { - return ( - - -

- {agentRequest.error.message} -

-
-
- ); - } + const headerLeftContent = useMemo( + () => ( + + + + + + + + +

+ {agentData?.item?.local_metadata['host.hostname'] || ( + + )} +

+
+
+
+ ), + [agentData, agentId, agentListUrl] + ); - if (!agentRequest.data) { - return ( - - - - ); - } + const headerRightContent = useMemo( + () => + agentData && agentData.item ? ( + + {[ + { + label: i18n.translate('xpack.ingestManager.agentDetails.statusLabel', { + defaultMessage: 'Status', + }), + content: , + }, + { isDivider: true }, + { + label: i18n.translate('xpack.ingestManager.agentDetails.configurationLabel', { + defaultMessage: 'Configuration', + }), + content: isAgentConfigLoading ? ( + + ) : agentConfigData?.item ? ( + + {agentConfigData.item.name || agentData.item.config_id} + + ) : ( + agentData.item.config_id || '-' + ), + }, + { isDivider: true }, + { + content: , + }, + ].map((item, index) => ( + + {item.isDivider ?? false ? ( + + ) : item.label ? ( + + {item.label} + {item.content} + + ) : ( + item.content + )} + + ))} + + ) : ( + undefined + ), + [agentConfigData, agentConfigUrl, agentData, isAgentConfigLoading] + ); - const agent = agentRequest.data.item; + const headerTabs = useMemo(() => { + return [ + { + id: 'activity_log', + name: i18n.translate('xpack.ingestManager.agentDetails.subTabs.activityLogTab', { + defaultMessage: 'Activity log', + }), + href: agentActivityTabUrl, + isSelected: !tabId || tabId === 'activity', + }, + { + id: 'details', + name: i18n.translate('xpack.ingestManager.agentDetails.subTabs.detailsTab', { + defaultMessage: 'Agent details', + }), + href: agentDetailsTabUrl, + isSelected: tabId === 'details', + }, + ]; + }, [agentActivityTabUrl, agentDetailsTabUrl, tabId]); return ( - agentRequest.sendRequest() }}> - }> - + { + sendAgentRequest(); + sendAgentConfigRequest(); + }, + }} + > + + {isLoading && isInitialRequest ? ( + + ) : error ? ( + + } + error={error} + /> + ) : agentData && agentData.item ? ( + + { + return ( + + ); + }} + /> + { + return ; + }} + /> + + ) : ( + + } + error={i18n.translate( + 'xpack.ingestManager.agentDetails.agentNotFoundErrorDescription', + { + defaultMessage: 'Cannot found agent ID {agentId}', + values: { + agentId, + }, + } + )} + /> + )} ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index 05264c157434e..5e7fe745a0c4a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -238,13 +238,13 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const columns = [ { - field: 'local_metadata.host.hostname', + field: 'local_metadata.host', name: i18n.translate('xpack.ingestManager.agentList.hostColumnTitle', { defaultMessage: 'Host', }), render: (host: string, agent: Agent) => ( - {host} + {agent.local_metadata['host.hostname'] || host || ''} ), }, @@ -308,11 +308,13 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { }, }, { - field: 'local_metadata.agent_version', + field: 'local_metadata.version', width: '100px', name: i18n.translate('xpack.ingestManager.agentList.versionTitle', { defaultMessage: 'Version', }), + render: (version: string, agent: Agent) => + agent.local_metadata['agent.version'] || version || '', }, { field: 'last_checkin', diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx index a0092f4073e5a..780adf5fbd3ac 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/index.tsx @@ -8,3 +8,5 @@ export * from './loading'; export * from './agent_reassign_config_flyout'; export * from './navigation/child_routes'; export * from './navigation/connected_link'; +export * from './agent_health'; +export * from './agent_unenroll_provider'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx index b9c5418dbf6f3..c820a9b867b63 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx @@ -42,7 +42,7 @@ export const FleetApp: React.FunctionComponent = () => { } /> - + diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index 9b1565e7d74aa..2c8b1d5bb6078 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -7,6 +7,7 @@ import { SavedObjectsClientContract, SavedObjectsBulkCreateObject } from 'src/core/server'; import { Agent, + NewAgentEvent, AgentEvent, AgentAction, AgentSOAttributes, @@ -23,7 +24,7 @@ import { appContextService } from '../app_context'; export async function agentCheckin( soClient: SavedObjectsClientContract, agent: Agent, - events: AgentEvent[], + events: NewAgentEvent[], localMetadata?: any ) { const updateData: { @@ -85,10 +86,10 @@ export async function agentCheckin( async function processEventsForCheckin( soClient: SavedObjectsClientContract, agent: Agent, - events: AgentEvent[] + events: NewAgentEvent[] ) { const acknowledgedActionIds: string[] = []; - const updatedErrorEvents = [...agent.current_error_events]; + const updatedErrorEvents: Array = [...agent.current_error_events]; for (const event of events) { // @ts-ignore event.config_id = agent.config_id; @@ -122,7 +123,7 @@ async function processEventsForCheckin( async function createEventsForAgent( soClient: SavedObjectsClientContract, agentId: string, - events: AgentEvent[] + events: NewAgentEvent[] ) { const objects: Array> = events.map( eventData => { @@ -139,11 +140,11 @@ async function createEventsForAgent( return soClient.bulkCreate(objects); } -function isErrorOrState(event: AgentEvent) { +function isErrorOrState(event: AgentEvent | NewAgentEvent) { return event.type === 'STATE' || event.type === 'ERROR'; } -function isActionEvent(event: AgentEvent) { +function isActionEvent(event: AgentEvent | NewAgentEvent) { return ( event.type === 'ACTION' && (event.subtype === 'ACKNOWLEDGED' || event.subtype === 'UNKNOWN') ); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/events.ts b/x-pack/plugins/ingest_manager/server/services/agents/events.ts index 2758374eba65f..947f79bbea094 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/events.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/events.ts @@ -39,6 +39,7 @@ export async function getAgentEvents( const items: AgentEvent[] = saved_objects.map(so => { return { + id: so.id, ...so.attributes, payload: so.attributes.payload ? JSON.parse(so.attributes.payload) : undefined, }; diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index 27ed1de849987..e8ae8146d4fa2 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -12,6 +12,7 @@ export { AgentSOAttributes, AgentStatus, AgentType, + NewAgentEvent, AgentEvent, AgentEventSOAttributes, AgentAction, diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index f18846348432b..1b396db9b0c88 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -49,8 +49,13 @@ export const AckEventSchema = schema.object({ ...{ action_id: schema.string() }, }); +export const NewAgentEventSchema = schema.object({ + ...AgentEventBase, +}); + export const AgentEventSchema = schema.object({ ...AgentEventBase, + id: schema.string(), }); export const NewAgentActionSchema = schema.object({ diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts index ac1679101312e..5526e889124f9 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent.ts @@ -5,7 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { AckEventSchema, AgentEventSchema, AgentTypeSchema, NewAgentActionSchema } from '../models'; +import { + AckEventSchema, + NewAgentEventSchema, + AgentTypeSchema, + NewAgentActionSchema, +} from '../models'; export const GetAgentsRequestSchema = { query: schema.object({ @@ -28,7 +33,7 @@ export const PostAgentCheckinRequestSchema = { }), body: schema.object({ local_metadata: schema.maybe(schema.recordOf(schema.string(), schema.any())), - events: schema.maybe(schema.arrayOf(AgentEventSchema)), + events: schema.maybe(schema.arrayOf(NewAgentEventSchema)), }), }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 377e58964dfdb..70860907ef642 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8220,15 +8220,10 @@ "xpack.ingestManager.agentConfigList.revisionNumber": "rev. {revNumber}", "xpack.ingestManager.agentConfigList.updatedOnColumnTitle": "最終更新日:", "xpack.ingestManager.agentConfigList.viewConfigActionText": "構成を表示", - "xpack.ingestManager.agentDetails.agentConfigLabel": "AgentConfig", - "xpack.ingestManager.agentDetails.agentDetailsTitle": "エージェントの詳細", "xpack.ingestManager.agentDetails.agentNotFoundErrorTitle": "エージェントが見つかりません", - "xpack.ingestManager.agentDetails.idLabel": "ID", "xpack.ingestManager.agentDetails.localMetadataSectionSubtitle": "メタデータを読み込み中", "xpack.ingestManager.agentDetails.metadataSectionTitle": "メタデータ", "xpack.ingestManager.agentDetails.statusLabel": "ステータス", - "xpack.ingestManager.agentDetails.typeLabel": "タイプ", - "xpack.ingestManager.agentDetails.unavailableConfigTooltipText": "この構成は利用できなくなりました", "xpack.ingestManager.agentDetails.unexceptedErrorTitle": "エージェントを読み込む間にエラーが発生しました", "xpack.ingestManager.agentDetails.userProvidedMetadataSectionSubtitle": "ユーザー提供メタデータ", "xpack.ingestManager.agentEnrollment.apiKeySelectionDescription": "ご希望のエージェント構成とプラットフォームをすばやく選択できます。次いで、以下の手順に従ってエージェントをセットアップして登録します。", @@ -8244,11 +8239,9 @@ "xpack.ingestManager.agentEnrollment.stepTestAgents": "エージェントのテスト", "xpack.ingestManager.agentEnrollment.testAgentLoadingMessage": "新しいエージェントの登録を待っています", "xpack.ingestManager.agentEventsList.messageColumnTitle": "メッセージ", - "xpack.ingestManager.agentEventsList.paylodColumnTitle": "ペイロード", "xpack.ingestManager.agentEventsList.refreshButton": "更新", "xpack.ingestManager.agentEventsList.subtypeColumnTitle": "サブタイプ", "xpack.ingestManager.agentEventsList.timestampColumnTitle": "タイムスタンプ", - "xpack.ingestManager.agentEventsList.title": "アクティビティログ", "xpack.ingestManager.agentEventsList.typeColumnTitle": "タイプ", "xpack.ingestManager.agentHealth.checkInTooltipText": "前回のチェックイン {lastCheckIn}", "xpack.ingestManager.agentHealth.errorStatusText": "エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 37141fbbc855f..1d3b1ecfd8ced 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8226,15 +8226,10 @@ "xpack.ingestManager.agentConfigList.revisionNumber": "修订 {revNumber}", "xpack.ingestManager.agentConfigList.updatedOnColumnTitle": "最后更新时间", "xpack.ingestManager.agentConfigList.viewConfigActionText": "查看配置", - "xpack.ingestManager.agentDetails.agentConfigLabel": "代理配置", - "xpack.ingestManager.agentDetails.agentDetailsTitle": "代理详情", "xpack.ingestManager.agentDetails.agentNotFoundErrorTitle": "未找到代理", - "xpack.ingestManager.agentDetails.idLabel": "ID", "xpack.ingestManager.agentDetails.localMetadataSectionSubtitle": "本地元数据", "xpack.ingestManager.agentDetails.metadataSectionTitle": "元数据", "xpack.ingestManager.agentDetails.statusLabel": "状态", - "xpack.ingestManager.agentDetails.typeLabel": "类型", - "xpack.ingestManager.agentDetails.unavailableConfigTooltipText": "此配置不再可用", "xpack.ingestManager.agentDetails.unexceptedErrorTitle": "加载代理时发生错误", "xpack.ingestManager.agentDetails.userProvidedMetadataSectionSubtitle": "用户提供的元数据", "xpack.ingestManager.agentEnrollment.apiKeySelectionDescription": "快速选择所需的代理配置和平台。然后,根据下面的说明设置和注册代理。", @@ -8250,11 +8245,9 @@ "xpack.ingestManager.agentEnrollment.stepTestAgents": "测试代理", "xpack.ingestManager.agentEnrollment.testAgentLoadingMessage": "正在等候新代理注册", "xpack.ingestManager.agentEventsList.messageColumnTitle": "消息", - "xpack.ingestManager.agentEventsList.paylodColumnTitle": "负载", "xpack.ingestManager.agentEventsList.refreshButton": "刷新", "xpack.ingestManager.agentEventsList.subtypeColumnTitle": "子类型", "xpack.ingestManager.agentEventsList.timestampColumnTitle": "时间戳", - "xpack.ingestManager.agentEventsList.title": "活动日志", "xpack.ingestManager.agentEventsList.typeColumnTitle": "类型", "xpack.ingestManager.agentHealth.checkInTooltipText": "上次签入时间 {lastCheckIn}", "xpack.ingestManager.agentHealth.errorStatusText": "错误", diff --git a/x-pack/test/api_integration/apis/fleet/agents/acks.ts b/x-pack/test/api_integration/apis/fleet/agents/acks.ts index adde6dd184b81..084f2b7a656dc 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/acks.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/acks.ts @@ -106,7 +106,7 @@ export default function(providerContext: FtrProviderContext) { item.action_id === '48cebde1-c906-4893-b89f-595d943b72a2' ); expect(expectedEvents.length).to.eql(2); - const expectedEvent = expectedEvents.find( + const { id, ...expectedEvent } = expectedEvents.find( (item: Record) => item.action_id === '48cebde1-c906-4893-b89f-595d943b72a1' ); expect(expectedEvent).to.eql({ From 0bc8e3ef3d23e3ac2703c0bb3681657694dcd3b5 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 4 May 2020 21:50:53 -0400 Subject: [PATCH 21/55] [Uptime] TLS alerting (#63913) (#65211) * Refactor settings form event handling and modify certs fields. * Fix/improve broken types/unit/integration/api tests. * Modify default expiration threshold. * Rename test vars. * Implement PR feedback. * Refresh snapshots, fix broken tests/types. * Remove unnecessary state spreading. * Add type for settings field errors. * Refresh test snapshots. * Improve punctuation. * Add TLS alert type. * Add cert API request and runtime type checking. * Add api test for cert api. * Add unload command to certs test. * Extract API params interface to io-ts type. * Add TLS alert type on server. * WIP - add state for changing selected alert type. * Finish adding alert type for client, add server alert summary. * Add some state variables. * Update certs summary function to create required values. * Refresh test snapshots. * Clean up message generator function. * Add a comment. * Update formatting for alert messages, add flags denoting presence of age/expiration data. * Add relative date information to tls alert messages. * Clean up more logic in certs request function. * Fix broken unit tests. * Move tests for common function to new file. * Fix logic error in test and add common state fields to tls alerts. * Extract common state field translations from status check alert. * Add a comment. * Add nested context navigation for uptime alert selection. * Clean up types. * Fix translation key typo. * Extract translations from tls alert factory. * Extract summary messages to translation file. * Change default tls alert time window from 1w to 1d. * Remove unnecessary import. * Simplify page linking. * Extract a non-trivial component to a dedicated file. * Simplify create alert copy. * Fix broken functional test. * Fix busted types. * Fix tls query error. * Allow for alerts toggle button to receive a set of types to display. * Add alerts toggle button to certs page. * Fix copy. * Fixup punctuation in default message to avoid double-period symbols. * Refresh snapshots. --- .../plugins/uptime/common/constants/alerts.ts | 9 + .../plugins/uptime/common/constants/index.ts | 2 +- .../common/runtime_types/alerts/common.ts | 23 +++ .../common/runtime_types/alerts/index.ts | 8 +- .../runtime_types/alerts/status_check.ts | 16 -- .../uptime/common/runtime_types/certs.ts | 2 + .../components/overview/alerts/alert_tls.tsx | 51 +++++ .../alerts/alerts_containers/alert_tls.tsx | 26 +++ .../alerts/alerts_containers/index.ts | 5 +- .../toggle_alert_flyout_button.tsx | 18 +- .../uptime_alerts_flyout_wrapper.tsx | 13 +- .../settings_message_expression_popover.tsx | 72 +++++++ .../alerts/toggle_alert_flyout_button.tsx | 155 ++++++++++----- .../overview/alerts/translations.ts | 79 ++++++++ .../alerts/uptime_alerts_flyout_wrapper.tsx | 6 +- .../overview/overview_container.tsx | 27 +-- .../uptime/public/lib/alert_types/index.ts | 6 +- .../public/lib/alert_types/monitor_status.tsx | 14 +- .../uptime/public/lib/alert_types/tls.tsx | 23 +++ .../public/lib/alert_types/translations.ts | 52 +++++ .../uptime/public/pages/certificates.tsx | 6 +- .../plugins/uptime/public/pages/overview.tsx | 8 +- .../plugins/uptime/public/state/actions/ui.ts | 4 +- .../uptime/public/state/reducers/ui.ts | 7 + .../uptime/public/state/selectors/index.ts | 2 + x-pack/plugins/uptime/public/uptime_app.tsx | 5 +- .../lib/alerts/__tests__/common.test.ts | 180 ++++++++++++++++++ .../lib/alerts/__tests__/status_check.test.ts | 174 ----------------- .../server/lib/alerts/__tests__/tls.test.ts | 147 ++++++++++++++ .../uptime/server/lib/alerts/common.ts | 56 ++++++ .../plugins/uptime/server/lib/alerts/index.ts | 6 +- .../uptime/server/lib/alerts/status_check.ts | 126 +----------- .../plugins/uptime/server/lib/alerts/tls.ts | 152 +++++++++++++++ .../uptime/server/lib/alerts/translations.ts | 150 +++++++++++++++ .../uptime/server/lib/requests/get_certs.ts | 37 +++- .../api_integration/apis/uptime/rest/index.ts | 1 + .../test/functional/services/uptime/alerts.ts | 1 + 37 files changed, 1260 insertions(+), 409 deletions(-) create mode 100644 x-pack/plugins/uptime/common/runtime_types/alerts/common.ts create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx create mode 100644 x-pack/plugins/uptime/public/components/overview/alerts/translations.ts create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/tls.tsx create mode 100644 x-pack/plugins/uptime/public/lib/alert_types/translations.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/common.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/tls.ts create mode 100644 x-pack/plugins/uptime/server/lib/alerts/translations.ts diff --git a/x-pack/plugins/uptime/common/constants/alerts.ts b/x-pack/plugins/uptime/common/constants/alerts.ts index c0db9ae309843..a259fc0a3eb81 100644 --- a/x-pack/plugins/uptime/common/constants/alerts.ts +++ b/x-pack/plugins/uptime/common/constants/alerts.ts @@ -16,4 +16,13 @@ export const ACTION_GROUP_DEFINITIONS: ActionGroupDefinitions = { id: 'xpack.uptime.alerts.actionGroups.monitorStatus', name: 'Uptime Down Monitor', }, + TLS: { + id: 'xpack.uptime.alerts.actionGroups.tls', + name: 'Uptime TLS Alert', + }, +}; + +export const CLIENT_ALERT_TYPES = { + MONITOR_STATUS: 'xpack.uptime.alerts.monitorStatus', + TLS: 'xpack.uptime.alerts.tls', }; diff --git a/x-pack/plugins/uptime/common/constants/index.ts b/x-pack/plugins/uptime/common/constants/index.ts index 00baa39044a55..0ddb995301266 100644 --- a/x-pack/plugins/uptime/common/constants/index.ts +++ b/x-pack/plugins/uptime/common/constants/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ACTION_GROUP_DEFINITIONS } from './alerts'; +export * from './alerts'; export { CHART_FORMAT_LIMITS } from './chart_format_limits'; export { CLIENT_DEFAULTS } from './client_defaults'; export { CONTEXT_DEFAULTS } from './context_defaults'; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts new file mode 100644 index 0000000000000..a6b03551c9061 --- /dev/null +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/common.ts @@ -0,0 +1,23 @@ +/* + * 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'; + +export const UptimeCommonStateType = t.intersection([ + t.partial({ + currentTriggerStarted: t.string, + firstTriggeredAt: t.string, + lastTriggeredAt: t.string, + lastResolvedAt: t.string, + }), + t.type({ + firstCheckedAt: t.string, + lastCheckedAt: t.string, + isTriggered: t.boolean, + }), +]); + +export type UptimeCommonState = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts index ee284249c38c0..9b2138fc2524c 100644 --- a/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/index.ts @@ -4,9 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - StatusCheckAlertStateType, - StatusCheckAlertState, - StatusCheckExecutorParamsType, - StatusCheckExecutorParams, -} from './status_check'; +export * from './common'; +export * from './status_check'; diff --git a/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts b/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts index bc234b268df27..909669bb5d3eb 100644 --- a/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts +++ b/x-pack/plugins/uptime/common/runtime_types/alerts/status_check.ts @@ -6,22 +6,6 @@ import * as t from 'io-ts'; -export const StatusCheckAlertStateType = t.intersection([ - t.partial({ - currentTriggerStarted: t.string, - firstTriggeredAt: t.string, - lastTriggeredAt: t.string, - lastResolvedAt: t.string, - }), - t.type({ - firstCheckedAt: t.string, - lastCheckedAt: t.string, - isTriggered: t.boolean, - }), -]); - -export type StatusCheckAlertState = t.TypeOf; - export const StatusCheckExecutorParamsType = t.intersection([ t.partial({ filters: t.string, diff --git a/x-pack/plugins/uptime/common/runtime_types/certs.ts b/x-pack/plugins/uptime/common/runtime_types/certs.ts index e9071e76b6d75..923143ca8bb5d 100644 --- a/x-pack/plugins/uptime/common/runtime_types/certs.ts +++ b/x-pack/plugins/uptime/common/runtime_types/certs.ts @@ -15,6 +15,8 @@ export const GetCertsParamsType = t.intersection([ }), t.partial({ search: t.string, + notValidBefore: t.string, + notValidAfter: t.string, from: t.string, to: t.string, }), diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx new file mode 100644 index 0000000000000..203ef07cc2bc8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alert_tls.tsx @@ -0,0 +1,51 @@ +/* + * 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 { EuiExpression, EuiFlexItem, EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import { TlsTranslations } from './translations'; +import { SettingsMessageExpressionPopover } from './settings_message_expression_popover'; + +interface Props { + ageThreshold?: number; + expirationThreshold?: number; + setAlertFlyoutVisible: (value: boolean) => void; +} + +export const AlertTlsComponent: React.FC = props => ( + <> + + + + + + + + + + + + + + +); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx new file mode 100644 index 0000000000000..ab24d0fb5ee67 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_tls.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useDispatch, useSelector } from 'react-redux'; +import React, { useCallback } from 'react'; +import { AlertTlsComponent } from '../alert_tls'; +import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { selectDynamicSettings } from '../../../../state/selectors'; + +export const AlertTls = () => { + const dispatch = useDispatch(); + const setFlyoutVisible = useCallback((value: boolean) => dispatch(setAlertFlyoutVisible(value)), [ + dispatch, + ]); + const { settings } = useSelector(selectDynamicSettings); + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts index 87179a96fc0b2..c30312b19f711 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts @@ -5,5 +5,8 @@ */ export { AlertMonitorStatus } from './alert_monitor_status'; -export { ToggleAlertFlyoutButton } from './toggle_alert_flyout_button'; +export { + ToggleAlertFlyoutButton, + ToggleAlertFlyoutButtonProps, +} from './toggle_alert_flyout_button'; export { UptimeAlertsFlyoutWrapper } from './uptime_alerts_flyout_wrapper'; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx index 45ba72d76fba6..c6b2074338c63 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx @@ -6,14 +6,26 @@ import React from 'react'; import { useDispatch } from 'react-redux'; -import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { setAlertFlyoutType, setAlertFlyoutVisible } from '../../../../state/actions'; import { ToggleAlertFlyoutButtonComponent } from '../index'; -export const ToggleAlertFlyoutButton = () => { +export interface ToggleAlertFlyoutButtonProps { + alertOptions?: string[]; +} + +export const ToggleAlertFlyoutButton: React.FC = props => { const dispatch = useDispatch(); return ( dispatch(setAlertFlyoutVisible(value))} + {...props} + setAlertFlyoutVisible={(value: boolean | string) => { + if (typeof value === 'string') { + dispatch(setAlertFlyoutType(value)); + dispatch(setAlertFlyoutVisible(true)); + } else { + dispatch(setAlertFlyoutVisible(value)); + } + }} /> ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx index 7bfd44a762455..33eafbd1e21bc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx @@ -7,27 +7,22 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { setAlertFlyoutVisible } from '../../../../state/actions'; -import { selectAlertFlyoutVisibility } from '../../../../state/selectors'; -import { UptimeAlertsFlyoutWrapperComponent } from '../index'; +import { UptimeAlertsFlyoutWrapperComponent } from '../uptime_alerts_flyout_wrapper'; +import { selectAlertFlyoutVisibility, selectAlertFlyoutType } from '../../../../state/selectors'; -interface Props { - alertTypeId?: string; - canChangeTrigger?: boolean; -} - -export const UptimeAlertsFlyoutWrapper = ({ alertTypeId, canChangeTrigger }: Props) => { +export const UptimeAlertsFlyoutWrapper: React.FC = () => { const dispatch = useDispatch(); const setAddFlyoutVisibility = (value: React.SetStateAction) => // @ts-ignore the value here is a boolean, and it works with the action creator function dispatch(setAlertFlyoutVisible(value)); const alertFlyoutVisible = useSelector(selectAlertFlyoutVisibility); + const alertTypeId = useSelector(selectAlertFlyoutType); return ( ); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx new file mode 100644 index 0000000000000..8d9de08751eee --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/settings_message_expression_popover.tsx @@ -0,0 +1,72 @@ +/* + * 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 { EuiExpression, EuiPopover } from '@elastic/eui'; +import { Link } from 'react-router-dom'; +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { SETTINGS_ROUTE } from '../../../../common/constants'; + +interface SettingsMessageExpressionPopoverProps { + 'aria-label': string; + description: string; + id: string; + setAlertFlyoutVisible: (value: boolean) => void; + value: string; +} + +export const SettingsMessageExpressionPopover: React.FC = ({ + 'aria-label': ariaLabel, + description, + setAlertFlyoutVisible, + value, + id, +}) => { + const [isOpen, setIsOpen] = useState(false); + return ( + setIsOpen(!isOpen)} + value={value} + /> + } + isOpen={isOpen} + closePopover={() => setIsOpen(false)} + > + + { + setAlertFlyoutVisible(false); + }} + onKeyUp={e => { + if (e.key === 'Enter') { + setAlertFlyoutVisible(false); + } + }} + > + settings page + + + ), + }} + /> + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx index 92fc015a276d3..cba96cd2fe5b1 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx @@ -4,27 +4,128 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiContextMenuPanelItemDescriptor, + EuiLink, + EuiPopover, +} from '@elastic/eui'; import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { CLIENT_ALERT_TYPES } from '../../../../common/constants'; +import { ToggleFlyoutTranslations } from './translations'; +import { ToggleAlertFlyoutButtonProps } from './alerts_containers'; -interface Props { - setAlertFlyoutVisible: (value: boolean) => void; +interface ComponentProps { + setAlertFlyoutVisible: (value: boolean | string) => void; } -export const ToggleAlertFlyoutButtonComponent = ({ setAlertFlyoutVisible }: Props) => { +type Props = ComponentProps & ToggleAlertFlyoutButtonProps; + +const ALERT_CONTEXT_MAIN_PANEL_ID = 0; +const ALERT_CONTEXT_SELECT_TYPE_PANEL_ID = 1; + +export const ToggleAlertFlyoutButtonComponent: React.FC = ({ + alertOptions, + setAlertFlyoutVisible, +}) => { const [isOpen, setIsOpen] = useState(false); const kibana = useKibana(); + const monitorStatusAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.toggleMonitorStatusAriaLabel, + 'data-test-subj': 'xpack.uptime.toggleAlertFlyout', + name: ToggleFlyoutTranslations.toggleMonitorStatusContent, + onClick: () => { + setAlertFlyoutVisible(CLIENT_ALERT_TYPES.MONITOR_STATUS); + setIsOpen(false); + }, + }; + + const tlsAlertContextMenuItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.toggleTlsAriaLabel, + 'data-test-subj': 'xpack.uptime.toggleTlsAlertFlyout', + name: ToggleFlyoutTranslations.toggleTlsContent, + onClick: () => { + setAlertFlyoutVisible(CLIENT_ALERT_TYPES.TLS); + setIsOpen(false); + }, + }; + + const managementContextItem: EuiContextMenuPanelItemDescriptor = { + 'aria-label': ToggleFlyoutTranslations.navigateToAlertingUIAriaLabel, + 'data-test-subj': 'xpack.uptime.navigateToAlertingUi', + name: ( + + + + ), + icon: 'tableOfContents', + }; + + let selectionItems: EuiContextMenuPanelItemDescriptor[] = []; + if (!alertOptions) { + selectionItems = [monitorStatusAlertContextMenuItem, tlsAlertContextMenuItem]; + } else { + alertOptions.forEach(option => { + if (option === CLIENT_ALERT_TYPES.MONITOR_STATUS) + selectionItems.push(monitorStatusAlertContextMenuItem); + else if (option === CLIENT_ALERT_TYPES.TLS) selectionItems.push(tlsAlertContextMenuItem); + }); + } + + if (selectionItems.length === 1) { + selectionItems[0].icon = 'bell'; + } + + let panels: EuiContextMenuPanelDescriptor[]; + if (selectionItems.length === 1) { + panels = [ + { + id: ALERT_CONTEXT_MAIN_PANEL_ID, + title: 'main panel', + items: [...selectionItems, managementContextItem], + }, + ]; + } else { + panels = [ + { + id: ALERT_CONTEXT_MAIN_PANEL_ID, + title: 'main panel', + items: [ + { + 'aria-label': ToggleFlyoutTranslations.openAlertContextPanelAriaLabel, + 'data-test-subj': 'xpack.uptime.openAlertContextPanel', + name: ToggleFlyoutTranslations.openAlertContextPanelLabel, + icon: 'bell', + panel: ALERT_CONTEXT_SELECT_TYPE_PANEL_ID, + }, + managementContextItem, + ], + }, + { + id: ALERT_CONTEXT_SELECT_TYPE_PANEL_ID, + title: 'create alerts', + items: selectionItems, + }, + ]; + } return ( - { - setAlertFlyoutVisible(true); - setIsOpen(false); - }} - > - - , - - - , - ]} - /> + ); }; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts new file mode 100644 index 0000000000000..406654c808186 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/alerts/translations.ts @@ -0,0 +1,79 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const TlsTranslations = { + criteriaAriaLabel: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.ariaLabel', { + defaultMessage: + 'An expression displaying the criteria for monitor that are watched by this alert', + }), + criteriaDescription: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.description', { + defaultMessage: 'when', + description: + 'The context of this `when` is in the conditional sense, like "when there are three cookies, eat them all".', + }), + criteriaValue: i18n.translate('xpack.uptime.alerts.tls.criteriaExpression.value', { + defaultMessage: 'any monitor', + }), + expirationAriaLabel: i18n.translate('xpack.uptime.alerts.tls.expirationExpression.ariaLabel', { + defaultMessage: + 'An expression displaying the threshold that will trigger the TLS alert for certificate expiration', + }), + expirationDescription: i18n.translate( + 'xpack.uptime.alerts.tls.expirationExpression.description', + { + defaultMessage: 'has a certificate expiring within', + } + ), + expirationValue: (value?: number) => + i18n.translate('xpack.uptime.alerts.tls.expirationExpression.value', { + defaultMessage: '{value} days', + values: { value }, + }), + ageAriaLabel: i18n.translate('xpack.uptime.alerts.tls.ageExpression.ariaLabel', { + defaultMessage: + 'An expressing displaying the threshold that will trigger the TLS alert for old certificates', + }), + ageDescription: i18n.translate('xpack.uptime.alerts.tls.ageExpression.description', { + defaultMessage: 'or older than', + }), + ageValue: (value?: number) => + i18n.translate('xpack.uptime.alerts.tls.ageExpression.value', { + defaultMessage: '{value} days', + values: { value }, + }), +}; + +export const ToggleFlyoutTranslations = { + toggleButtonAriaLabel: i18n.translate('xpack.uptime.alertsPopover.toggleButton.ariaLabel', { + defaultMessage: 'Open alert context menu', + }), + openAlertContextPanelAriaLabel: i18n.translate('xpack.uptime.openAlertContextPanel.ariaLabel', { + defaultMessage: 'Open the alert context panel so you can choose an alert type', + }), + openAlertContextPanelLabel: i18n.translate('xpack.uptime.openAlertContextPanel.label', { + defaultMessage: 'Create alert', + }), + toggleTlsAriaLabel: i18n.translate('xpack.uptime.toggleTlsAlertButton.ariaLabel', { + defaultMessage: 'Open TLS alert flyout', + }), + toggleTlsContent: i18n.translate('xpack.uptime.toggleTlsAlertButton.content', { + defaultMessage: 'TLS alert', + }), + toggleMonitorStatusAriaLabel: i18n.translate('xpack.uptime.toggleAlertFlyout.ariaLabel', { + defaultMessage: 'Open add alert flyout', + }), + toggleMonitorStatusContent: i18n.translate('xpack.uptime.toggleAlertButton.content', { + defaultMessage: 'Monitor status alert', + }), + navigateToAlertingUIAriaLabel: i18n.translate('xpack.uptime.navigateToAlertingUi', { + defaultMessage: 'Leave Uptime and go to Alerting Management page', + }), + navigateToAlertingButtonContent: i18n.translate('xpack.uptime.navigateToAlertingButton.content', { + defaultMessage: 'Manage alerts', + }), +}; diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx index 9b1d3a73dc661..de4e67cfe8edc 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx @@ -10,14 +10,12 @@ import { AlertAdd } from '../../../../../../plugins/triggers_actions_ui/public'; interface Props { alertFlyoutVisible: boolean; alertTypeId?: string; - canChangeTrigger?: boolean; setAlertFlyoutVisibility: React.Dispatch>; } export const UptimeAlertsFlyoutWrapperComponent = ({ alertFlyoutVisible, alertTypeId, - canChangeTrigger, setAlertFlyoutVisibility, }: Props) => ( ); diff --git a/x-pack/plugins/uptime/public/components/overview/overview_container.tsx b/x-pack/plugins/uptime/public/components/overview/overview_container.tsx index d64e489c48076..320536bc63b3c 100644 --- a/x-pack/plugins/uptime/public/components/overview/overview_container.tsx +++ b/x-pack/plugins/uptime/public/components/overview/overview_container.tsx @@ -4,20 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { connect } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import React, { useCallback } from 'react'; +import { DataPublicPluginSetup } from 'src/plugins/data/public'; import { OverviewPageComponent } from '../../pages/overview'; import { selectIndexPattern } from '../../state/selectors'; -import { AppState } from '../../state'; import { setEsKueryString } from '../../state/actions'; -interface DispatchProps { - setEsKueryFilters: typeof setEsKueryString; +export interface OverviewPageProps { + autocomplete: DataPublicPluginSetup['autocomplete']; } -const mapDispatchToProps = (dispatch: any): DispatchProps => ({ - setEsKueryFilters: (esFilters: string) => dispatch(setEsKueryString(esFilters)), -}); - -const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); - -export const OverviewPage = connect(mapStateToProps, mapDispatchToProps)(OverviewPageComponent); +export const OverviewPage: React.FC = props => { + const dispatch = useDispatch(); + const setEsKueryFilters = useCallback( + (esFilters: string) => dispatch(setEsKueryString(esFilters)), + [dispatch] + ); + const indexPattern = useSelector(selectIndexPattern); + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/lib/alert_types/index.ts b/x-pack/plugins/uptime/public/lib/alert_types/index.ts index f7ab254ffe675..9a0151e95748c 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/index.ts +++ b/x-pack/plugins/uptime/public/lib/alert_types/index.ts @@ -6,7 +6,11 @@ import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { initMonitorStatusAlertType } from './monitor_status'; +import { initTlsAlertType } from './tls'; export type AlertTypeInitializer = (dependenies: { autocomplete: any }) => AlertTypeModel; -export const alertTypeInitializers: AlertTypeInitializer[] = [initMonitorStatusAlertType]; +export const alertTypeInitializers: AlertTypeInitializer[] = [ + initMonitorStatusAlertType, + initTlsAlertType, +]; diff --git a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx index e7695fb1cbb56..f93e17270a192 100644 --- a/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -12,6 +12,8 @@ import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; import { AlertTypeInitializer } from '.'; import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; import { AlertMonitorStatus } from '../../components/overview/alerts/alerts_containers'; +import { CLIENT_ALERT_TYPES } from '../../../common/constants'; +import { MonitorStatusTranslations } from './translations'; export const validate = (alertParams: any) => { const errors: Record = {}; @@ -52,15 +54,15 @@ export const validate = (alertParams: any) => { return { errors }; }; +const { name, defaultActionMessage } = MonitorStatusTranslations; + export const initMonitorStatusAlertType: AlertTypeInitializer = ({ autocomplete, }): AlertTypeModel => ({ - id: 'xpack.uptime.alerts.monitorStatus', - name: 'Uptime monitor status', + id: CLIENT_ALERT_TYPES.MONITOR_STATUS, + name, iconClass: 'uptimeApp', - alertParamsExpression: params => { - return ; - }, + alertParamsExpression: params => , validate, - defaultActionMessage: `{{context.message}}\nLast triggered at: {{state.lastTriggeredAt}}\n{{context.downMonitorsWithGeo}}`, + defaultActionMessage, }); diff --git a/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx new file mode 100644 index 0000000000000..0a5c09acbb69f --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/alert_types/tls.tsx @@ -0,0 +1,23 @@ +/* + * 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 { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { CLIENT_ALERT_TYPES } from '../../../common/constants'; +import { TlsTranslations } from './translations'; +import { AlertTypeInitializer } from '.'; +import { AlertTls } from '../../components/overview/alerts/alerts_containers/alert_tls'; + +const { name, defaultActionMessage } = TlsTranslations; + +export const initTlsAlertType: AlertTypeInitializer = (): AlertTypeModel => ({ + id: CLIENT_ALERT_TYPES.TLS, + iconClass: 'uptimeApp', + alertParamsExpression: () => , + name, + validate: () => ({ errors: {} }), + defaultActionMessage, +}); diff --git a/x-pack/plugins/uptime/public/lib/alert_types/translations.ts b/x-pack/plugins/uptime/public/lib/alert_types/translations.ts new file mode 100644 index 0000000000000..cdf3cd107b00f --- /dev/null +++ b/x-pack/plugins/uptime/public/lib/alert_types/translations.ts @@ -0,0 +1,52 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const MonitorStatusTranslations = { + defaultActionMessage: i18n.translate('xpack.uptime.alerts.monitorStatus.defaultActionMessage', { + defaultMessage: '{contextMessage}\nLast triggered at: {lastTriggered}\n{downMonitors}', + values: { + contextMessage: '{{context.message}}', + lastTriggered: '{{state.lastTriggeredAt}}', + downMonitors: '{{context.downMonitorsWithGeo}}', + }, + }), + name: i18n.translate('xpack.uptime.alerts.monitorStatus.clientName', { + defaultMessage: 'Uptime monitor status', + }), +}; + +export const TlsTranslations = { + defaultActionMessage: i18n.translate('xpack.uptime.alerts.tls.defaultActionMessage', { + defaultMessage: `Detected {count} TLS certificates expiring or becoming too old. + +{expiringConditionalOpen} +Expiring cert count: {expiringCount} +Expiring Certificates: {expiringCommonNameAndDate} +{expiringConditionalClose} + +{agingConditionalOpen} +Aging cert count: {agingCount} +Aging Certificates: {agingCommonNameAndDate} +{agingConditionalClose} +`, + values: { + count: '{{state.count}}', + expiringCount: '{{state.expiringCount}}', + expiringCommonNameAndDate: '{{state.expiringCommonNameAndDate}}', + expiringConditionalOpen: '{{#state.hasExpired}}', + expiringConditionalClose: '{{/state.hasExpired}}', + agingCount: '{{state.agingCount}}', + agingCommonNameAndDate: '{{state.agingCommonNameAndDate}}', + agingConditionalOpen: '{{#state.hasAging}}', + agingConditionalClose: '{{/state.hasAging}}', + }, + }), + name: i18n.translate('xpack.uptime.alerts.tls.clientName', { + defaultMessage: 'Uptime TLS', + }), +}; diff --git a/x-pack/plugins/uptime/public/pages/certificates.tsx b/x-pack/plugins/uptime/public/pages/certificates.tsx index d6c1b8e2b4568..92a41adcf01cd 100644 --- a/x-pack/plugins/uptime/public/pages/certificates.tsx +++ b/x-pack/plugins/uptime/public/pages/certificates.tsx @@ -19,13 +19,14 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { useTrackPageview } from '../../../observability/public'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; -import { OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../../common/constants'; +import { OVERVIEW_ROUTE, SETTINGS_ROUTE, CLIENT_ALERT_TYPES } from '../../common/constants'; import { getDynamicSettings } from '../state/actions/dynamic_settings'; import { UptimeRefreshContext } from '../contexts'; import * as labels from './translations'; import { UptimePage, useUptimeTelemetry } from '../hooks'; import { certificatesSelector, getCertificatesAction } from '../state/certificates/certificates'; import { CertificateList, CertificateSearch, CertSort } from '../components/certificates'; +import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; const DEFAULT_PAGE_SIZE = 10; const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize'; @@ -83,6 +84,9 @@ export const CertificatesPage: React.FC = () => {
+ + + diff --git a/x-pack/plugins/uptime/public/pages/overview.tsx b/x-pack/plugins/uptime/public/pages/overview.tsx index fefd804cbfabf..65f64aa7352a9 100644 --- a/x-pack/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/plugins/uptime/public/pages/overview.tsx @@ -11,22 +11,20 @@ import { i18n } from '@kbn/i18n'; import { useUptimeTelemetry, UptimePage, useGetUrlParams } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { PageHeader } from './page_header'; -import { DataPublicPluginSetup, IIndexPattern } from '../../../../../src/plugins/data/public'; +import { IIndexPattern } from '../../../../../src/plugins/data/public'; import { useUpdateKueryString } from '../hooks'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { useTrackPageview } from '../../../observability/public'; import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; import { EmptyState, FilterGroup, KueryBar, ParsingErrorCallout } from '../components/overview'; import { StatusPanel } from '../components/overview/status_panel'; +import { OverviewPageProps } from '../components/overview/overview_container'; -interface OverviewPageProps { - autocomplete: DataPublicPluginSetup['autocomplete']; +interface Props extends OverviewPageProps { indexPattern: IIndexPattern | null; setEsKueryFilters: (esFilters: string) => void; } -type Props = OverviewPageProps; - const EuiFlexItemStyled = styled(EuiFlexItem)` && { min-width: 598px; diff --git a/x-pack/plugins/uptime/public/state/actions/ui.ts b/x-pack/plugins/uptime/public/state/actions/ui.ts index 4885f974dbbd4..80e8796843ac2 100644 --- a/x-pack/plugins/uptime/public/state/actions/ui.ts +++ b/x-pack/plugins/uptime/public/state/actions/ui.ts @@ -12,7 +12,9 @@ export interface PopoverState { export type UiPayload = PopoverState & string & number & Map; -export const setAlertFlyoutVisible = createAction('TOGGLE ALERT FLYOUT'); +export const setAlertFlyoutVisible = createAction('TOGGLE ALERT FLYOUT'); + +export const setAlertFlyoutType = createAction('SET ALERT FLYOUT TYPE'); export const setBasePath = createAction('SET BASE PATH'); diff --git a/x-pack/plugins/uptime/public/state/reducers/ui.ts b/x-pack/plugins/uptime/public/state/reducers/ui.ts index c533f293fc940..82c2bfe2c0cec 100644 --- a/x-pack/plugins/uptime/public/state/reducers/ui.ts +++ b/x-pack/plugins/uptime/public/state/reducers/ui.ts @@ -12,11 +12,13 @@ import { setEsKueryString, triggerAppRefresh, UiPayload, + setAlertFlyoutType, setAlertFlyoutVisible, } from '../actions'; export interface UiState { alertFlyoutVisible: boolean; + alertFlyoutType?: string; basePath: string; esKuery: string; integrationsPopoverOpen: PopoverState | null; @@ -57,6 +59,11 @@ export const uiReducer = handleActions( ...state, esKuery: action.payload as string, }), + + [String(setAlertFlyoutType)]: (state, action: Action) => ({ + ...state, + alertFlyoutType: action.payload, + }), }, initialState ); diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index 15fc8b8a7b173..9552538182dd2 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -91,6 +91,8 @@ export const selectDurationLines = ({ monitorDuration }: AppState) => { export const selectAlertFlyoutVisibility = ({ ui: { alertFlyoutVisible } }: AppState) => alertFlyoutVisible; +export const selectAlertFlyoutType = ({ ui: { alertFlyoutType } }: AppState) => alertFlyoutType; + export const selectMonitorStatusAlert = ({ indexPattern, overviewFilters, ui }: AppState) => ({ filters: ui.esKuery, indexPattern: indexPattern.index_pattern, diff --git a/x-pack/plugins/uptime/public/uptime_app.tsx b/x-pack/plugins/uptime/public/uptime_app.tsx index 0d18f959230d1..836d942d92165 100644 --- a/x-pack/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/uptime_app.tsx @@ -105,10 +105,7 @@ const Application = (props: UptimeAppProps) => {
- +
diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts new file mode 100644 index 0000000000000..cd06370816ccc --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/common.test.ts @@ -0,0 +1,180 @@ +/* + * 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 { updateState } from '../common'; + +describe('updateState', () => { + let spy: jest.SpyInstance; + beforeEach(() => { + spy = jest.spyOn(Date.prototype, 'toISOString'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('sets initial state values', () => { + spy.mockImplementation(() => 'foo date string'); + const result = updateState({}, false); + expect(spy).toHaveBeenCalledTimes(1); + expect(result).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "foo date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "foo date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + }); + + it('updates the correct field in subsequent calls', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string'); + const firstState = updateState({}, false); + const secondState = updateState(firstState, true); + expect(spy).toHaveBeenCalledTimes(2); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "second date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + }); + + it('correctly marks resolution times', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string') + .mockImplementationOnce(() => 'third date string'); + const firstState = updateState({}, true); + const secondState = updateState(firstState, true); + const thirdState = updateState(secondState, false); + expect(spy).toHaveBeenCalledTimes(3); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "first date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": true, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "first date string", + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "first date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + expect(thirdState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "first date string", + "isTriggered": false, + "lastCheckedAt": "third date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "second date string", + } + `); + }); + + it('correctly marks state fields across multiple triggers/resolutions', () => { + spy + .mockImplementationOnce(() => 'first date string') + .mockImplementationOnce(() => 'second date string') + .mockImplementationOnce(() => 'third date string') + .mockImplementationOnce(() => 'fourth date string') + .mockImplementationOnce(() => 'fifth date string'); + const firstState = updateState({}, false); + const secondState = updateState(firstState, true); + const thirdState = updateState(secondState, false); + const fourthState = updateState(thirdState, true); + const fifthState = updateState(fourthState, false); + expect(spy).toHaveBeenCalledTimes(5); + expect(firstState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": undefined, + "isTriggered": false, + "lastCheckedAt": "first date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": undefined, + } + `); + expect(secondState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "second date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "second date string", + "lastResolvedAt": undefined, + "lastTriggeredAt": "second date string", + } + `); + expect(thirdState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": false, + "lastCheckedAt": "third date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "second date string", + } + `); + expect(fourthState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": "fourth date string", + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": true, + "lastCheckedAt": "fourth date string", + "lastResolvedAt": "third date string", + "lastTriggeredAt": "fourth date string", + } + `); + expect(fifthState).toMatchInlineSnapshot(` + Object { + "currentTriggerStarted": undefined, + "firstCheckedAt": "first date string", + "firstTriggeredAt": "second date string", + "isTriggered": false, + "lastCheckedAt": "fifth date string", + "lastResolvedAt": "fifth date string", + "lastTriggeredAt": "fourth date 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 db1eed2367a14..6991279271cf7 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 @@ -7,7 +7,6 @@ import { contextMessage, uniqueMonitorIds, - updateState, statusCheckAlertFactory, fullListByIdAndLocation, } from '../status_check'; @@ -337,179 +336,6 @@ describe('status check alert', () => { }); }); - describe('updateState', () => { - let spy: jest.SpyInstance; - beforeEach(() => { - spy = jest.spyOn(Date.prototype, 'toISOString'); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('sets initial state values', () => { - spy.mockImplementation(() => 'foo date string'); - const result = updateState({}, false); - expect(spy).toHaveBeenCalledTimes(1); - expect(result).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "foo date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "foo date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - }); - - it('updates the correct field in subsequent calls', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string'); - const firstState = updateState({}, false); - const secondState = updateState(firstState, true); - expect(spy).toHaveBeenCalledTimes(2); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "second date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - }); - - it('correctly marks resolution times', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string') - .mockImplementationOnce(() => 'third date string'); - const firstState = updateState({}, true); - const secondState = updateState(firstState, true); - const thirdState = updateState(secondState, false); - expect(spy).toHaveBeenCalledTimes(3); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "first date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": true, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "first date string", - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "first date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - expect(thirdState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "first date string", - "isTriggered": false, - "lastCheckedAt": "third date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "second date string", - } - `); - }); - - it('correctly marks state fields across multiple triggers/resolutions', () => { - spy - .mockImplementationOnce(() => 'first date string') - .mockImplementationOnce(() => 'second date string') - .mockImplementationOnce(() => 'third date string') - .mockImplementationOnce(() => 'fourth date string') - .mockImplementationOnce(() => 'fifth date string'); - const firstState = updateState({}, false); - const secondState = updateState(firstState, true); - const thirdState = updateState(secondState, false); - const fourthState = updateState(thirdState, true); - const fifthState = updateState(fourthState, false); - expect(spy).toHaveBeenCalledTimes(5); - expect(firstState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": undefined, - "isTriggered": false, - "lastCheckedAt": "first date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": undefined, - } - `); - expect(secondState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "second date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "second date string", - "lastResolvedAt": undefined, - "lastTriggeredAt": "second date string", - } - `); - expect(thirdState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": false, - "lastCheckedAt": "third date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "second date string", - } - `); - expect(fourthState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": "fourth date string", - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": true, - "lastCheckedAt": "fourth date string", - "lastResolvedAt": "third date string", - "lastTriggeredAt": "fourth date string", - } - `); - expect(fifthState).toMatchInlineSnapshot(` - Object { - "currentTriggerStarted": undefined, - "firstCheckedAt": "first date string", - "firstTriggeredAt": "second date string", - "isTriggered": false, - "lastCheckedAt": "fifth date string", - "lastResolvedAt": "fifth date string", - "lastTriggeredAt": "fourth date string", - } - `); - }); - }); - describe('uniqueMonitorIds', () => { let items: GetMonitorStatusResult[]; beforeEach(() => { diff --git a/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts b/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts new file mode 100644 index 0000000000000..2b74d608417cc --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/__tests__/tls.test.ts @@ -0,0 +1,147 @@ +/* + * 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 { getCertSummary } from '../tls'; +import { Cert } from '../../../../common/runtime_types'; + +describe('tls alert', () => { + describe('getCertSummary', () => { + let mockCerts: Cert[]; + let diffSpy: jest.SpyInstance; + + beforeEach(() => { + diffSpy = jest.spyOn(moment.prototype, 'diff'); + mockCerts = [ + { + not_after: '2020-07-16T03:15:39.000Z', + not_before: '2019-07-24T03:15:39.000Z', + common_name: 'Common-One', + monitors: [{ name: 'monitor-one', id: 'monitor1' }], + sha256: 'abc', + }, + { + not_after: '2020-07-18T03:15:39.000Z', + not_before: '2019-07-20T03:15:39.000Z', + common_name: 'Common-Two', + monitors: [{ name: 'monitor-two', id: 'monitor2' }], + sha256: 'bcd', + }, + { + not_after: '2020-07-19T03:15:39.000Z', + not_before: '2019-07-22T03:15:39.000Z', + common_name: 'Common-Three', + monitors: [{ name: 'monitor-three', id: 'monitor3' }], + sha256: 'cde', + }, + { + not_after: '2020-07-25T03:15:39.000Z', + not_before: '2019-07-25T03:15:39.000Z', + common_name: 'Common-Four', + monitors: [{ name: 'monitor-four', id: 'monitor4' }], + sha256: 'def', + }, + ]; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('sorts expiring certs appropriately when creating summary', () => { + diffSpy + .mockReturnValueOnce(900) + .mockReturnValueOnce(901) + .mockReturnValueOnce(902); + const result = getCertSummary( + mockCerts, + new Date('2020-07-20T05:00:00.000Z').valueOf(), + new Date('2019-03-01T00:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "", + "agingCount": 0, + "count": 4, + "expiringCommonNameAndDate": "Common-One, expired on 2020-07-16T03:15:39.000Z 900 days ago.; Common-Two, expired on 2020-07-18T03:15:39.000Z 901 days ago.; Common-Three, expired on 2020-07-19T03:15:39.000Z 902 days ago.", + "expiringCount": 3, + "hasAging": null, + "hasExpired": true, + } + `); + }); + + it('sorts aging certs appropriate when creating summary', () => { + diffSpy + .mockReturnValueOnce(702) + .mockReturnValueOnce(701) + .mockReturnValueOnce(700); + const result = getCertSummary( + mockCerts, + new Date('2020-07-01T12:00:00.000Z').valueOf(), + new Date('2019-09-01T03:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "Common-Two, valid since 2019-07-20T03:15:39.000Z, 702 days ago.; Common-Three, valid since 2019-07-22T03:15:39.000Z, 701 days ago.; Common-One, valid since 2019-07-24T03:15:39.000Z, 700 days ago.", + "agingCount": 4, + "count": 4, + "expiringCommonNameAndDate": "", + "expiringCount": 0, + "hasAging": true, + "hasExpired": null, + } + `); + }); + + it('handles negative diff values appropriately for aging certs', () => { + diffSpy + .mockReturnValueOnce(700) + .mockReturnValueOnce(-90) + .mockReturnValueOnce(-80); + const result = getCertSummary( + mockCerts, + new Date('2020-07-01T12:00:00.000Z').valueOf(), + new Date('2019-09-01T03:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "Common-Two, valid since 2019-07-20T03:15:39.000Z, 700 days ago.; Common-Three, invalid until 2019-07-22T03:15:39.000Z, 90 days from now.; Common-One, invalid until 2019-07-24T03:15:39.000Z, 80 days from now.", + "agingCount": 4, + "count": 4, + "expiringCommonNameAndDate": "", + "expiringCount": 0, + "hasAging": true, + "hasExpired": null, + } + `); + }); + + it('handles negative diff values appropriately for expiring certs', () => { + diffSpy + // negative days are in the future, positive days are in the past + .mockReturnValueOnce(-96) + .mockReturnValueOnce(-94) + .mockReturnValueOnce(2); + const result = getCertSummary( + mockCerts, + new Date('2020-07-20T05:00:00.000Z').valueOf(), + new Date('2019-03-01T00:00:00.000Z').valueOf() + ); + expect(result).toMatchInlineSnapshot(` + Object { + "agingCommonNameAndDate": "", + "agingCount": 0, + "count": 4, + "expiringCommonNameAndDate": "Common-One, expires on 2020-07-16T03:15:39.000Z in 96 days.; Common-Two, expires on 2020-07-18T03:15:39.000Z in 94 days.; Common-Three, expired on 2020-07-19T03:15:39.000Z 2 days ago.", + "expiringCount": 3, + "hasAging": null, + "hasExpired": true, + } + `); + }); + }); +}); diff --git a/x-pack/plugins/uptime/server/lib/alerts/common.ts b/x-pack/plugins/uptime/server/lib/alerts/common.ts new file mode 100644 index 0000000000000..f33cd9052d229 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/common.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 { isRight } from 'fp-ts/lib/Either'; +import { UptimeCommonState, UptimeCommonStateType } from '../../../common/runtime_types'; + +export type UpdateUptimeAlertState = ( + state: Record, + isTriggeredNow: boolean +) => UptimeCommonState; + +export const updateState: UpdateUptimeAlertState = (state, isTriggeredNow) => { + const now = new Date().toISOString(); + const decoded = UptimeCommonStateType.decode(state); + if (!isRight(decoded)) { + const triggerVal = isTriggeredNow ? now : undefined; + return { + currentTriggerStarted: triggerVal, + firstCheckedAt: now, + firstTriggeredAt: triggerVal, + isTriggered: isTriggeredNow, + lastTriggeredAt: triggerVal, + lastCheckedAt: now, + lastResolvedAt: undefined, + }; + } + const { + currentTriggerStarted, + firstCheckedAt, + firstTriggeredAt, + lastTriggeredAt, + // this is the stale trigger status, we're naming it `wasTriggered` + // to differentiate it from the `isTriggeredNow` param + isTriggered: wasTriggered, + lastResolvedAt, + } = decoded.right; + + let cts: string | undefined; + if (isTriggeredNow && !currentTriggerStarted) { + cts = now; + } else if (isTriggeredNow) { + cts = currentTriggerStarted; + } + return { + currentTriggerStarted: cts, + firstCheckedAt: firstCheckedAt ?? now, + firstTriggeredAt: isTriggeredNow && !firstTriggeredAt ? now : firstTriggeredAt, + lastCheckedAt: now, + lastTriggeredAt: isTriggeredNow ? now : lastTriggeredAt, + lastResolvedAt: !isTriggeredNow && wasTriggered ? now : lastResolvedAt, + isTriggered: isTriggeredNow, + }; +}; diff --git a/x-pack/plugins/uptime/server/lib/alerts/index.ts b/x-pack/plugins/uptime/server/lib/alerts/index.ts index 0e61fd70e0024..661df39ece628 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/index.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/index.ts @@ -6,5 +6,9 @@ import { UptimeAlertTypeFactory } from './types'; import { statusCheckAlertFactory } from './status_check'; +import { tlsAlertFactory } from './tls'; -export const uptimeAlertTypeFactories: UptimeAlertTypeFactory[] = [statusCheckAlertFactory]; +export const uptimeAlertTypeFactories: UptimeAlertTypeFactory[] = [ + statusCheckAlertFactory, + tlsAlertFactory, +]; diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts index f9df559a3977b..0eaa12e8f4372 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.ts @@ -11,13 +11,11 @@ import { i18n } from '@kbn/i18n'; import { AlertExecutorOptions } from '../../../../alerting/server'; import { UptimeAlertTypeFactory } from './types'; import { GetMonitorStatusResult } from '../requests'; -import { - StatusCheckExecutorParamsType, - StatusCheckAlertStateType, - StatusCheckAlertState, -} from '../../../common/runtime_types'; +import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; import { ACTION_GROUP_DEFINITIONS } from '../../../common/constants'; import { savedObjectsAdapter } from '../saved_objects'; +import { updateState } from './common'; +import { commonStateTranslations } from './translations'; const { MONITOR_STATUS } = ACTION_GROUP_DEFINITIONS; @@ -34,7 +32,7 @@ export const uniqueMonitorIds = (items: GetMonitorStatusResult[]): Set = /** * Generates a message to include in contexts of alerts. * @param monitors the list of monitors to include in the message - * @param max + * @param max the maximum number of items the summary should contain */ export const contextMessage = (monitorIds: string[], max: number): string => { const MIN = 2; @@ -122,58 +120,11 @@ export const fullListByIdAndLocation = ( ); }; -export const updateState = ( - state: Record, - isTriggeredNow: boolean -): StatusCheckAlertState => { - const now = new Date().toISOString(); - const decoded = StatusCheckAlertStateType.decode(state); - if (!isRight(decoded)) { - const triggerVal = isTriggeredNow ? now : undefined; - return { - currentTriggerStarted: triggerVal, - firstCheckedAt: now, - firstTriggeredAt: triggerVal, - isTriggered: isTriggeredNow, - lastTriggeredAt: triggerVal, - lastCheckedAt: now, - lastResolvedAt: undefined, - }; - } - const { - currentTriggerStarted, - firstCheckedAt, - firstTriggeredAt, - lastTriggeredAt, - // this is the stale trigger status, we're naming it `wasTriggered` - // to differentiate it from the `isTriggeredNow` param - isTriggered: wasTriggered, - lastResolvedAt, - } = decoded.right; - - let cts: string | undefined; - if (isTriggeredNow && !currentTriggerStarted) { - cts = now; - } else if (isTriggeredNow) { - cts = currentTriggerStarted; - } - - return { - currentTriggerStarted: cts, - firstCheckedAt: firstCheckedAt ?? now, - firstTriggeredAt: isTriggeredNow && !firstTriggeredAt ? now : firstTriggeredAt, - lastCheckedAt: now, - lastTriggeredAt: isTriggeredNow ? now : lastTriggeredAt, - lastResolvedAt: !isTriggeredNow && wasTriggered ? now : lastResolvedAt, - isTriggered: isTriggeredNow, - }; -}; - // Right now the maximum number of monitors shown in the message is hardcoded here. // we might want to make this a parameter in the future const DEFAULT_MAX_MESSAGE_ROWS = 3; -export const statusCheckAlertFactory: UptimeAlertTypeFactory = (server, libs) => ({ +export const statusCheckAlertFactory: UptimeAlertTypeFactory = (_server, libs) => ({ id: 'xpack.uptime.alerts.monitorStatus', name: i18n.translate('xpack.uptime.alerts.monitorStatus', { defaultMessage: 'Uptime monitor status', @@ -218,72 +169,7 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = (server, libs) => ), }, ], - state: [ - { - name: 'firstCheckedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt', - { - defaultMessage: 'Timestamp indicating when this alert first checked', - } - ), - }, - { - name: 'firstTriggeredAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt', - { - defaultMessage: 'Timestamp indicating when the alert first triggered', - } - ), - }, - { - name: 'currentTriggerStarted', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted', - { - defaultMessage: - 'Timestamp indicating when the current trigger state began, if alert is triggered', - } - ), - }, - { - name: 'isTriggered', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.isTriggered', - { - defaultMessage: `Flag indicating if the alert is currently triggering`, - } - ), - }, - { - name: 'lastCheckedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastCheckedAt', - { - defaultMessage: `Timestamp indicating the alert's most recent check time`, - } - ), - }, - { - name: 'lastResolvedAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastResolvedAt', - { - defaultMessage: `Timestamp indicating the most recent resolution time for this alert`, - } - ), - }, - { - name: 'lastTriggeredAt', - description: i18n.translate( - 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastTriggeredAt', - { - defaultMessage: `Timestamp indicating the alert's most recent trigger time`, - } - ), - }, - ], + state: [...commonStateTranslations], }, async executor(options: AlertExecutorOptions) { const { params: rawParams } = options; diff --git a/x-pack/plugins/uptime/server/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/lib/alerts/tls.ts new file mode 100644 index 0000000000000..518e3ed93b424 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/tls.ts @@ -0,0 +1,152 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { UptimeAlertTypeFactory } from './types'; +import { savedObjectsAdapter } from '../saved_objects'; +import { updateState } from './common'; +import { ACTION_GROUP_DEFINITIONS, DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; +import { Cert, CertResult } from '../../../common/runtime_types'; +import { commonStateTranslations, tlsTranslations } from './translations'; + +const { TLS } = ACTION_GROUP_DEFINITIONS; + +const DEFAULT_FROM = 'now-1d'; +const DEFAULT_TO = 'now'; +const DEFAULT_INDEX = 0; +const DEFAULT_SIZE = 20; + +interface TlsAlertState { + count: number; + agingCount: number; + agingCommonNameAndDate: string; + expiringCount: number; + expiringCommonNameAndDate: string; + hasAging: true | null; + hasExpired: true | null; +} + +const sortCerts = (a: string, b: string) => new Date(a).valueOf() - new Date(b).valueOf(); + +const mapCertsToSummaryString = ( + certs: Cert[], + certLimitMessage: (cert: Cert) => string, + maxSummaryItems: number +): string => + certs + .slice(0, maxSummaryItems) + .map(cert => `${cert.common_name}, ${certLimitMessage(cert)}`) + .reduce((prev, cur) => (prev === '' ? cur : prev.concat(`; ${cur}`)), ''); + +const getValidAfter = ({ not_after: date }: Cert) => { + if (!date) return 'Error, missing `certificate_not_valid_after` date.'; + const relativeDate = moment().diff(date, 'days'); + return relativeDate >= 0 + ? tlsTranslations.validAfterExpiredString(date, relativeDate) + : tlsTranslations.validAfterExpiringString(date, Math.abs(relativeDate)); +}; + +const getValidBefore = ({ not_before: date }: Cert): string => { + if (!date) return 'Error, missing `certificate_not_valid_before` date.'; + const relativeDate = moment().diff(date, 'days'); + return relativeDate >= 0 + ? tlsTranslations.validBeforeExpiredString(date, relativeDate) + : tlsTranslations.validBeforeExpiringString(date, Math.abs(relativeDate)); +}; + +export const getCertSummary = ( + certs: Cert[], + expirationThreshold: number, + ageThreshold: number, + maxSummaryItems: number = 3 +): TlsAlertState => { + certs.sort((a, b) => sortCerts(a.not_after ?? '', b.not_after ?? '')); + const expiring = certs.filter( + cert => new Date(cert.not_after ?? '').valueOf() < expirationThreshold + ); + + certs.sort((a, b) => sortCerts(a.not_before ?? '', b.not_before ?? '')); + const aging = certs.filter(cert => new Date(cert.not_before ?? '').valueOf() < ageThreshold); + + return { + count: certs.length, + agingCount: aging.length, + agingCommonNameAndDate: mapCertsToSummaryString(aging, getValidBefore, maxSummaryItems), + expiringCommonNameAndDate: mapCertsToSummaryString(expiring, getValidAfter, maxSummaryItems), + expiringCount: expiring.length, + hasAging: aging.length > 0 ? true : null, + hasExpired: expiring.length > 0 ? true : null, + }; +}; + +export const tlsAlertFactory: UptimeAlertTypeFactory = (_server, libs) => ({ + id: 'xpack.uptime.alerts.tls', + name: tlsTranslations.alertFactoryName, + validate: { + params: schema.object({}), + }, + defaultActionGroupId: TLS.id, + actionGroups: [ + { + id: TLS.id, + name: TLS.name, + }, + ], + actionVariables: { + context: [], + state: [...tlsTranslations.actionVariables, ...commonStateTranslations], + }, + async executor(options) { + const { + services: { alertInstanceFactory, callCluster, savedObjectsClient }, + state, + } = options; + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); + + const { certs, total }: CertResult = await libs.requests.getCerts({ + callES: callCluster, + dynamicSettings, + from: DEFAULT_FROM, + to: DEFAULT_TO, + index: DEFAULT_INDEX, + size: DEFAULT_SIZE, + notValidAfter: `now+${dynamicSettings.certThresholds?.expiration ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.expiration}d`, + notValidBefore: `now-${dynamicSettings.certThresholds?.age ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.age}d`, + sortBy: 'common_name', + direction: 'desc', + }); + + const foundCerts = total > 0; + + if (foundCerts) { + const absoluteExpirationThreshold = moment() + .add( + dynamicSettings.certThresholds?.expiration ?? + DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.expiration, + 'd' + ) + .valueOf(); + const absoluteAgeThreshold = moment() + .subtract( + dynamicSettings.certThresholds?.age ?? DYNAMIC_SETTINGS_DEFAULTS.certThresholds?.age, + 'd' + ) + .valueOf(); + const alertInstance = alertInstanceFactory(TLS.id); + const summary = getCertSummary(certs, absoluteExpirationThreshold, absoluteAgeThreshold); + alertInstance.replaceState({ + ...updateState(state, foundCerts), + ...summary, + }); + alertInstance.scheduleActions(TLS.id); + } + + return updateState(state, foundCerts); + }, +}); diff --git a/x-pack/plugins/uptime/server/lib/alerts/translations.ts b/x-pack/plugins/uptime/server/lib/alerts/translations.ts new file mode 100644 index 0000000000000..e41930aad5af0 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/alerts/translations.ts @@ -0,0 +1,150 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +export const commonStateTranslations = [ + { + name: 'firstCheckedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstCheckedAt', + { + defaultMessage: 'Timestamp indicating when this alert first checked', + } + ), + }, + { + name: 'firstTriggeredAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.firstTriggeredAt', + { + defaultMessage: 'Timestamp indicating when the alert first triggered', + } + ), + }, + { + name: 'currentTriggerStarted', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.currentTriggerStarted', + { + defaultMessage: + 'Timestamp indicating when the current trigger state began, if alert is triggered', + } + ), + }, + { + name: 'isTriggered', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.isTriggered', + { + defaultMessage: `Flag indicating if the alert is currently triggering`, + } + ), + }, + { + name: 'lastCheckedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastCheckedAt', + { + defaultMessage: `Timestamp indicating the alert's most recent check time`, + } + ), + }, + { + name: 'lastResolvedAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastResolvedAt', + { + defaultMessage: `Timestamp indicating the most recent resolution time for this alert`, + } + ), + }, + { + name: 'lastTriggeredAt', + description: i18n.translate( + 'xpack.uptime.alerts.monitorStatus.actionVariables.state.lastTriggeredAt', + { + defaultMessage: `Timestamp indicating the alert's most recent trigger time`, + } + ), + }, +]; + +export const tlsTranslations = { + alertFactoryName: i18n.translate('xpack.uptime.alerts.tls', { + defaultMessage: 'Uptime TLS', + }), + actionVariables: [ + { + name: 'count', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.count', { + defaultMessage: 'The number of certs detected by the alert executor', + }), + }, + { + name: 'expiringCount', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.expiringCount', { + defaultMessage: 'The number of expiring certs detected by the alert.', + }), + }, + { + name: 'expiringCommonNameAndDate', + description: i18n.translate( + 'xpack.uptime.alerts.tls.actionVariables.state.expiringCommonNameAndDate', + { + defaultMessage: 'The common names and expiration date/time of the detected certs', + } + ), + }, + { + name: 'agingCount', + description: i18n.translate('xpack.uptime.alerts.tls.actionVariables.state.agingCount', { + defaultMessage: 'The number of detected certs that are becoming too old.', + }), + }, + { + name: 'agingCommonNameAndDate', + description: i18n.translate( + 'xpack.uptime.alerts.tls.actionVariables.state.agingCommonNameAndDate', + { + defaultMessage: 'The common names and expiration date/time of the detected certs.', + } + ), + }, + ], + validAfterExpiredString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validAfterExpiredString', { + defaultMessage: `expired on {date} {relativeDate} days ago.`, + values: { + date, + relativeDate, + }, + }), + validAfterExpiringString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validAfterExpiringString', { + defaultMessage: `expires on {date} in {relativeDate} days.`, + values: { + date, + relativeDate, + }, + }), + validBeforeExpiredString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validBeforeExpiredString', { + defaultMessage: 'valid since {date}, {relativeDate} days ago.', + values: { + date, + relativeDate, + }, + }), + validBeforeExpiringString: (date: string, relativeDate: number) => + i18n.translate('xpack.uptime.alerts.tls.validBeforeExpiringString', { + defaultMessage: 'invalid until {date}, {relativeDate} days from now.', + values: { + date, + relativeDate, + }, + }), +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index 6820cd69376d1..57a59936ddf7c 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -20,8 +20,10 @@ export const getCerts: UMElasticsearchQueryFn = asyn index, from, to, - search, size, + search, + notValidBefore, + notValidAfter, sortBy, direction, }) => { @@ -91,6 +93,10 @@ export const getCerts: UMElasticsearchQueryFn = asyn }, }; + if (!params.body.query.bool.should) { + params.body.query.bool.should = []; + } + if (search) { params.body.query.bool.minimum_should_match = 1; params.body.query.bool.should = [ @@ -110,6 +116,35 @@ export const getCerts: UMElasticsearchQueryFn = asyn ]; } + if (notValidBefore || notValidAfter) { + const validityFilters: any = { + bool: { + should: [], + }, + }; + if (notValidBefore) { + validityFilters.bool.should.push({ + range: { + 'tls.certificate_not_valid_before': { + lte: notValidBefore, + }, + }, + }); + } + if (notValidAfter) { + validityFilters.bool.should.push({ + range: { + 'tls.certificate_not_valid_after': { + lte: notValidAfter, + }, + }, + }); + } + + params.body.query.bool.filter.push(validityFilters); + } + + // console.log(JSON.stringify(params, null, 2)); const result = await callES('search', params); const certs = (result?.hits?.hits ?? []).map((hit: any) => { diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index f77c14e960ab2..2084c1a572058 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -46,6 +46,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./monitor_states_generated')); loadTestFile(require.resolve('./telemetry_collectors')); }); + describe('with real-world data', () => { beforeEach('load heartbeat data', async () => await esArchiver.load('uptime/full_heartbeat')); afterEach('unload', async () => await esArchiver.unload('uptime/full_heartbeat')); diff --git a/x-pack/test/functional/services/uptime/alerts.ts b/x-pack/test/functional/services/uptime/alerts.ts index 3a8193ff3d327..03620621d68f1 100644 --- a/x-pack/test/functional/services/uptime/alerts.ts +++ b/x-pack/test/functional/services/uptime/alerts.ts @@ -13,6 +13,7 @@ export function UptimeAlertsProvider({ getService }: FtrProviderContext) { return { async openFlyout() { await testSubjects.click('xpack.uptime.alertsPopover.toggleButton', 5000); + await testSubjects.click('xpack.uptime.openAlertContextPanel', 5000); await testSubjects.click('xpack.uptime.toggleAlertFlyout', 5000); }, async openMonitorStatusAlertType(alertType: string) { From 2f080c43331ff85e39498615743d4e7f2186f60e Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 4 May 2020 20:49:45 -0700 Subject: [PATCH 22/55] Fix data streams returning 500 when there are no matching indices (#65152) (#65199) Co-authored-by: Elastic Machine --- .../server/routes/data_streams/handlers.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts index a24518d644c4c..0d2909edf00c4 100644 --- a/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/data_streams/handlers.ts @@ -21,11 +21,7 @@ export const getListHandler: RequestHandler = async (context, request, response) // Get all matching indices and info about each // This returns the top 100,000 indices (as buckets) by last activity - const { - aggregations: { - index: { buckets: indexResults }, - }, - } = await callCluster('search', { + const { aggregations } = await callCluster('search', { index: DATA_STREAM_INDEX_PATTERN, body: { size: 0, @@ -90,6 +86,20 @@ export const getListHandler: RequestHandler = async (context, request, response) }, }); + const body: GetDataStreamsResponse = { + data_streams: [], + }; + + if (!(aggregations && aggregations.index && aggregations.index.buckets)) { + return response.ok({ + body, + }); + } + + const { + index: { buckets: indexResults }, + } = aggregations; + const dataStreams: DataStream[] = (indexResults as any[]).map(result => { const { key: indexName, @@ -110,9 +120,8 @@ export const getListHandler: RequestHandler = async (context, request, response) }; }); - const body: GetDataStreamsResponse = { - data_streams: dataStreams, - }; + body.data_streams = dataStreams; + return response.ok({ body, }); From 5c6795aaf5f39f311ca942f32a7da7c3c83a8104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 5 May 2020 08:05:28 +0100 Subject: [PATCH 23/55] [APM] Ensure that `/api/apm/security/indices_privileges` doesn't fail when security is disabled (#64627) (#65156) * logging when security api is disable * logging when security api is disable * checking statuc code 400 * adding security plugin * checking if security plugin is enabled before calling it * fixing unit test * show apm ui when index is empty * addressing PR comments * refactoring * refactoring Co-authored-by: Elastic Machine Co-authored-by: Elastic Machine --- x-pack/plugins/apm/kibana.json | 3 +- ...icesPermission.test.tsx => index.test.tsx} | 92 ++++++----- .../app/APMIndicesPermission/index.tsx | 20 ++- .../apm/server/lib/helpers/es_client.ts | 3 +- .../security/get_indices_privileges.test.ts | 148 ++++++++++++++++++ .../lib/security/get_indices_privileges.ts | 16 +- .../server/lib/security/get_permissions.ts | 32 ---- x-pack/plugins/apm/server/plugin.ts | 7 +- .../server/routes/create_api/index.test.ts | 3 +- .../apm/server/routes/create_api/index.ts | 5 +- x-pack/plugins/apm/server/routes/security.ts | 6 +- x-pack/plugins/apm/server/routes/typings.ts | 7 + 12 files changed, 255 insertions(+), 87 deletions(-) rename x-pack/plugins/apm/public/components/app/APMIndicesPermission/{__test__/APMIndicesPermission.test.tsx => index.test.tsx} (63%) create mode 100644 x-pack/plugins/apm/server/lib/security/get_indices_privileges.test.ts delete mode 100644 x-pack/plugins/apm/server/lib/security/get_permissions.ts diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 1a0ad67c7b696..85e3761129018 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -15,7 +15,8 @@ "usageCollection", "taskManager", "actions", - "alerting" + "alerting", + "security" ], "server": true, "ui": true, diff --git a/x-pack/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.test.tsx similarity index 63% rename from x-pack/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx rename to x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.test.tsx index 68acaee4abe5d..b3f90fd9aee34 100644 --- a/x-pack/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx +++ b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.test.tsx @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, act } from '@testing-library/react'; import { shallow } from 'enzyme'; -import { APMIndicesPermission } from '../'; +import { APMIndicesPermission } from './'; -import * as hooks from '../../../../hooks/useFetcher'; +import * as hooks from '../../../hooks/useFetcher'; import { expectTextsInDocument, expectTextsNotInDocument -} from '../../../../utils/testHelpers'; -import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; +} from '../../../utils/testHelpers'; +import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext'; describe('APMIndicesPermission', () => { it('returns empty component when api status is loading', () => { @@ -34,7 +34,10 @@ describe('APMIndicesPermission', () => { spyOn(hooks, 'useFetcher').and.returnValue({ status: hooks.FETCH_STATUS.SUCCESS, data: { - 'apm-*': { read: false } + has_all_requested: false, + index: { + 'apm-*': { read: false } + } } }); const component = render( @@ -48,39 +51,32 @@ describe('APMIndicesPermission', () => { 'apm-*' ]); }); - it('shows escape hatch button when at least one indice has read privileges', () => { + + it('shows children component when no index is returned', () => { spyOn(hooks, 'useFetcher').and.returnValue({ status: hooks.FETCH_STATUS.SUCCESS, data: { - 'apm-7.5.1-error-*': { read: false }, - 'apm-7.5.1-metric-*': { read: false }, - 'apm-7.5.1-transaction-*': { read: false }, - 'apm-7.5.1-span-*': { read: true } + has_all_requested: false, + index: {} } }); const component = render( - + +

My amazing component

+
); - expectTextsInDocument(component, [ - 'Missing permissions to access APM', - 'apm-7.5.1-error-*', - 'apm-7.5.1-metric-*', - 'apm-7.5.1-transaction-*', - 'Dismiss' - ]); - expectTextsNotInDocument(component, ['apm-7.5.1-span-*']); + expectTextsNotInDocument(component, ['Missing permissions to access APM']); + expectTextsInDocument(component, ['My amazing component']); }); it('shows children component when indices have read privileges', () => { spyOn(hooks, 'useFetcher').and.returnValue({ status: hooks.FETCH_STATUS.SUCCESS, data: { - 'apm-7.5.1-error-*': { read: true }, - 'apm-7.5.1-metric-*': { read: true }, - 'apm-7.5.1-transaction-*': { read: true }, - 'apm-7.5.1-span-*': { read: true } + has_all_requested: true, + index: {} } }); const component = render( @@ -90,13 +86,7 @@ describe('APMIndicesPermission', () => { ); - expectTextsNotInDocument(component, [ - 'Missing permissions to access APM', - 'apm-7.5.1-error-*', - 'apm-7.5.1-metric-*', - 'apm-7.5.1-transaction-*', - 'apm-7.5.1-span-*' - ]); + expectTextsNotInDocument(component, ['Missing permissions to access APM']); expectTextsInDocument(component, ['My amazing component']); }); @@ -104,10 +94,13 @@ describe('APMIndicesPermission', () => { spyOn(hooks, 'useFetcher').and.returnValue({ status: hooks.FETCH_STATUS.SUCCESS, data: { - 'apm-7.5.1-error-*': { read: false }, - 'apm-7.5.1-metric-*': { read: false }, - 'apm-7.5.1-transaction-*': { read: false }, - 'apm-7.5.1-span-*': { read: true } + has_all_requested: false, + index: { + 'apm-error-*': { read: false }, + 'apm-trasanction-*': { read: false }, + 'apm-metrics-*': { read: true }, + 'apm-span-*': { read: true } + } } }); const component = render( @@ -117,8 +110,33 @@ describe('APMIndicesPermission', () => { ); - expectTextsInDocument(component, ['Dismiss']); - fireEvent.click(component.getByText('Dismiss')); + expectTextsInDocument(component, [ + 'Dismiss', + 'apm-error-*', + 'apm-trasanction-*' + ]); + act(() => { + fireEvent.click(component.getByText('Dismiss')); + }); + expectTextsInDocument(component, ['My amazing component']); + }); + + it("shows children component when api doesn't return value", () => { + spyOn(hooks, 'useFetcher').and.returnValue({}); + const component = render( + + +

My amazing component

+
+
+ ); + expectTextsNotInDocument(component, [ + 'Missing permissions to access APM', + 'apm-7.5.1-error-*', + 'apm-7.5.1-metric-*', + 'apm-7.5.1-transaction-*', + 'apm-7.5.1-span-*' + ]); expectTextsInDocument(component, ['My amazing component']); }); }); diff --git a/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx index 40e039dcd40c5..9074726f76e6d 100644 --- a/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx +++ b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx @@ -15,9 +15,9 @@ import { EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { isEmpty } from 'lodash'; import React, { useState } from 'react'; import styled from 'styled-components'; +import { isEmpty } from 'lodash'; import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; import { fontSize, pct, px, units } from '../../../style/variables'; import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink'; @@ -29,7 +29,7 @@ export const APMIndicesPermission: React.FC = ({ children }) => { setIsPermissionWarningDismissed ] = useState(false); - const { data: indicesPrivileges = {}, status } = useFetcher(callApmApi => { + const { data: indicesPrivileges, status } = useFetcher(callApmApi => { return callApmApi({ pathname: '/api/apm/security/indices_privileges' }); @@ -40,13 +40,17 @@ export const APMIndicesPermission: React.FC = ({ children }) => { return null; } - const indicesWithoutPermission = Object.keys(indicesPrivileges).filter( - index => !indicesPrivileges[index].read - ); - // Show permission warning when a user has at least one index without Read privilege, - // and he has not manually dismissed the warning - if (!isEmpty(indicesWithoutPermission) && !isPermissionWarningDismissed) { + // and they have not manually dismissed the warning + if ( + indicesPrivileges && + !indicesPrivileges.has_all_requested && + !isEmpty(indicesPrivileges.index) && + !isPermissionWarningDismissed + ) { + const indicesWithoutPermission = Object.keys( + indicesPrivileges.index + ).filter(index => !indicesPrivileges.index[index].read); return ( = Omit, 'type'>; -interface IndexPrivileges { +export interface IndexPrivileges { has_all_requested: boolean; - username: string; index: Record; } diff --git a/x-pack/plugins/apm/server/lib/security/get_indices_privileges.test.ts b/x-pack/plugins/apm/server/lib/security/get_indices_privileges.test.ts new file mode 100644 index 0000000000000..c1bc48f4ed1fd --- /dev/null +++ b/x-pack/plugins/apm/server/lib/security/get_indices_privileges.test.ts @@ -0,0 +1,148 @@ +/* + * 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 { Setup } from '../helpers/setup_request'; +import { getIndicesPrivileges } from './get_indices_privileges'; + +describe('getIndicesPrivileges', () => { + const indices = { + apm_oss: { + errorIndices: 'apm-*', + metricsIndices: 'apm-*', + transactionIndices: 'apm-*', + spanIndices: 'apm-*' + } + }; + it('return that the user has privileges when security plugin is disabled', async () => { + const setup = ({ + indices, + client: { + hasPrivileges: () => { + const error = { + message: + 'no handler found for uri [/_security/user/_has_privileges]', + statusCode: 400 + }; + throw error; + } + } + } as unknown) as Setup; + const privileges = await getIndicesPrivileges({ + setup, + isSecurityPluginEnabled: false + }); + expect(privileges).toEqual({ + has_all_requested: true, + index: {} + }); + }); + it('throws when an error happens while fetching indices privileges', async () => { + const setup = ({ + indices, + client: { + hasPrivileges: () => { + throw new Error('unknow error'); + } + } + } as unknown) as Setup; + await expect( + getIndicesPrivileges({ setup, isSecurityPluginEnabled: true }) + ).rejects.toThrowError('unknow error'); + }); + it("has privileges to read from 'apm-*'", async () => { + const setup = ({ + indices, + client: { + hasPrivileges: () => { + return Promise.resolve({ + has_all_requested: true, + index: { 'apm-*': { read: true } } + }); + } + } + } as unknown) as Setup; + const privileges = await getIndicesPrivileges({ + setup, + isSecurityPluginEnabled: true + }); + + expect(privileges).toEqual({ + has_all_requested: true, + index: { + 'apm-*': { + read: true + } + } + }); + }); + + it("doesn't have privileges to read from 'apm-*'", async () => { + const setup = ({ + indices, + client: { + hasPrivileges: () => { + return Promise.resolve({ + has_all_requested: false, + index: { 'apm-*': { read: false } } + }); + } + } + } as unknown) as Setup; + + const privileges = await getIndicesPrivileges({ + setup, + isSecurityPluginEnabled: true + }); + + expect(privileges).toEqual({ + has_all_requested: false, + index: { + 'apm-*': { + read: false + } + } + }); + }); + it("doesn't have privileges on multiple indices", async () => { + const setup = ({ + indices: { + apm_oss: { + errorIndices: 'apm-error-*', + metricsIndices: 'apm-metrics-*', + transactionIndices: 'apm-trasanction-*', + spanIndices: 'apm-span-*' + } + }, + client: { + hasPrivileges: () => { + return Promise.resolve({ + has_all_requested: false, + index: { + 'apm-error-*': { read: false }, + 'apm-trasanction-*': { read: false }, + 'apm-metrics-*': { read: true }, + 'apm-span-*': { read: true } + } + }); + } + } + } as unknown) as Setup; + + const privileges = await getIndicesPrivileges({ + setup, + isSecurityPluginEnabled: true + }); + + expect(privileges).toEqual({ + has_all_requested: false, + index: { + 'apm-error-*': { read: false }, + 'apm-trasanction-*': { read: false }, + 'apm-metrics-*': { read: true }, + 'apm-span-*': { read: true } + } + }); + }); +}); diff --git a/x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts b/x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts index 1a80a13b2ad19..46ed64f518bb8 100644 --- a/x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts +++ b/x-pack/plugins/apm/server/lib/security/get_indices_privileges.ts @@ -4,8 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ import { Setup } from '../helpers/setup_request'; +import { IndexPrivileges } from '../helpers/es_client'; + +export async function getIndicesPrivileges({ + setup, + isSecurityPluginEnabled +}: { + setup: Setup; + isSecurityPluginEnabled: boolean; +}): Promise { + // When security plugin is not enabled, returns that the user has all requested privileges. + if (!isSecurityPluginEnabled) { + return { has_all_requested: true, index: {} }; + } -export async function getIndicesPrivileges(setup: Setup) { const { client, indices } = setup; const response = await client.hasPrivileges({ index: [ @@ -20,5 +32,5 @@ export async function getIndicesPrivileges(setup: Setup) { } ] }); - return response.index; + return response; } diff --git a/x-pack/plugins/apm/server/lib/security/get_permissions.ts b/x-pack/plugins/apm/server/lib/security/get_permissions.ts deleted file mode 100644 index ed2a1f64e7f84..0000000000000 --- a/x-pack/plugins/apm/server/lib/security/get_permissions.ts +++ /dev/null @@ -1,32 +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 { Setup } from '../helpers/setup_request'; - -export async function getPermissions(setup: Setup) { - const { client, indices } = setup; - - const params = { - index: Object.values(indices), - body: { - size: 0, - query: { - match_all: {} - } - } - }; - - try { - await client.search(params); - return { hasPermission: true }; - } catch (e) { - // If 403, it means the user doesnt have permission. - if (e.status === 403) { - return { hasPermission: false }; - } - // if any other error happens, throw it. - throw e; - } -} diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 8cf29de5b8b73..29ab618cbdd0a 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -12,6 +12,7 @@ import { } from 'src/core/server'; import { Observable, combineLatest } from 'rxjs'; import { map, take } from 'rxjs/operators'; +import { SecurityPluginSetup } from '../../security/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { TaskManagerSetupContract } from '../../task_manager/server'; import { AlertingPlugin } from '../../alerting/server'; @@ -57,6 +58,7 @@ export class APMPlugin implements Plugin { alerting?: AlertingPlugin['setup']; actions?: ActionsPlugin['setup']; features: FeaturesPluginSetup; + security?: SecurityPluginSetup; } ) { this.logger = this.initContext.logger.get(); @@ -110,7 +112,10 @@ export class APMPlugin implements Plugin { createApmApi().init(core, { config$: mergedConfig$, - logger: this.logger! + logger: this.logger!, + plugins: { + security: plugins.security + } }); return { diff --git a/x-pack/plugins/apm/server/routes/create_api/index.test.ts b/x-pack/plugins/apm/server/routes/create_api/index.test.ts index 20c586868a979..6236fcb0a6942 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.test.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.test.ts @@ -39,7 +39,8 @@ const getCoreMock = () => { config$: new BehaviorSubject({} as APMConfig), logger: ({ error: jest.fn() - } as unknown) as Logger + } as unknown) as Logger, + plugins: {} } }; }; diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index a97e2f30fc2b6..9b611a0bbd6bc 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -30,7 +30,7 @@ export function createApi() { factoryFns.push(fn); return this as any; }, - init(core, { config$, logger }) { + init(core, { config$, logger, plugins }) { const router = core.http.createRouter(); let config = {} as APMConfig; @@ -141,7 +141,8 @@ export function createApi() { // it's not defined in the route. params: pick(parsedParams, ...Object.keys(params), 'query'), config, - logger + logger, + plugins } }); diff --git a/x-pack/plugins/apm/server/routes/security.ts b/x-pack/plugins/apm/server/routes/security.ts index 0a8222b665d83..1e2a302ab9a4a 100644 --- a/x-pack/plugins/apm/server/routes/security.ts +++ b/x-pack/plugins/apm/server/routes/security.ts @@ -12,6 +12,10 @@ export const indicesPrivilegesRoute = createRoute(() => ({ path: '/api/apm/security/indices_privileges', handler: async ({ context, request }) => { const setup = await setupRequest(context, request); - return getIndicesPrivileges(setup); + return getIndicesPrivileges({ + setup, + isSecurityPluginEnabled: + context.plugins.security?.license.isEnabled() ?? false + }); } })); diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 6543f2015599b..e049255eb8ec8 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -16,6 +16,7 @@ import { Observable } from 'rxjs'; import { Server } from 'hapi'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { FetchOptions } from '../../public/services/rest/callApi'; +import { SecurityPluginSetup } from '../../../security/public'; import { APMConfig } from '..'; export interface Params { @@ -62,6 +63,9 @@ export type APMRequestHandlerContext< params: { query: { _debug: boolean } } & TDecodedParams; config: APMConfig; logger: Logger; + plugins: { + security?: SecurityPluginSetup; + }; }; export type RouteFactoryFn< @@ -105,6 +109,9 @@ export interface ServerAPI { context: { config$: Observable; logger: Logger; + plugins: { + security?: SecurityPluginSetup; + }; } ) => void; } From ea0482889ab062f6416c730dc0a428d31da27a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 5 May 2020 08:05:56 +0100 Subject: [PATCH 24/55] fixing unit test (#65068) (#65157) --- .../CustomLinkFlyout/LinkPreview.test.tsx | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx index 2f4d9a4c4016d..a1afbd7a807cc 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx @@ -5,9 +5,25 @@ */ import React from 'react'; import { LinkPreview } from '../CustomLinkFlyout/LinkPreview'; -import { render, getNodeText, getByTestId, act } from '@testing-library/react'; +import { + render, + getNodeText, + getByTestId, + act, + wait +} from '@testing-library/react'; +import * as apmApi from '../../../../../../services/rest/createCallApmApi'; describe('LinkPreview', () => { + let callApmApiSpy: jasmine.Spy; + beforeAll(() => { + callApmApiSpy = spyOn(apmApi, 'callApmApi').and.returnValue({ + transaction: { id: 'foo' } + }); + }); + afterAll(() => { + jest.clearAllMocks(); + }); const getElementValue = (container: HTMLElement, id: string) => getNodeText( ((getByTestId(container, id) as HTMLDivElement) @@ -42,7 +58,7 @@ describe('LinkPreview', () => { }); }); - it('shows warning when couldnt replace context variables', () => { + it("shows warning when couldn't replace context variables", () => { act(() => { const { container } = render( { expect(getByTestId(container, 'preview-warning')).toBeInTheDocument(); }); }); + it('replaces url with transaction id', async () => { + const { container } = render( + + ); + await wait(() => expect(callApmApiSpy).toHaveBeenCalled()); + expect(getElementValue(container, 'preview-label')).toEqual('foo'); + expect( + (getByTestId(container, 'preview-link') as HTMLAnchorElement).text + ).toEqual('https://baz.co?transaction=foo'); + }); }); From f62d084f45e47b1edb929825c2494973349e9a5c Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 5 May 2020 10:27:56 +0200 Subject: [PATCH 25/55] [ML] Embeddable Anomaly Swimlane (#65180) (#65203) * [ML] Embeddable Anomaly Swimlane (#64056) * [ML] embeddables setup * [ML] fix initialization * [ML] ts refactoring * [ML] refactor time_buckets.js * [ML] async services * [ML] extract job_selector_flyout.tsx * [ML] fetch overall swimlane data * [ML] import explorer styles * [ML] revert package_globs.ts * [ML] refactor with container, services with DI * [ML] resize throttle, fetch based on chart width * [ML] swimlane embeddable setup * [ML] explorer service * [ML] chart_tooltip_service * [ML] fix types * [ML] overall type for single job with no influencers * [ML] improve anomaly_swimlane_initializer ux * [ML] fix services initialization, unsubscribe on destroy * [ML] support custom time range * [ML] add tooltip * [ML] rollback initGetSwimlaneBucketInterval * [ML] new tooltip service * [ML] MlTooltipComponent with render props, fix warning * [ML] fix typo in the filename * [ML] remove redundant time range output * [ML] fix time_buckets.test.js jest tests * [ML] fix explorer chart tests * [ML] swimlane tests * [ML] store job ids instead of complete job objects * [ML] swimlane limit input * [ML] memo tooltip component, loading indicator * [ML] scrollable content * [ML] support query and filters * [ML] handle query syntax errors * [ML] rename anomaly_swimlane_service * [ML] introduce constants * [ML] edit panel title during setup * [ML] withTimeRangeSelector * [ML] rename explorer_service * [ML] getJobs$ method with one API call * [ML] fix groups selection * [ML] swimlane input resolver hook * [ML] useSwimlaneInputResolver tests * [ML] factory test * [ML] container test * [ML] set wrapper * [ML] tooltip tests * [ML] fix displayScore * [ML] label colors * [ML] support edit mode * [ML] call super render Co-authored-by: Elastic Machine (cherry picked from commit f62df99ae38801b905dd10d281dbf972c5b17578) * [ML] fix typing issues Co-authored-by: Elastic Machine --- x-pack/plugins/ml/kibana.json | 4 +- .../chart_tooltip/chart_tooltip.tsx | 182 +++++----- .../chart_tooltip/chart_tooltip_service.d.ts | 42 --- .../chart_tooltip/chart_tooltip_service.js | 37 -- .../chart_tooltip_service.test.ts | 63 +++- .../chart_tooltip/chart_tooltip_service.ts | 73 ++++ .../components/chart_tooltip/index.ts | 4 +- .../components/job_selector/job_selector.tsx | 280 ++-------------- .../job_selector_badge/{index.js => index.ts} | 0 ...lector_badge.js => job_selector_badge.tsx} | 35 +- .../job_selector/job_selector_flyout.tsx | 289 ++++++++++++++++ .../job_selector_table/job_selector_table.js | 2 +- .../{index.js => index.ts} | 0 ..._badges.js => new_selection_id_badges.tsx} | 32 +- .../datavisualizer/index_based/page.tsx | 4 +- ...s.snap => explorer_swimlane.test.tsx.snap} | 0 .../application/explorer/_explorer.scss | 316 +++++++++--------- .../public/application/explorer/explorer.js | 63 ++-- .../explorer_chart_distribution.js | 12 +- .../explorer_chart_distribution.test.js | 34 +- .../explorer_chart_single_metric.js | 14 +- .../explorer_chart_single_metric.test.js | 34 +- .../explorer_charts_container.js | 109 +++--- .../explorer_charts_container.test.js | 12 +- .../explorer/explorer_constants.ts | 10 +- .../explorer/explorer_dashboard_service.ts | 7 +- ...ane.test.js => explorer_swimlane.test.tsx} | 50 ++- ...orer_swimlane.js => explorer_swimlane.tsx} | 253 ++++++++------ .../application/explorer/explorer_utils.d.ts | 10 +- .../application/explorer/explorer_utils.js | 4 +- .../components/charts/common/settings.ts | 4 +- .../jobs/new_job/pages/new_job/page.tsx | 4 +- .../services/anomaly_detector_service.ts | 58 ++++ .../application/services/explorer_service.ts | 308 +++++++++++++++++ .../application/services/http_service.ts | 104 +++++- .../services/results_service/index.ts | 2 + .../results_service/results_service.d.ts | 11 +- .../timeseries_chart/timeseries_chart.d.ts | 2 + .../timeseries_chart/timeseries_chart.js | 23 +- .../timeseries_chart_annotations.ts | 8 +- .../timeseriesexplorer/timeseriesexplorer.js | 30 +- .../timeseriesexplorer_utils.js | 6 +- .../public/application/util/chart_utils.d.ts | 7 + .../ml/public/application/util/date_utils.ts | 2 +- .../public/application/util/time_buckets.d.ts | 37 +- .../public/application/util/time_buckets.js | 26 +- .../application/util/time_buckets.test.js | 28 +- .../anomaly_swimlane_embeddable.tsx | 115 +++++++ ...omaly_swimlane_embeddable_factory.test.tsx | 51 +++ .../anomaly_swimlane_embeddable_factory.ts | 81 +++++ .../anomaly_swimlane_initializer.tsx | 201 +++++++++++ .../anomaly_swimlane_setup_flyout.tsx | 95 ++++++ .../explorer_swimlane_container.test.tsx | 124 +++++++ .../explorer_swimlane_container.tsx | 122 +++++++ .../embeddables/anomaly_swimlane/index.ts | 8 + .../swimlane_input_resolver.test.ts | 271 +++++++++++++++ .../swimlane_input_resolver.ts | 211 ++++++++++++ x-pack/plugins/ml/public/embeddables/index.ts | 23 ++ x-pack/plugins/ml/public/plugin.ts | 13 + .../ui_actions/edit_swimlane_panel_action.tsx | 65 ++++ x-pack/plugins/ml/public/ui_actions/index.ts | 30 ++ 61 files changed, 3101 insertions(+), 944 deletions(-) delete mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.d.ts delete mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.js create mode 100644 x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.ts rename x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/{index.js => index.ts} (100%) rename x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/{job_selector_badge.js => job_selector_badge.tsx} (68%) create mode 100644 x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx rename x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/{index.js => index.ts} (100%) rename x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/{new_selection_id_badges.js => new_selection_id_badges.tsx} (80%) rename x-pack/plugins/ml/public/application/explorer/__snapshots__/{explorer_swimlane.test.js.snap => explorer_swimlane.test.tsx.snap} (100%) rename x-pack/plugins/ml/public/application/explorer/{explorer_swimlane.test.js => explorer_swimlane.test.tsx} (70%) rename x-pack/plugins/ml/public/application/explorer/{explorer_swimlane.js => explorer_swimlane.tsx} (79%) create mode 100644 x-pack/plugins/ml/public/application/services/anomaly_detector_service.ts create mode 100644 x-pack/plugins/ml/public/application/services/explorer_service.ts create mode 100644 x-pack/plugins/ml/public/application/util/chart_utils.d.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.test.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_setup_flyout.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/explorer_swimlane_container.test.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/explorer_swimlane_container.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/index.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.test.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_swimlane/swimlane_input_resolver.ts create mode 100644 x-pack/plugins/ml/public/embeddables/index.ts create mode 100644 x-pack/plugins/ml/public/ui_actions/edit_swimlane_panel_action.tsx create mode 100644 x-pack/plugins/ml/public/ui_actions/index.ts diff --git a/x-pack/plugins/ml/kibana.json b/x-pack/plugins/ml/kibana.json index 038f61b3a33b7..e9d4aff3484b1 100644 --- a/x-pack/plugins/ml/kibana.json +++ b/x-pack/plugins/ml/kibana.json @@ -13,7 +13,9 @@ "home", "licensing", "usageCollection", - "share" + "share", + "embeddable", + "uiActions" ], "optionalPlugins": [ "security", diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx index 9cc42a4df2f66..decd1275fe884 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip.tsx @@ -4,56 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ +import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; -import React, { useRef, FC } from 'react'; +import TooltipTrigger from 'react-popper-tooltip'; import { TooltipValueFormatter } from '@elastic/charts'; -import useObservable from 'react-use/lib/useObservable'; -import { chartTooltip$, ChartTooltipState, ChartTooltipValue } from './chart_tooltip_service'; +import './_index.scss'; -type RefValue = HTMLElement | null; - -function useRefWithCallback(chartTooltipState?: ChartTooltipState) { - const ref = useRef(null); - - return (node: RefValue) => { - ref.current = node; - - if ( - node !== null && - node.parentElement !== null && - chartTooltipState !== undefined && - chartTooltipState.isTooltipVisible - ) { - const parentBounding = node.parentElement.getBoundingClientRect(); - - const { targetPosition, offset } = chartTooltipState; - - const contentWidth = document.body.clientWidth - parentBounding.left; - const tooltipWidth = node.clientWidth; - - let left = targetPosition.left + offset.x - parentBounding.left; - if (left + tooltipWidth > contentWidth) { - // the tooltip is hanging off the side of the page, - // so move it to the other side of the target - left = left - (tooltipWidth + offset.x); - } - - const top = targetPosition.top + offset.y - parentBounding.top; - - if ( - chartTooltipState.tooltipPosition.left !== left || - chartTooltipState.tooltipPosition.top !== top - ) { - // render the tooltip with adjusted position. - chartTooltip$.next({ - ...chartTooltipState, - tooltipPosition: { left, top }, - }); - } - } - }; -} +import { ChildrenArg, TooltipTriggerProps } from 'react-popper-tooltip/dist/types'; +import { ChartTooltipService, ChartTooltipValue, TooltipData } from './chart_tooltip_service'; const renderHeader = (headerData?: ChartTooltipValue, formatter?: TooltipValueFormatter) => { if (!headerData) { @@ -63,48 +22,101 @@ const renderHeader = (headerData?: ChartTooltipValue, formatter?: TooltipValueFo return formatter ? formatter(headerData) : headerData.label; }; -export const ChartTooltip: FC = () => { - const chartTooltipState = useObservable(chartTooltip$); - const chartTooltipElement = useRefWithCallback(chartTooltipState); +const Tooltip: FC<{ service: ChartTooltipService }> = React.memo(({ service }) => { + const [tooltipData, setData] = useState([]); + const refCallback = useRef(); - if (chartTooltipState === undefined || !chartTooltipState.isTooltipVisible) { - return
; - } + useEffect(() => { + const subscription = service.tooltipState$.subscribe(tooltipState => { + if (refCallback.current) { + // update trigger + refCallback.current(tooltipState.target); + } + setData(tooltipState.tooltipData); + }); + return () => { + subscription.unsubscribe(); + }; + }, []); + + const triggerCallback = useCallback( + (({ triggerRef }) => { + // obtain the reference to the trigger setter callback + // to update the target based on changes from the service. + refCallback.current = triggerRef; + // actual trigger is resolved by the service, hence don't render + return null; + }) as TooltipTriggerProps['children'], + [] + ); + + const tooltipCallback = useCallback( + (({ tooltipRef, getTooltipProps }) => { + return ( +
+ {tooltipData.length > 0 && tooltipData[0].skipHeader === undefined && ( +
{renderHeader(tooltipData[0])}
+ )} + {tooltipData.length > 1 && ( +
+ {tooltipData + .slice(1) + .map(({ label, value, color, isHighlighted, seriesIdentifier, valueAccessor }) => { + const classes = classNames('mlChartTooltip__item', { + /* eslint @typescript-eslint/camelcase:0 */ + echTooltip__rowHighlighted: isHighlighted, + }); + return ( +
+ {label} + {value} +
+ ); + })} +
+ )} +
+ ); + }) as TooltipTriggerProps['tooltip'], + [tooltipData] + ); - const { tooltipData, tooltipHeaderFormatter, tooltipPosition } = chartTooltipState; - const transform = `translate(${tooltipPosition.left}px, ${tooltipPosition.top}px)`; + const isTooltipShown = tooltipData.length > 0; return ( -
- {tooltipData.length > 0 && tooltipData[0].skipHeader === undefined && ( -
- {renderHeader(tooltipData[0], tooltipHeaderFormatter)} -
- )} - {tooltipData.length > 1 && ( -
- {tooltipData - .slice(1) - .map(({ label, value, color, isHighlighted, seriesIdentifier, valueAccessor }) => { - const classes = classNames('mlChartTooltip__item', { - /* eslint @typescript-eslint/camelcase:0 */ - echTooltip__rowHighlighted: isHighlighted, - }); - return ( -
- {label} - {value} -
- ); - })} -
- )} -
+ + {triggerCallback} + + ); +}); + +interface MlTooltipComponentProps { + children: (tooltipService: ChartTooltipService) => React.ReactElement; +} + +export const MlTooltipComponent: FC = ({ children }) => { + const service = useMemo(() => new ChartTooltipService(), []); + + return ( + <> + + {children(service)} + ); }; diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.d.ts b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.d.ts deleted file mode 100644 index e6b0b6b4270bd..0000000000000 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { BehaviorSubject } from 'rxjs'; - -import { TooltipValue, TooltipValueFormatter } from '@elastic/charts'; - -export declare const getChartTooltipDefaultState: () => ChartTooltipState; - -export interface ChartTooltipValue extends TooltipValue { - skipHeader?: boolean; -} - -interface ChartTooltipState { - isTooltipVisible: boolean; - offset: ToolTipOffset; - targetPosition: ClientRect; - tooltipData: ChartTooltipValue[]; - tooltipHeaderFormatter?: TooltipValueFormatter; - tooltipPosition: { left: number; top: number }; -} - -export declare const chartTooltip$: BehaviorSubject; - -interface ToolTipOffset { - x: number; - y: number; -} - -interface MlChartTooltipService { - show: ( - tooltipData: ChartTooltipValue[], - target?: HTMLElement | null, - offset?: ToolTipOffset - ) => void; - hide: () => void; -} - -export declare const mlChartTooltipService: MlChartTooltipService; diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.js b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.js deleted file mode 100644 index 59cf98e5ffd71..0000000000000 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.js +++ /dev/null @@ -1,37 +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 { BehaviorSubject } from 'rxjs'; - -export const getChartTooltipDefaultState = () => ({ - isTooltipVisible: false, - tooltipData: [], - offset: { x: 0, y: 0 }, - targetPosition: { left: 0, top: 0 }, - tooltipPosition: { left: 0, top: 0 }, -}); - -export const chartTooltip$ = new BehaviorSubject(getChartTooltipDefaultState()); - -export const mlChartTooltipService = { - show: (tooltipData, target, offset = { x: 0, y: 0 }) => { - if (typeof target !== 'undefined' && target !== null) { - chartTooltip$.next({ - ...chartTooltip$.getValue(), - isTooltipVisible: true, - offset, - targetPosition: target.getBoundingClientRect(), - tooltipData, - }); - } - }, - hide: () => { - chartTooltip$.next({ - ...getChartTooltipDefaultState(), - isTooltipVisible: false, - }); - }, -}; diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.test.ts b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.test.ts index aa1dbf92b0677..231854cd264c2 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.test.ts +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.test.ts @@ -4,18 +4,61 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getChartTooltipDefaultState, mlChartTooltipService } from './chart_tooltip_service'; +import { + ChartTooltipService, + getChartTooltipDefaultState, + TooltipData, +} from './chart_tooltip_service'; -describe('ML - mlChartTooltipService', () => { - it('service API duck typing', () => { - expect(typeof mlChartTooltipService).toBe('object'); - expect(typeof mlChartTooltipService.show).toBe('function'); - expect(typeof mlChartTooltipService.hide).toBe('function'); +describe('ChartTooltipService', () => { + let service: ChartTooltipService; + + beforeEach(() => { + service = new ChartTooltipService(); + }); + + test('should update the tooltip state on show and hide', () => { + const spy = jest.fn(); + + service.tooltipState$.subscribe(spy); + + expect(spy).toHaveBeenCalledWith(getChartTooltipDefaultState()); + + const update = [ + { + label: 'new tooltip', + }, + ] as TooltipData; + const mockEl = document.createElement('div'); + + service.show(update, mockEl); + + expect(spy).toHaveBeenCalledWith({ + isTooltipVisible: true, + tooltipData: update, + offset: { x: 0, y: 0 }, + target: mockEl, + }); + + service.hide(); + + expect(spy).toHaveBeenCalledWith({ + isTooltipVisible: false, + tooltipData: ([] as unknown) as TooltipData, + offset: { x: 0, y: 0 }, + target: null, + }); }); - it('should fail silently when target is not defined', () => { - expect(() => { - mlChartTooltipService.show(getChartTooltipDefaultState().tooltipData, null); - }).not.toThrow('Call to show() should fail silently.'); + test('update the tooltip state only on a new value', () => { + const spy = jest.fn(); + + service.tooltipState$.subscribe(spy); + + expect(spy).toHaveBeenCalledWith(getChartTooltipDefaultState()); + + service.hide(); + + expect(spy).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.ts b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.ts new file mode 100644 index 0000000000000..b524e18102a95 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/chart_tooltip_service.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 { BehaviorSubject, Observable } from 'rxjs'; +import { isEqual } from 'lodash'; +import { TooltipValue, TooltipValueFormatter } from '@elastic/charts'; +import { distinctUntilChanged } from 'rxjs/operators'; + +export interface ChartTooltipValue extends TooltipValue { + skipHeader?: boolean; +} + +export interface TooltipHeader { + skipHeader: boolean; +} + +export type TooltipData = ChartTooltipValue[]; + +export interface ChartTooltipState { + isTooltipVisible: boolean; + offset: TooltipOffset; + tooltipData: TooltipData; + tooltipHeaderFormatter?: TooltipValueFormatter; + target: HTMLElement | null; +} + +interface TooltipOffset { + x: number; + y: number; +} + +export const getChartTooltipDefaultState = (): ChartTooltipState => ({ + isTooltipVisible: false, + tooltipData: ([] as unknown) as TooltipData, + offset: { x: 0, y: 0 }, + target: null, +}); + +export class ChartTooltipService { + private chartTooltip$ = new BehaviorSubject(getChartTooltipDefaultState()); + + public tooltipState$: Observable = this.chartTooltip$ + .asObservable() + .pipe(distinctUntilChanged(isEqual)); + + public show( + tooltipData: TooltipData, + target: HTMLElement, + offset: TooltipOffset = { x: 0, y: 0 } + ) { + if (!target) { + throw new Error('target is required for the tooltip positioning'); + } + + this.chartTooltip$.next({ + ...this.chartTooltip$.getValue(), + isTooltipVisible: true, + offset, + tooltipData, + target, + }); + } + + public hide() { + this.chartTooltip$.next({ + ...getChartTooltipDefaultState(), + isTooltipVisible: false, + }); + } +} diff --git a/x-pack/plugins/ml/public/application/components/chart_tooltip/index.ts b/x-pack/plugins/ml/public/application/components/chart_tooltip/index.ts index 75c65ebaa0f50..ec19fe18bd324 100644 --- a/x-pack/plugins/ml/public/application/components/chart_tooltip/index.ts +++ b/x-pack/plugins/ml/public/application/components/chart_tooltip/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { mlChartTooltipService } from './chart_tooltip_service'; -export { ChartTooltip } from './chart_tooltip'; +export { ChartTooltipService } from './chart_tooltip_service'; +export { MlTooltipComponent } from './chart_tooltip'; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx index 381e5e75356c1..f709c161bef17 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx @@ -4,45 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import PropTypes from 'prop-types'; - -import { - EuiButton, - EuiButtonEmpty, - EuiFlexItem, - EuiFlexGroup, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyoutHeader, - EuiSwitch, - EuiTitle, -} from '@elastic/eui'; +import React, { useState, useEffect } from 'react'; +import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useMlKibana } from '../../contexts/kibana'; import { Dictionary } from '../../../../common/types/common'; -import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; -import { ml } from '../../services/ml_api_service'; import { useUrlState } from '../../util/url_state'; // @ts-ignore -import { JobSelectorTable } from './job_selector_table/index'; -// @ts-ignore import { IdBadges } from './id_badges/index'; -// @ts-ignore -import { NewSelectionIdBadges } from './new_selection_id_badges/index'; -import { - getGroupsFromJobs, - getTimeRangeFromSelection, - normalizeTimes, -} from './job_select_service_utils'; +import { BADGE_LIMIT, JobSelectorFlyout, JobSelectorFlyoutProps } from './job_selector_flyout'; +import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; interface GroupObj { groupId: string; jobIds: string[]; } + function mergeSelection( jobIds: string[], groupObjs: GroupObj[], @@ -71,7 +49,7 @@ function mergeSelection( } type GroupsMap = Dictionary; -function getInitialGroupsMap(selectedGroups: GroupObj[]): GroupsMap { +export function getInitialGroupsMap(selectedGroups: GroupObj[]): GroupsMap { const map: GroupsMap = {}; if (selectedGroups.length) { @@ -83,81 +61,38 @@ function getInitialGroupsMap(selectedGroups: GroupObj[]): GroupsMap { return map; } -const BADGE_LIMIT = 10; -const DEFAULT_GANTT_BAR_WIDTH = 299; // pixels - interface JobSelectorProps { dateFormatTz: string; singleSelection: boolean; timeseriesOnly: boolean; } +export interface JobSelectionMaps { + jobsMap: Dictionary; + groupsMap: Dictionary; +} + export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: JobSelectorProps) { const [globalState, setGlobalState] = useUrlState('_g'); const selectedJobIds = globalState?.ml?.jobIds ?? []; const selectedGroups = globalState?.ml?.groups ?? []; - const [jobs, setJobs] = useState([]); - const [groups, setGroups] = useState([]); - const [maps, setMaps] = useState({ groupsMap: getInitialGroupsMap(selectedGroups), jobsMap: {} }); + const [maps, setMaps] = useState({ + groupsMap: getInitialGroupsMap(selectedGroups), + jobsMap: {}, + }); const [selectedIds, setSelectedIds] = useState( mergeSelection(selectedJobIds, selectedGroups, singleSelection) ); - const [newSelection, setNewSelection] = useState( - mergeSelection(selectedJobIds, selectedGroups, singleSelection) - ); - const [showAllBadges, setShowAllBadges] = useState(false); const [showAllBarBadges, setShowAllBarBadges] = useState(false); - const [applyTimeRange, setApplyTimeRange] = useState(true); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); - const [ganttBarWidth, setGanttBarWidth] = useState(DEFAULT_GANTT_BAR_WIDTH); - const flyoutEl = useRef<{ flyout: HTMLElement }>(null); - const { - services: { notifications }, - } = useMlKibana(); // Ensure JobSelectionBar gets updated when selection via globalState changes. useEffect(() => { setSelectedIds(mergeSelection(selectedJobIds, selectedGroups, singleSelection)); }, [JSON.stringify([selectedJobIds, selectedGroups])]); - // Ensure current selected ids always show up in flyout - useEffect(() => { - setNewSelection(selectedIds); - }, [isFlyoutVisible]); // eslint-disable-line - - // Wrap handleResize in useCallback as it is a dependency for useEffect on line 131 below. - // Not wrapping it would cause this dependency to change on every render - const handleResize = useCallback(() => { - if (jobs.length > 0 && flyoutEl && flyoutEl.current && flyoutEl.current.flyout) { - // get all cols in flyout table - const tableHeaderCols: NodeListOf = flyoutEl.current.flyout.querySelectorAll( - 'table thead th' - ); - // get the width of the last col - const derivedWidth = tableHeaderCols[tableHeaderCols.length - 1].offsetWidth - 16; - const normalizedJobs = normalizeTimes(jobs, dateFormatTz, derivedWidth); - setJobs(normalizedJobs); - const { groups: updatedGroups } = getGroupsFromJobs(normalizedJobs); - setGroups(updatedGroups); - setGanttBarWidth(derivedWidth); - } - }, [dateFormatTz, jobs]); - - useEffect(() => { - // Ensure ganttBar width gets calculated on resize - window.addEventListener('resize', handleResize); - - return () => { - window.removeEventListener('resize', handleResize); - }; - }, [handleResize]); - - useEffect(() => { - handleResize(); - }, [handleResize, jobs]); - function closeFlyout() { setIsFlyoutVisible(false); } @@ -168,78 +103,26 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J function handleJobSelectionClick() { showFlyout(); - - ml.jobs - .jobsWithTimerange(dateFormatTz) - .then(resp => { - const normalizedJobs = normalizeTimes(resp.jobs, dateFormatTz, DEFAULT_GANTT_BAR_WIDTH); - const { groups: groupsWithTimerange, groupsMap } = getGroupsFromJobs(normalizedJobs); - setJobs(normalizedJobs); - setGroups(groupsWithTimerange); - setMaps({ groupsMap, jobsMap: resp.jobsMap }); - }) - .catch((err: any) => { - console.error('Error fetching jobs with time range', err); // eslint-disable-line - const { toasts } = notifications; - toasts.addDanger({ - title: i18n.translate('xpack.ml.jobSelector.jobFetchErrorMessage', { - defaultMessage: 'An error occurred fetching jobs. Refresh and try again.', - }), - }); - }); - } - - function handleNewSelection({ selectionFromTable }: { selectionFromTable: any }) { - setNewSelection(selectionFromTable); } - function applySelection() { - // allNewSelection will be a list of all job ids (including those from groups) selected from the table - const allNewSelection: string[] = []; - const groupSelection: Array<{ groupId: string; jobIds: string[] }> = []; - - newSelection.forEach(id => { - if (maps.groupsMap[id] !== undefined) { - // Push all jobs from selected groups into the newSelection list - allNewSelection.push(...maps.groupsMap[id]); - // if it's a group - push group obj to set in global state - groupSelection.push({ groupId: id, jobIds: maps.groupsMap[id] }); - } else { - allNewSelection.push(id); - } - }); - // create a Set to remove duplicate values - const allNewSelectionUnique = Array.from(new Set(allNewSelection)); - + const applySelection: JobSelectorFlyoutProps['onSelectionConfirmed'] = ({ + newSelection, + jobIds, + groups: newGroups, + time, + }) => { setSelectedIds(newSelection); - setNewSelection([]); - - closeFlyout(); - - const time = applyTimeRange - ? getTimeRangeFromSelection(jobs, allNewSelectionUnique) - : undefined; setGlobalState({ ml: { - jobIds: allNewSelectionUnique, - groups: groupSelection, + jobIds, + groups: newGroups, }, ...(time !== undefined ? { time } : {}), }); - } - - function toggleTimerangeSwitch() { - setApplyTimeRange(!applyTimeRange); - } - - function removeId(id: string) { - setNewSelection(newSelection.filter(item => item !== id)); - } - function clearSelection() { - setNewSelection([]); - } + closeFlyout(); + }; function renderJobSelectionBar() { return ( @@ -280,103 +163,16 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J function renderFlyout() { if (isFlyoutVisible) { return ( - - - -

- {i18n.translate('xpack.ml.jobSelector.flyoutTitle', { - defaultMessage: 'Job selection', - })} -

-
-
- - - - - setShowAllBadges(!showAllBadges)} - showAllBadges={showAllBadges} - /> - - - - - - {!singleSelection && newSelection.length > 0 && ( - - {i18n.translate('xpack.ml.jobSelector.clearAllFlyoutButton', { - defaultMessage: 'Clear all', - })} - - )} - - - - - - - - - - - - - - {i18n.translate('xpack.ml.jobSelector.applyFlyoutButton', { - defaultMessage: 'Apply', - })} - - - - - {i18n.translate('xpack.ml.jobSelector.closeFlyoutButton', { - defaultMessage: 'Close', - })} - - - - -
+ ); } } @@ -388,9 +184,3 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J
); } - -JobSelector.propTypes = { - selectedJobIds: PropTypes.array, - singleSelection: PropTypes.bool, - timeseriesOnly: PropTypes.bool, -}; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/index.js b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/index.js rename to x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/index.ts diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.tsx similarity index 68% rename from x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js rename to x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.tsx index 4d2ab01e2a054..b2cae278c0e77 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.js +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_badge/job_selector_badge.tsx @@ -4,18 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { PropTypes } from 'prop-types'; -import { EuiBadge } from '@elastic/eui'; -import { tabColor } from '../../../../../common/util/group_color_utils'; +import React, { FC } from 'react'; +import { EuiBadge, EuiBadgeProps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { tabColor } from '../../../../../common/util/group_color_utils'; -export function JobSelectorBadge({ icon, id, isGroup = false, numJobs, removeId }) { +interface JobSelectorBadgeProps { + icon?: boolean; + id: string; + isGroup?: boolean; + numJobs?: number; + removeId?: Function; +} + +export const JobSelectorBadge: FC = ({ + icon, + id, + isGroup = false, + numJobs, + removeId, +}) => { const color = isGroup ? tabColor(id) : 'hollow'; - let props = { color }; + let props = { color } as EuiBadgeProps; let jobCount; - if (icon === true) { + if (icon === true && removeId) { + // @ts-ignore props = { ...props, iconType: 'cross', @@ -37,11 +51,4 @@ export function JobSelectorBadge({ icon, id, isGroup = false, numJobs, removeId {`${id}${jobCount ? jobCount : ''}`} ); -} -JobSelectorBadge.propTypes = { - icon: PropTypes.bool, - id: PropTypes.string.isRequired, - isGroup: PropTypes.bool, - numJobs: PropTypes.number, - removeId: PropTypes.func, }; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx new file mode 100644 index 0000000000000..66aa05d2aaa97 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx @@ -0,0 +1,289 @@ +/* + * 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, useCallback, useEffect, useRef, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexItem, + EuiFlexGroup, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiSwitch, + EuiTitle, +} from '@elastic/eui'; +import { NewSelectionIdBadges } from './new_selection_id_badges'; +// @ts-ignore +import { JobSelectorTable } from './job_selector_table'; +import { + getGroupsFromJobs, + getTimeRangeFromSelection, + normalizeTimes, +} from './job_select_service_utils'; +import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs'; +import { ml } from '../../services/ml_api_service'; +import { useMlKibana } from '../../contexts/kibana'; +import { JobSelectionMaps } from './job_selector'; + +export const BADGE_LIMIT = 10; +export const DEFAULT_GANTT_BAR_WIDTH = 299; // pixels + +export interface JobSelectorFlyoutProps { + dateFormatTz: string; + selectedIds?: string[]; + newSelection?: string[]; + onFlyoutClose: () => void; + onJobsFetched?: (maps: JobSelectionMaps) => void; + onSelectionChange?: (newSelection: string[]) => void; + onSelectionConfirmed: (payload: { + newSelection: string[]; + jobIds: string[]; + groups: Array<{ groupId: string; jobIds: string[] }>; + time: any; + }) => void; + singleSelection: boolean; + timeseriesOnly: boolean; + maps: JobSelectionMaps; + withTimeRangeSelector?: boolean; +} + +export const JobSelectorFlyout: FC = ({ + dateFormatTz, + selectedIds = [], + singleSelection, + timeseriesOnly, + onJobsFetched, + onSelectionChange, + onSelectionConfirmed, + onFlyoutClose, + maps, + withTimeRangeSelector = true, +}) => { + const { + services: { notifications }, + } = useMlKibana(); + + const [newSelection, setNewSelection] = useState(selectedIds); + + const [showAllBadges, setShowAllBadges] = useState(false); + const [applyTimeRange, setApplyTimeRange] = useState(true); + const [jobs, setJobs] = useState([]); + const [groups, setGroups] = useState([]); + const [ganttBarWidth, setGanttBarWidth] = useState(DEFAULT_GANTT_BAR_WIDTH); + const [jobGroupsMaps, setJobGroupsMaps] = useState(maps); + + const flyoutEl = useRef<{ flyout: HTMLElement }>(null); + + function applySelection() { + // allNewSelection will be a list of all job ids (including those from groups) selected from the table + const allNewSelection: string[] = []; + const groupSelection: Array<{ groupId: string; jobIds: string[] }> = []; + + newSelection.forEach(id => { + if (jobGroupsMaps.groupsMap[id] !== undefined) { + // Push all jobs from selected groups into the newSelection list + allNewSelection.push(...jobGroupsMaps.groupsMap[id]); + // if it's a group - push group obj to set in global state + groupSelection.push({ groupId: id, jobIds: jobGroupsMaps.groupsMap[id] }); + } else { + allNewSelection.push(id); + } + }); + // create a Set to remove duplicate values + const allNewSelectionUnique = Array.from(new Set(allNewSelection)); + + const time = applyTimeRange + ? getTimeRangeFromSelection(jobs, allNewSelectionUnique) + : undefined; + + onSelectionConfirmed({ + newSelection: allNewSelectionUnique, + jobIds: allNewSelectionUnique, + groups: groupSelection, + time, + }); + } + + function removeId(id: string) { + setNewSelection(newSelection.filter(item => item !== id)); + } + + function toggleTimerangeSwitch() { + setApplyTimeRange(!applyTimeRange); + } + + function clearSelection() { + setNewSelection([]); + } + + function handleNewSelection({ selectionFromTable }: { selectionFromTable: any }) { + setNewSelection(selectionFromTable); + } + + // Wrap handleResize in useCallback as it is a dependency for useEffect on line 131 below. + // Not wrapping it would cause this dependency to change on every render + const handleResize = useCallback(() => { + if (jobs.length > 0 && flyoutEl && flyoutEl.current && flyoutEl.current.flyout) { + // get all cols in flyout table + const tableHeaderCols: NodeListOf = flyoutEl.current.flyout.querySelectorAll( + 'table thead th' + ); + // get the width of the last col + const derivedWidth = tableHeaderCols[tableHeaderCols.length - 1].offsetWidth - 16; + const normalizedJobs = normalizeTimes(jobs, dateFormatTz, derivedWidth); + setJobs(normalizedJobs); + const { groups: updatedGroups } = getGroupsFromJobs(normalizedJobs); + setGroups(updatedGroups); + setGanttBarWidth(derivedWidth); + } + }, [dateFormatTz, jobs]); + + // Fetch jobs list on flyout open + useEffect(() => { + fetchJobs(); + }, []); + + async function fetchJobs() { + try { + const resp = await ml.jobs.jobsWithTimerange(dateFormatTz); + const normalizedJobs = normalizeTimes(resp.jobs, dateFormatTz, DEFAULT_GANTT_BAR_WIDTH); + const { groups: groupsWithTimerange, groupsMap } = getGroupsFromJobs(normalizedJobs); + setJobs(normalizedJobs); + setGroups(groupsWithTimerange); + setJobGroupsMaps({ groupsMap, jobsMap: resp.jobsMap }); + + if (onJobsFetched) { + onJobsFetched({ groupsMap, jobsMap: resp.jobsMap }); + } + } catch (e) { + console.error('Error fetching jobs with time range', e); // eslint-disable-line + const { toasts } = notifications; + toasts.addDanger({ + title: i18n.translate('xpack.ml.jobSelector.jobFetchErrorMessage', { + defaultMessage: 'An error occurred fetching jobs. Refresh and try again.', + }), + }); + } + } + + useEffect(() => { + // Ensure ganttBar width gets calculated on resize + window.addEventListener('resize', handleResize); + + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [handleResize]); + + useEffect(() => { + handleResize(); + }, [handleResize, jobs]); + + return ( + + + +

+ {i18n.translate('xpack.ml.jobSelector.flyoutTitle', { + defaultMessage: 'Job selection', + })} +

+
+
+ + + + + setShowAllBadges(!showAllBadges)} + showAllBadges={showAllBadges} + /> + + + + + + {!singleSelection && newSelection.length > 0 && ( + + {i18n.translate('xpack.ml.jobSelector.clearAllFlyoutButton', { + defaultMessage: 'Clear all', + })} + + )} + + {withTimeRangeSelector && ( + + + + )} + + + + + + + + + + {i18n.translate('xpack.ml.jobSelector.applyFlyoutButton', { + defaultMessage: 'Apply', + })} + + + + + {i18n.translate('xpack.ml.jobSelector.closeFlyoutButton', { + defaultMessage: 'Close', + })} + + + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js index 64793d15f1e4a..c55e03776c09d 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_table/job_selector_table.js @@ -224,7 +224,7 @@ export function JobSelectorTable({ {jobs.length === 0 && } {jobs.length !== 0 && singleSelection === true && renderJobsTable()} - {jobs.length !== 0 && singleSelection === undefined && renderTabs()} + {jobs.length !== 0 && !singleSelection && renderTabs()} ); } diff --git a/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/index.js b/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/index.js rename to x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/index.ts diff --git a/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.js b/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.tsx similarity index 80% rename from x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.js rename to x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.tsx index 67dce47323889..4c018e72f3e10 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.js +++ b/x-pack/plugins/ml/public/application/components/job_selector/new_selection_id_badges/new_selection_id_badges.tsx @@ -4,20 +4,29 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { PropTypes } from 'prop-types'; +import React, { FC, MouseEventHandler } from 'react'; import { EuiFlexItem, EuiLink, EuiText } from '@elastic/eui'; -import { JobSelectorBadge } from '../job_selector_badge'; import { i18n } from '@kbn/i18n'; +import { JobSelectorBadge } from '../job_selector_badge'; +import { JobSelectionMaps } from '../job_selector'; -export function NewSelectionIdBadges({ +interface NewSelectionIdBadgesProps { + limit: number; + maps: JobSelectionMaps; + newSelection: string[]; + onDeleteClick?: Function; + onLinkClick?: MouseEventHandler; + showAllBadges?: boolean; +} + +export const NewSelectionIdBadges: FC = ({ limit, maps, newSelection, onDeleteClick, onLinkClick, showAllBadges, -}) { +}) => { const badges = []; for (let i = 0; i < newSelection.length; i++) { @@ -60,16 +69,5 @@ export function NewSelectionIdBadges({ ); } - return badges; -} -NewSelectionIdBadges.propTypes = { - limit: PropTypes.number, - maps: PropTypes.shape({ - jobsMap: PropTypes.object, - groupsMap: PropTypes.object, - }), - newSelection: PropTypes.array, - onDeleteClick: PropTypes.func, - onLinkClick: PropTypes.func, - showAllBadges: PropTypes.bool, + return <>{badges}; }; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx index 86ffc4a2614b9..06d89ab782167 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx @@ -41,7 +41,7 @@ import { useMlContext } from '../../contexts/ml'; import { kbnTypeToMLJobType } from '../../util/field_types_utils'; import { useTimefilter } from '../../contexts/kibana'; import { timeBasedIndexCheck, getQueryFromSavedSearch } from '../../util/index_utils'; -import { TimeBuckets } from '../../util/time_buckets'; +import { getTimeBucketsFromCache } from '../../util/time_buckets'; import { useUrlState } from '../../util/url_state'; import { FieldRequestConfig, FieldVisConfig } from './common'; import { ActionsPanel } from './components/actions_panel'; @@ -318,7 +318,7 @@ export const Page: FC = () => { // Obtain the interval to use for date histogram aggregations // (such as the document count chart). Aim for 75 bars. - const buckets = new TimeBuckets(); + const buckets = getTimeBucketsFromCache(); const tf = timefilter as any; let earliest: number | undefined; diff --git a/x-pack/plugins/ml/public/application/explorer/__snapshots__/explorer_swimlane.test.js.snap b/x-pack/plugins/ml/public/application/explorer/__snapshots__/explorer_swimlane.test.tsx.snap similarity index 100% rename from x-pack/plugins/ml/public/application/explorer/__snapshots__/explorer_swimlane.test.js.snap rename to x-pack/plugins/ml/public/application/explorer/__snapshots__/explorer_swimlane.test.tsx.snap diff --git a/x-pack/plugins/ml/public/application/explorer/_explorer.scss b/x-pack/plugins/ml/public/application/explorer/_explorer.scss index 9fb2f0c3bed94..cfcba081983c2 100644 --- a/x-pack/plugins/ml/public/application/explorer/_explorer.scss +++ b/x-pack/plugins/ml/public/application/explorer/_explorer.scss @@ -106,164 +106,6 @@ padding: 0; margin-bottom: $euiSizeS; - div.ml-swimlanes { - margin: 0px 0px 0px 10px; - - div.cells-marker-container { - margin-left: 176px; - height: 22px; - white-space: nowrap; - - // background-color: #CCC; - .sl-cell { - height: 10px; - display: inline-block; - vertical-align: top; - margin-top: 16px; - text-align: center; - visibility: hidden; - cursor: default; - - i { - color: $euiColorDarkShade; - } - } - - .sl-cell-hover { - visibility: visible; - - i { - display: block; - margin-top: -6px; - } - } - - .sl-cell-active-hover { - visibility: visible; - - .floating-time-label { - display: inline-block; - } - } - } - - div.lane { - height: 30px; - border-bottom: 0px; - border-radius: 2px; - margin-top: -1px; - white-space: nowrap; - - div.lane-label { - display: inline-block; - font-size: 13px; - height: 30px; - text-align: right; - vertical-align: middle; - border-radius: 2px; - padding-right: 5px; - margin-right: 5px; - border: 1px solid transparent; - overflow: hidden; - text-overflow: ellipsis; - } - - div.lane-label.lane-label-masked { - opacity: 0.3; - } - - div.cells-container { - border: $euiBorderThin; - border-right: 0px; - display: inline-block; - height: 30px; - vertical-align: middle; - background-color: $euiColorEmptyShade; - - .sl-cell { - color: $euiColorEmptyShade; - cursor: default; - display: inline-block; - height: 29px; - border-right: $euiBorderThin; - vertical-align: top; - position: relative; - - .sl-cell-inner, - .sl-cell-inner-dragselect { - height: 26px; - margin: 1px; - border-radius: 2px; - text-align: center; - } - - .sl-cell-inner.sl-cell-inner-masked { - opacity: 0.2; - } - - .sl-cell-inner.sl-cell-inner-selected, - .sl-cell-inner-dragselect.sl-cell-inner-selected { - border: 2px solid $euiColorDarkShade; - } - - .sl-cell-inner.sl-cell-inner-selected.sl-cell-inner-masked, - .sl-cell-inner-dragselect.sl-cell-inner-selected.sl-cell-inner-masked { - border: 2px solid $euiColorFullShade; - opacity: 0.4; - } - } - - .sl-cell:hover { - .sl-cell-inner { - opacity: 0.8; - cursor: pointer; - } - } - - .sl-cell.ds-selected { - - .sl-cell-inner, - .sl-cell-inner-dragselect { - border: 2px solid $euiColorDarkShade; - border-radius: 2px; - opacity: 1; - } - } - - } - } - - div.lane:last-child { - div.cells-container { - .sl-cell { - border-bottom: $euiBorderThin; - } - } - } - - .time-tick-labels { - height: 25px; - margin-top: $euiSizeXS / 2; - margin-left: 175px; - - /* hide d3's domain line */ - path.domain { - display: none; - } - - /* hide d3's tick line */ - g.tick line { - display: none; - } - - /* override d3's default tick styles */ - g.tick text { - font-size: 11px; - fill: $euiColorMediumShade; - } - } - } - line.gridLine { stroke: $euiBorderColor; fill: none; @@ -328,3 +170,161 @@ } } } + +.ml-swimlanes { + margin: 0px 0px 0px 10px; + + div.cells-marker-container { + margin-left: 176px; + height: 22px; + white-space: nowrap; + + // background-color: #CCC; + .sl-cell { + height: 10px; + display: inline-block; + vertical-align: top; + margin-top: 16px; + text-align: center; + visibility: hidden; + cursor: default; + + i { + color: $euiColorDarkShade; + } + } + + .sl-cell-hover { + visibility: visible; + + i { + display: block; + margin-top: -6px; + } + } + + .sl-cell-active-hover { + visibility: visible; + + .floating-time-label { + display: inline-block; + } + } + } + + div.lane { + height: 30px; + border-bottom: 0px; + border-radius: 2px; + margin-top: -1px; + white-space: nowrap; + + div.lane-label { + display: inline-block; + font-size: 13px; + height: 30px; + text-align: right; + vertical-align: middle; + border-radius: 2px; + padding-right: 5px; + margin-right: 5px; + border: 1px solid transparent; + overflow: hidden; + text-overflow: ellipsis; + } + + div.lane-label.lane-label-masked { + opacity: 0.3; + } + + div.cells-container { + border: $euiBorderThin; + border-right: 0px; + display: inline-block; + height: 30px; + vertical-align: middle; + background-color: $euiColorEmptyShade; + + .sl-cell { + color: $euiColorEmptyShade; + cursor: default; + display: inline-block; + height: 29px; + border-right: $euiBorderThin; + vertical-align: top; + position: relative; + + .sl-cell-inner, + .sl-cell-inner-dragselect { + height: 26px; + margin: 1px; + border-radius: 2px; + text-align: center; + } + + .sl-cell-inner.sl-cell-inner-masked { + opacity: 0.2; + } + + .sl-cell-inner.sl-cell-inner-selected, + .sl-cell-inner-dragselect.sl-cell-inner-selected { + border: 2px solid $euiColorDarkShade; + } + + .sl-cell-inner.sl-cell-inner-selected.sl-cell-inner-masked, + .sl-cell-inner-dragselect.sl-cell-inner-selected.sl-cell-inner-masked { + border: 2px solid $euiColorFullShade; + opacity: 0.4; + } + } + + .sl-cell:hover { + .sl-cell-inner { + opacity: 0.8; + cursor: pointer; + } + } + + .sl-cell.ds-selected { + + .sl-cell-inner, + .sl-cell-inner-dragselect { + border: 2px solid $euiColorDarkShade; + border-radius: 2px; + opacity: 1; + } + } + + } + } + + div.lane:last-child { + div.cells-container { + .sl-cell { + border-bottom: $euiBorderThin; + } + } + } + + .time-tick-labels { + height: 25px; + margin-top: $euiSizeXS / 2; + margin-left: 175px; + + /* hide d3's domain line */ + path.domain { + display: none; + } + + /* hide d3's tick line */ + g.tick line { + display: none; + } + + /* override d3's default tick styles */ + g.tick text { + font-size: 11px; + fill: $euiColorMediumShade; + } + } +} diff --git a/x-pack/plugins/ml/public/application/explorer/explorer.js b/x-pack/plugins/ml/public/application/explorer/explorer.js index d61d56d07b644..86d16776b68e2 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer.js @@ -36,9 +36,8 @@ import { ExplorerNoJobsFound, ExplorerNoResultsFound, } from './components'; -import { ChartTooltip } from '../components/chart_tooltip'; import { ExplorerSwimlane } from './explorer_swimlane'; -import { TimeBuckets } from '../util/time_buckets'; +import { getTimeBucketsFromCache } from '../util/time_buckets'; import { InfluencersList } from '../components/influencers_list'; import { ALLOW_CELL_RANGE_SELECTION, @@ -81,6 +80,7 @@ import { AnomaliesTable } from '../components/anomalies_table/anomalies_table'; import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public'; import { getTimefilter, getToastNotifications } from '../util/dependency_cache'; +import { MlTooltipComponent } from '../components/chart_tooltip'; function mapSwimlaneOptionsToEuiOptions(options) { return options.map(option => ({ @@ -179,6 +179,8 @@ export class Explorer extends React.Component { // Required to redraw the time series chart when the container is resized. this.resizeChecker = new ResizeChecker(this.resizeRef.current); this.resizeChecker.on('resize', this.resizeHandler); + + this.timeBuckets = getTimeBucketsFromCache(); } componentWillUnmount() { @@ -358,9 +360,6 @@ export class Explorer extends React.Component { return (
- {/* Make sure ChartTooltip is inside wrapping div with 0px left/right padding so positioning can be inferred correctly. */} - - {noInfluencersConfigured === false && influencers !== undefined && (
{showOverallSwimlane && ( - + + {tooltipService => ( + + )} + )}
@@ -494,17 +498,22 @@ export class Explorer extends React.Component { onMouseLeave={this.onSwimlaneLeaveHandler} data-test-subj="mlAnomalyExplorerSwimlaneViewBy" > - + + {tooltipService => ( + + )} +
)} diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 5fc1160093a49..03426869b0ccf 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -29,9 +29,8 @@ import { removeLabelOverlap, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; -import { TimeBuckets } from '../../util/time_buckets'; +import { getTimeBucketsFromCache } from '../../util/time_buckets'; import { mlFieldFormatService } from '../../services/field_format_service'; -import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service'; import { CHART_TYPE } from '../explorer_constants'; @@ -50,6 +49,7 @@ export class ExplorerChartDistribution extends React.Component { static propTypes = { seriesConfig: PropTypes.object, severity: PropTypes.number, + tooltipService: PropTypes.object.isRequired, }; componentDidMount() { @@ -61,7 +61,7 @@ export class ExplorerChartDistribution extends React.Component { } renderChart() { - const { tooManyBuckets } = this.props; + const { tooManyBuckets, tooltipService } = this.props; const element = this.rootNode; const config = this.props.seriesConfig; @@ -259,7 +259,7 @@ export class ExplorerChartDistribution extends React.Component { function drawRareChartAxes() { // Get the scaled date format to use for x axis tick labels. - const timeBuckets = new TimeBuckets(); + const timeBuckets = getTimeBucketsFromCache(); const bounds = { min: moment(config.plotEarliest), max: moment(config.plotLatest) }; timeBuckets.setBounds(bounds); timeBuckets.setInterval('auto'); @@ -397,7 +397,7 @@ export class ExplorerChartDistribution extends React.Component { .on('mouseover', function(d) { showLineChartTooltip(d, this); }) - .on('mouseout', () => mlChartTooltipService.hide()); + .on('mouseout', () => tooltipService.hide()); // Update all dots to new positions. dots @@ -550,7 +550,7 @@ export class ExplorerChartDistribution extends React.Component { }); } - mlChartTooltipService.show(tooltipData, circle, { + tooltipService.show(tooltipData, circle, { x: LINE_CHART_ANOMALY_RADIUS * 3, y: LINE_CHART_ANOMALY_RADIUS * 2, }); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js index 71d777db5b2ec..06fd82204c1e1 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js @@ -10,11 +10,13 @@ import seriesConfig from './__mocks__/mock_series_config_rare.json'; // Mock TimeBuckets and mlFieldFormatService, they don't play well // with the jest based test setup yet. jest.mock('../../util/time_buckets', () => ({ - TimeBuckets: function() { - this.setBounds = jest.fn(); - this.setInterval = jest.fn(); - this.getScaledDateFormat = jest.fn(); - }, + getTimeBucketsFromCache: jest.fn(() => { + return { + setBounds: jest.fn(), + setInterval: jest.fn(), + getScaledDateFormat: jest.fn(), + }; + }), })); jest.mock('../../services/field_format_service', () => ({ mlFieldFormatService: { @@ -43,8 +45,16 @@ describe('ExplorerChart', () => { afterEach(() => (SVGElement.prototype.getBBox = originalGetBBox)); test('Initialize', () => { + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + const wrapper = mountWithIntl( - + ); // without setting any attributes and corresponding data @@ -59,10 +69,16 @@ describe('ExplorerChart', () => { loading: true, }; + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + const wrapper = mountWithIntl( ); @@ -83,12 +99,18 @@ describe('ExplorerChart', () => { chartLimits: chartLimits(chartData), }; + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + // We create the element including a wrapper which sets the width: return mountWithIntl(
); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index dd9479be931a7..82041af39ca15 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -38,10 +38,9 @@ import { showMultiBucketAnomalyTooltip, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; -import { TimeBuckets } from '../../util/time_buckets'; +import { getTimeBucketsFromCache } from '../../util/time_buckets'; import { mlEscape } from '../../util/string_utils'; import { mlFieldFormatService } from '../../services/field_format_service'; -import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service'; import { i18n } from '@kbn/i18n'; @@ -53,6 +52,7 @@ export class ExplorerChartSingleMetric extends React.Component { tooManyBuckets: PropTypes.bool, seriesConfig: PropTypes.object, severity: PropTypes.number.isRequired, + tooltipService: PropTypes.object.isRequired, }; componentDidMount() { @@ -64,7 +64,7 @@ export class ExplorerChartSingleMetric extends React.Component { } renderChart() { - const { tooManyBuckets } = this.props; + const { tooManyBuckets, tooltipService } = this.props; const element = this.rootNode; const config = this.props.seriesConfig; @@ -191,7 +191,7 @@ export class ExplorerChartSingleMetric extends React.Component { function drawLineChartAxes() { // Get the scaled date format to use for x axis tick labels. - const timeBuckets = new TimeBuckets(); + const timeBuckets = getTimeBucketsFromCache(); const bounds = { min: moment(config.plotEarliest), max: moment(config.plotLatest) }; timeBuckets.setBounds(bounds); timeBuckets.setInterval('auto'); @@ -309,7 +309,7 @@ export class ExplorerChartSingleMetric extends React.Component { .on('mouseover', function(d) { showLineChartTooltip(d, this); }) - .on('mouseout', () => mlChartTooltipService.hide()); + .on('mouseout', () => tooltipService.hide()); const isAnomalyVisible = d => _.has(d, 'anomalyScore') && Number(d.anomalyScore) >= severity; @@ -354,7 +354,7 @@ export class ExplorerChartSingleMetric extends React.Component { .on('mouseover', function(d) { showLineChartTooltip(d, this); }) - .on('mouseout', () => mlChartTooltipService.hide()); + .on('mouseout', () => tooltipService.hide()); // Add rectangular markers for any scheduled events. const scheduledEventMarkers = lineChartGroup @@ -503,7 +503,7 @@ export class ExplorerChartSingleMetric extends React.Component { }); } - mlChartTooltipService.show(tooltipData, circle, { + tooltipService.show(tooltipData, circle, { x: LINE_CHART_ANOMALY_RADIUS * 3, y: LINE_CHART_ANOMALY_RADIUS * 2, }); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js index ca3e52308a936..54f541ceb7c3d 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js @@ -10,11 +10,13 @@ import seriesConfig from './__mocks__/mock_series_config_filebeat.json'; // Mock TimeBuckets and mlFieldFormatService, they don't play well // with the jest based test setup yet. jest.mock('../../util/time_buckets', () => ({ - TimeBuckets: function() { - this.setBounds = jest.fn(); - this.setInterval = jest.fn(); - this.getScaledDateFormat = jest.fn(); - }, + getTimeBucketsFromCache: jest.fn(() => { + return { + setBounds: jest.fn(), + setInterval: jest.fn(), + getScaledDateFormat: jest.fn(), + }; + }), })); jest.mock('../../services/field_format_service', () => ({ mlFieldFormatService: { @@ -43,8 +45,16 @@ describe('ExplorerChart', () => { afterEach(() => (SVGElement.prototype.getBBox = originalGetBBox)); test('Initialize', () => { + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + const wrapper = mountWithIntl( - + ); // without setting any attributes and corresponding data @@ -59,10 +69,16 @@ describe('ExplorerChart', () => { loading: true, }; + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + const wrapper = mountWithIntl( ); @@ -83,12 +99,18 @@ describe('ExplorerChart', () => { chartLimits: chartLimits(chartData), }; + const mockTooltipService = { + show: jest.fn(), + hide: jest.fn(), + }; + // We create the element including a wrapper which sets the width: return mountWithIntl(
); 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 99de38c1e0a84..5b95931d31ab6 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,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import $ from 'jquery'; - import React from 'react'; import { @@ -29,6 +27,7 @@ import { ExplorerChartLabel } from './components/explorer_chart_label'; import { CHART_TYPE } from '../explorer_constants'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { MlTooltipComponent } from '../../components/chart_tooltip'; const textTooManyBuckets = i18n.translate('xpack.ml.explorer.charts.tooManyBucketsDescription', { defaultMessage: @@ -121,19 +120,29 @@ function ExplorerChartContainer({ series, severity, tooManyBuckets, wrapLabel }) chartType === CHART_TYPE.POPULATION_DISTRIBUTION ) { return ( - + + {tooltipService => ( + + )} + ); } return ( - + + {tooltipService => ( + + )} + ); })()} @@ -141,48 +150,36 @@ function ExplorerChartContainer({ series, severity, tooManyBuckets, wrapLabel }) } // Flex layout wrapper for all explorer charts -export class ExplorerChartsContainer extends React.Component { - componentDidMount() { - // Create a div for the tooltip. - $('.ml-explorer-charts-tooltip').remove(); - $('body').append( - '