diff --git a/docs/developer/core/development-elasticsearch.asciidoc b/docs/developer/core/development-elasticsearch.asciidoc index 5e56603249854..fb5db3fc437fe 100644 --- a/docs/developer/core/development-elasticsearch.asciidoc +++ b/docs/developer/core/development-elasticsearch.asciidoc @@ -3,7 +3,7 @@ Kibana exposes two clients on the server and browser for communicating with elasticsearch. There is an 'admin' client which is used for managing Kibana's state, and a 'data' client for all -other requests. The clients use the {client-ref}/javascript-api/current/index.html[elasticsearch.js library]. +other requests. The clients use the {jsclient}/javascript-api/current/index.html[elasticsearch.js library]. [float] [[client-server]] diff --git a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx index 435db0589c96c..2dc6ef7847cd2 100644 --- a/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx +++ b/src/legacy/core_plugins/data/public/query/query_bar/components/query_bar_top_row.tsx @@ -24,7 +24,7 @@ import React, { useState, useEffect } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSuperDatePicker } from '@elastic/eui'; // @ts-ignore -import { EuiSuperUpdateButton } from '@elastic/eui'; +import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui'; import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import { Toast } from 'src/core/public'; import { TimeRange } from 'src/plugins/data/public'; @@ -41,10 +41,12 @@ interface Props { query?: Query; onSubmit: (payload: { dateRange: TimeRange; query?: Query }) => void; onChange: (payload: { dateRange: TimeRange; query?: Query }) => void; + onRefresh?: (payload: { dateRange: TimeRange }) => void; disableAutoFocus?: boolean; screenTitle?: string; indexPatterns?: Array; intl: InjectedIntl; + isLoading?: boolean; prepend?: React.ReactNode; showQueryInput?: boolean; showDatePicker?: boolean; @@ -125,6 +127,18 @@ function QueryBarTopRowUI(props: Props) { } } + function onRefresh({ start, end }: OnRefreshProps) { + const retVal = { + dateRange: { + from: start, + to: end, + }, + }; + if (props.onRefresh) { + props.onRefresh(retVal); + } + } + function onSubmit({ query, dateRange }: { query?: Query; dateRange: TimeRange }) { handleLuceneSyntaxWarning(); @@ -175,6 +189,7 @@ function QueryBarTopRowUI(props: Props) { @@ -227,6 +242,7 @@ function QueryBarTopRowUI(props: Props) { isPaused={props.isRefreshPaused} refreshInterval={props.refreshInterval} onTimeChange={onTimeChange} + onRefresh={onRefresh} onRefreshChange={props.onRefreshChange} showUpdateButton={false} recentlyUsedRanges={recentlyUsedRanges} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx index c84e460a1556c..d801f8a69e2d6 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx @@ -17,7 +17,8 @@ * under the License. */ -import React from 'react'; +import React, { useState, useEffect } from 'react'; +import { Subscription } from 'rxjs'; import { Filter } from '@kbn/es-query'; import { CoreStart } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; @@ -64,8 +65,48 @@ export function createSearchBar({ // App name should come from the core application service. // Until it's available, we'll ask the user to provide it for the pre-wired component. return (props: StatetfulSearchBarProps) => { + const tfRefreshInterval = timefilter.timefilter.getRefreshInterval(); + const fmFilters = filterManager.getFilters(); + const [refreshInterval, setRefreshInterval] = useState(tfRefreshInterval.value); + const [refreshPaused, setRefreshPaused] = useState(tfRefreshInterval.pause); + + const [filters, setFilters] = useState(fmFilters); + + // We do not really need to keep track of the time + // since this is just for initialization const timeRange = timefilter.timefilter.getTime(); - const refreshInterval = timefilter.timefilter.getRefreshInterval(); + + useEffect(() => { + let isSubscribed = true; + const subscriptions = new Subscription(); + subscriptions.add( + timefilter.timefilter.getRefreshIntervalUpdate$().subscribe({ + next: () => { + if (isSubscribed) { + const newRefreshInterval = timefilter.timefilter.getRefreshInterval(); + setRefreshInterval(newRefreshInterval.value); + setRefreshPaused(newRefreshInterval.pause); + } + }, + }) + ); + + subscriptions.add( + filterManager.getUpdates$().subscribe({ + next: () => { + if (isSubscribed) { + const newFilters = filterManager.getFilters(); + setFilters(newFilters); + } + }, + }) + ); + + return () => { + isSubscribed = false; + subscriptions.unsubscribe(); + }; + }, []); return ( void; // User has cleared the active query, your app should clear the entire query bar onClearSavedQuery?: () => void; + + onRefresh?: (payload: { dateRange: TimeRange }) => void; } export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps; @@ -377,6 +380,7 @@ class SearchBarUI extends Component { screenTitle={this.props.screenTitle} onSubmit={this.onQueryBarSubmit} indexPatterns={this.props.indexPatterns} + isLoading={this.props.isLoading} prepend={this.props.showFilterBar ? savedQueryManagement : undefined} showDatePicker={this.props.showDatePicker} dateRangeFrom={this.state.dateRangeFrom} @@ -385,6 +389,7 @@ class SearchBarUI extends Component { refreshInterval={this.props.refreshInterval} showAutoRefreshOnly={this.props.showAutoRefreshOnly} showQueryInput={this.props.showQueryInput} + onRefresh={this.props.onRefresh} onRefreshChange={this.props.onRefreshChange} onChange={this.onQueryBarChange} isDirty={this.isDirty()} diff --git a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx index 3fbfec364fad9..0c677bea98536 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/index.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/index.tsx @@ -23,7 +23,7 @@ import { Query } from '../../query/query_bar'; export * from './components'; -type SavedQueryTimeFilter = TimeRange & { +export type SavedQueryTimeFilter = TimeRange & { refreshInterval: RefreshInterval; }; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts index 02f32ec64db53..feba88dbe8b90 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/elements/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { applyElementStrings } from '../strings'; +import { applyElementStrings } from '../../i18n/elements'; import { areaChart } from './area_chart'; import { bubbleChart } from './bubble_chart'; import { debug } from './debug'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts index 3c1b31f0da526..c80c37bb1ed56 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/location.ts @@ -6,7 +6,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; const noop = () => {}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts index ca09641c89409..2a79703d652f6 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts @@ -8,7 +8,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { Handlebars } from '../../../common/lib/handlebars'; import { Datatable, Render, Style } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = Datatable | null; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/urlparam.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/urlparam.ts index 1cbd0b1accfd1..a9ab9440ac181 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/urlparam.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/browser/urlparam.ts @@ -6,7 +6,7 @@ import { parse } from 'url'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { param: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/compare.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/compare.js index 3832030cd9058..2a255ff742962 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/compare.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/compare.js @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { compare } from '../compare'; import { functionWrapper } from '../../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../../strings'; +import { getFunctionErrors } from '../../../../i18n'; const errors = getFunctionErrors().compare; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/progress.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/progress.js index 0f270d50cf054..d53fe94d4a838 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/progress.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/__tests__/progress.js @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { progress } from '../progress'; import { functionWrapper } from '../../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../../strings'; +import { getFunctionErrors } from '../../../../i18n'; import { fontStyle } from './fixtures/test_styles'; const errors = getFunctionErrors().progress; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/all.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/all.ts index 798f524061156..e7335f8c739a9 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/all.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/all.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { condition: boolean[]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.test.js index f04db7523424f..c46b2277859d0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { emptyTable, testTable } from './__tests__/fixtures/test_tables'; import { alterColumn } from './alterColumn'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.ts index fefa404e55c81..53678027513bf 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/alterColumn.ts @@ -7,7 +7,7 @@ import { omit } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable, DatatableColumn, DatatableColumnType } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/any.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/any.ts index 137317a2459a0..89486f85d469a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/any.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/any.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { condition: boolean[]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/as.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/as.ts index 3d994e18f7726..8c68f89183c1c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/as.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/as.ts @@ -8,7 +8,7 @@ import { getType } from '@kbn/interpreter/common'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { name: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axisConfig.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axisConfig.ts index a65d451c6fadf..0d6f1e0033ed5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axisConfig.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axisConfig.ts @@ -7,7 +7,7 @@ import moment from 'moment'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Position } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { show: boolean; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axis_config.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axis_config.test.js index 11c5573452125..04552b988e561 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axis_config.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/axis_config.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { testTable } from './__tests__/fixtures/test_tables'; import { axisConfig } from './axisConfig'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/case.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/case.ts index 724cc9eadcf5b..46ac12b9ca1ae 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/case.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/case.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { when: () => any; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/clear.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/clear.ts index 847a14013f04c..4408a2fae7276 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/clear.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/clear.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export function clear(): ExpressionFunction<'clear', any, {}, null> { const { help } = getFunctionHelp().clear; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/columns.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/columns.ts index a47b2f86a9c19..f8370be22d106 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/columns.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/columns.ts @@ -7,7 +7,7 @@ import { omit, pick, find } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable, DatatableColumn } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { include: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/compare.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/compare.ts index 041cb9eb9ac89..7abb078d84e5b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/compare.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/compare.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; export enum Operation { EQ = 'eq', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/containerStyle.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/containerStyle.ts index 7a5451a202f4e..0f7b9294dfacf 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/containerStyle.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/containerStyle.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { ContainerStyle, Overflow, BackgroundRepeat, BackgroundSize } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; // @ts-ignore untyped local import { isValidUrl } from '../../../common/lib/url'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/container_style.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/container_style.test.js index f57aaea7bac8b..3edae257ce69a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/container_style.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/container_style.test.js @@ -6,7 +6,7 @@ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; import { elasticLogo } from '../../lib/elastic_logo'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { containerStyle } from './containerStyle'; const errors = getFunctionErrors().containerStyle; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/context.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/context.ts index de99126c0de72..af1de680c39c7 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/context.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/context.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export function context(): ExpressionFunction<'context', any, {}, any> { const { help } = getFunctionHelp().context; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.test.js index b9eb65eb36af2..58d019d79d801 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { csv } from './csv'; const errors = getFunctionErrors().csv; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.ts index c1e4b39174ab0..b365d5877ae8d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/csv.ts @@ -7,7 +7,7 @@ import Papa from 'papaparse'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { data: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.test.js index 8886e0f160cb9..96b122d8a0776 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.test.js @@ -6,7 +6,7 @@ import sinon from 'sinon'; import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { date } from './date'; const errors = getFunctionErrors().date; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.ts index b20b92c22e2c2..feec8423ef1b6 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/date.ts @@ -6,7 +6,7 @@ import moment from 'moment'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { value: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/do.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/do.ts index 43c58e1a0a3fe..819e0747f5f48 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/do.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/do.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { fn: any[]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/dropdownControl.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/dropdownControl.ts index d82b581a64181..69a12331e134f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/dropdownControl.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/dropdownControl.ts @@ -7,7 +7,7 @@ import { uniq } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable, Render } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { filterColumn: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/eq.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/eq.ts index ea330ef35462e..516940ae741a6 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/eq.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/eq.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { value: Context; 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 773b5459f5aa7..c1c1bd51801d6 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 @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/filterrows.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/filterrows.ts index d14b93851eb45..e5ddacedcd11d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/filterrows.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/filterrows.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { fn: (datatable: Datatable) => Promise; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatdate.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatdate.ts index 8af23678d769e..1c3c2c3f5e536 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatdate.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatdate.ts @@ -6,7 +6,7 @@ import moment from 'moment'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export interface Arguments { format: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatnumber.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatnumber.ts index 2e54372d747f7..ff51c4aac820b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatnumber.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/formatnumber.ts @@ -6,7 +6,7 @@ import numeral from '@elastic/numeral'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export interface Arguments { format: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.test.js index 1fb195a4fe658..976681f98260f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { emptyTable, testTable } from './__tests__/fixtures/test_tables'; import { getCell } from './getCell'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.ts index 4909f8fa90299..a3c996d3c5307 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/getCell.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gt.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gt.ts index de420bfcb383e..c00ecd939e2d6 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gt.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gt.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = number | string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gte.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gte.ts index d24c58632fa2d..727ce8406057d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gte.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/gte.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = number | string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/head.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/head.ts index 7af674fc91cc4..523a71c9c6832 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/head.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/head.ts @@ -7,7 +7,7 @@ import { take } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { count: number; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/if.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/if.ts index c5e07d7bbdec1..5d7d7443b292b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/if.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/if.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { condition: boolean | null; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/image.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/image.ts index dcce6fe4605ee..942fb3f2925db 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/image.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/image.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; // @ts-ignore untyped local import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.test.js index 0e2d902eda086..5c778300133a0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { testTable } from './__tests__/fixtures/test_tables'; import { joinRows } from './join_rows'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.ts index 283a269c9e502..5f06834ea164b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/join_rows.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lt.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lt.ts index 474591a40f881..9cd8b02cc14ec 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lt.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lt.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = number | string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lte.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lte.ts index 7e8f36d3c8a3c..b6108a61b9585 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lte.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/lte.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = number | string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/mapColumn.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/mapColumn.ts index 387bd67fc5671..1df6d9e9882a8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/mapColumn.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/mapColumn.ts @@ -8,7 +8,7 @@ import { getType } from '@kbn/interpreter/common'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { name: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.test.js index 8967f114e19bb..530a0043a7ef1 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { emptyTable, testTable } from './__tests__/fixtures/test_tables'; import { math } from './math'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts index 87b596ceb51ba..c180b6186ee92 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/math.ts @@ -10,7 +10,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { pivotObjectArray } from '../../../common/lib/pivot_object_array'; import { Datatable, isDatatable } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { expression: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/metric.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/metric.ts index 02d212f12fdef..55db96fcc11bc 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/metric.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/metric.ts @@ -7,7 +7,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { openSans } from '../../../common/lib/fonts'; import { Render, Style } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = number | string | null; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/neq.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/neq.ts index f03b2d40252b5..c9ae246c39f14 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/neq.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/neq.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; type Context = boolean | number | string | null; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/palette.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/palette.ts index b0537ac300927..e71888ff80cfc 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/palette.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/palette.ts @@ -7,7 +7,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { palettes } from '../../../common/lib/palettes'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { color: string[]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/pie.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/pie.ts index 32f3ea7fbe32e..6aff702280ff2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/pie.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/pie.ts @@ -12,7 +12,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { getColorsFromPalette } from '../../../common/lib/get_colors_from_palette'; // @ts-ignore untyped local import { getLegendConfig } from '../../../common/lib/get_legend_config'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; import { Legend, Palette, PointSeries, Render, SeriesStyle, Style } from '../../../types'; interface PieSeriesOptions { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/plot/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/plot/index.ts index 5d976f7cf4d3c..f366d73e55c3c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/plot/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/plot/index.ts @@ -16,7 +16,7 @@ import { getFlotAxisConfig } from './get_flot_axis_config'; import { getFontSpec } from './get_font_spec'; import { seriesStyleToFlot } from './series_style_to_flot'; import { getTickHash } from './get_tick_hash'; -import { getFunctionHelp } from '../../../strings'; +import { getFunctionHelp } from '../../../../i18n'; import { AxisConfig, PointSeries, Render, SeriesStyle, Palette, Legend } from '../../../../types'; interface Arguments { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.test.js index 0cd7643ae5fb9..59c76e594a40a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.test.js @@ -5,7 +5,7 @@ */ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { testTable } from './__tests__/fixtures/test_tables'; import { ply } from './ply'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.ts index ae64da6778368..4816f133f5edb 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/ply.ts @@ -7,7 +7,7 @@ import { groupBy, flatten, pick, map } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable, DatatableColumn } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { by: string[]; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/progress.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/progress.ts index bbad9f8864c8e..47b7b01625dcd 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/progress.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/progress.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { openSans } from '../../../common/lib/fonts'; import { Render, Style } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; export enum Shape { GAUGE = 'gauge', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/render.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/render.ts index e1372d18bfcb4..2b509f717690b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/render.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/render.ts @@ -6,7 +6,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Render, ContainerStyle } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; // @ts-ignore unconverted local file import { DEFAULT_ELEMENT_CSS } from '../../../common/lib/constants'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/repeatImage.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/repeatImage.ts index 55d80bedd39fe..6a80fd80765af 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/repeatImage.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/repeatImage.ts @@ -10,7 +10,7 @@ import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl'; // @ts-ignore .png file import { elasticOutline } from '../../lib/elastic_outline'; import { Render } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { image: string | null; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/replace.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/replace.ts index fc339b056edcc..c2e394ce61034 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/replace.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/replace.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { pattern: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts index 1e2d9c96b3c24..ce532cb887bf5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/revealImage.ts @@ -10,7 +10,7 @@ import { resolveWithMissingImage } from '../../../common/lib/resolve_dataurl'; // @ts-ignore .png file import { elasticOutline } from '../../lib/elastic_outline'; import { Render } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; export enum Origin { TOP = 'top', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/reveal_image.test.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/reveal_image.test.js index 0426cdcfe6f5b..2efc91e93ddc2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/reveal_image.test.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/reveal_image.test.js @@ -7,7 +7,7 @@ import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; import { elasticOutline } from '../../lib/elastic_outline'; import { elasticLogo } from '../../lib/elastic_logo'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { revealImage } from './revealImage'; const errors = getFunctionErrors().revealImage; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rounddate.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rounddate.ts index 45278d8a8e385..c914a67adfe5a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rounddate.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rounddate.ts @@ -6,7 +6,7 @@ import moment from 'moment'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export interface Arguments { format: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rowCount.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rowCount.ts index 5d4d2908c44a9..66bb864f41fb3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rowCount.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/rowCount.ts @@ -6,7 +6,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export function rowCount(): ExpressionFunction<'rowCount', Datatable, {}, number> { const { help } = getFunctionHelp().rowCount; 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 8c8f53ad0d74e..7541924008845 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 @@ -14,7 +14,7 @@ import { EmbeddableExpressionType, EmbeddableExpression, } from '../../expression_types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { id: string; 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 72ab334e68ca4..3159daee75bc6 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 @@ -14,7 +14,7 @@ import { import { buildEmbeddableFilters } from '../../../server/lib/build_embeddable_filters'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { id: string; 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 b3bc9b8ca69b7..cd01acd4387be 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 { } from '../../expression_types'; import { buildEmbeddableFilters } from '../../../server/lib/build_embeddable_filters'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { id: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/seriesStyle.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/seriesStyle.ts index 2cbe1a05728fa..98466459d9b0e 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/seriesStyle.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/seriesStyle.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; const name = 'seriesStyle'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/shape.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/shape.ts index 2bd0caf1a4a55..a3cd5de5b466a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/shape.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/shape.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; export enum Shape { ARROW = 'arrow', diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/sort.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/sort.ts index 2dfd42be09039..a9c282bf4fad3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/sort.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/sort.ts @@ -7,7 +7,7 @@ import { sortBy } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { by: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/staticColumn.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/staticColumn.ts index 1b4b75d263df9..9ffb5ff974778 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/staticColumn.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/staticColumn.ts @@ -8,7 +8,7 @@ import { getType } from '@kbn/interpreter/common'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { name: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/string.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/string.ts index facbe5eddea38..d1349e694ce53 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/string.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/string.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { value: Array; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/switch.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/switch.ts index 0c546a7027601..52d326e471fff 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/switch.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/switch.ts @@ -5,7 +5,7 @@ */ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Case } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { case: Array<() => Promise>; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/table.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/table.ts index 4df47bad98a90..c642eda3d0666 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/table.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/table.ts @@ -6,7 +6,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable, Render, Style } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { font: Style; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/tail.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/tail.ts index 4c1523dc00e2e..89c130ad60b90 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/tail.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/tail.ts @@ -7,7 +7,7 @@ import { takeRight } from 'lodash'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Datatable } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { count: number; 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 d672652df8ebd..aeab0d50c31a7 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 @@ -6,7 +6,7 @@ import sinon from 'sinon'; import { functionWrapper } from '../../../__tests__/helpers/function_wrapper'; -import { getFunctionErrors } from '../../strings'; +import { getFunctionErrors } from '../../../i18n'; import { emptyFilter } from './__tests__/fixtures/test_filters'; import { timefilter } from './timefilter'; 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 3a5e770de955a..1fa6abf0b51e8 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 @@ -7,7 +7,7 @@ import dateMath from '@elastic/datemath'; import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Filter } from '../../../types'; -import { getFunctionHelp, getFunctionErrors } from '../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts index 6cf3eb7d6605d..2e7de436ae75b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/common/timefilterControl.ts @@ -6,7 +6,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { Render } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { column: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts index 865596db33316..02f8efcfde95d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts @@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash'; import ci from './ci.json'; import shirts from './shirts.json'; -import { getFunctionErrors } from '../../../strings'; +import { getFunctionErrors } from '../../../../i18n'; export enum DemoRows { CI = 'ci', 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 92e5ecfc2bee2..3c3d1a8ff12cf 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,7 +10,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { queryDatatable } from '../../../../common/lib/datatable/query'; import { DemoRows, getDemoRows } from './get_demo_rows'; import { Filter, Datatable, DatatableColumn, DatatableRow } from '../../../../types'; -import { getFunctionHelp } from '../../../strings'; +import { getFunctionHelp } from '../../../../i18n'; interface Arguments { type: string; 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 667d52dfdb30f..23de713f0be3a 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 @@ -8,7 +8,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { buildESRequest } from '../../../server/lib/build_es_request'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { index: string | null; 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 3dcc6807724b4..620de0be7540a 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 @@ -9,7 +9,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { queryEsSQL } from '../../../server/lib/query_es_sql'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { index: string; 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 a505f41a75908..d57fa64faf4c2 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 @@ -8,7 +8,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; // @ts-ignore untyped local import { queryEsSQL } from '../../../server/lib/query_es_sql'; import { Filter } from '../../../types'; -import { getFunctionHelp } from '../../strings'; +import { getFunctionHelp } from '../../../i18n'; interface Arguments { query: string; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts index 9218f03841c1e..5889864df3c1d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/pointseries/index.ts @@ -19,7 +19,7 @@ import { unquoteString } from '../../../../common/lib/unquote_string'; import { isColumnReference } from './lib/is_column_reference'; // @ts-ignore Untyped local import { getExpressionType } from './lib/get_expression_type'; -import { getFunctionHelp, getFunctionErrors } from '../../../strings'; +import { getFunctionHelp, getFunctionErrors } from '../../../../i18n'; import { Datatable, DatatableRow, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_errors.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_errors.ts deleted file mode 100644 index b3be23c084aab..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_errors.ts +++ /dev/null @@ -1,41 +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 { errors as alterColumn } from './alterColumn'; -import { errors as axisConfig } from './axisConfig'; -import { errors as compare } from './compare'; -import { errors as containerStyle } from './containerStyle'; -import { errors as csv } from './csv'; -import { errors as date } from './date'; -import { errors as getCell } from './getCell'; -import { errors as joinRows } from './join_rows'; -import { errors as image } from './image'; -import { errors as math } from './math'; -import { errors as ply } from './ply'; -import { errors as progress } from './progress'; -import { errors as revealImage } from './revealImage'; -import { errors as timefilter } from './timefilter'; -import { errors as demodata } from './demodata'; -import { errors as pointseries } from './pointseries'; - -export const getFunctionErrors = () => ({ - alterColumn, - axisConfig, - compare, - containerStyle, - csv, - date, - getCell, - image, - joinRows, - math, - ply, - progress, - revealImage, - timefilter, - demodata, - pointseries, -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_help.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_help.ts deleted file mode 100644 index 327865ad1c61b..0000000000000 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_help.ts +++ /dev/null @@ -1,228 +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 { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; -import { CanvasFunction } from '../../../types'; -import { UnionToIntersection } from '../../../types'; - -import { help as all } from './all'; -import { help as alterColumn } from './alterColumn'; -import { help as any } from './any'; -import { help as asFn } from './as'; -import { help as axisConfig } from './axisConfig'; -import { help as caseFn } from './case'; -import { help as clear } from './clear'; -import { help as columns } from './columns'; -import { help as compare } from './compare'; -import { help as containerStyle } from './containerStyle'; -import { help as context } from './context'; -import { help as csv } from './csv'; -import { help as date } from './date'; -import { help as demodata } from './demodata'; -import { help as doFn } from './do'; -import { help as dropdownControl } from './dropdownControl'; -import { help as eq } from './eq'; -import { help as escount } from './escount'; -import { help as esdocs } from './esdocs'; -import { help as essql } from './essql'; -import { help as exactly } from './exactly'; -import { help as filterrows } from './filterrows'; -import { help as formatdate } from './formatdate'; -import { help as formatnumber } from './formatnumber'; -import { help as getCell } from './getCell'; -import { help as gt } from './gt'; -import { help as gte } from './gte'; -import { help as head } from './head'; -import { help as ifFn } from './if'; -import { help as image } from './image'; -import { help as joinRows } from './join_rows'; -import { help as location } from './location'; -import { help as lt } from './lt'; -import { help as lte } from './lte'; -import { help as mapColumn } from './mapColumn'; -import { help as markdown } from './markdown'; -import { help as math } from './math'; -import { help as metric } from './metric'; -import { help as neq } from './neq'; -import { help as palette } from './palette'; -import { help as pie } from './pie'; -import { help as plot } from './plot'; -import { help as ply } from './ply'; -import { help as pointseries } from './pointseries'; -import { help as progress } from './progress'; -import { help as render } from './render'; -import { help as repeatImage } from './repeatImage'; -import { help as replace } from './replace'; -import { help as revealImage } from './revealImage'; -import { help as rounddate } from './rounddate'; -import { help as rowCount } from './rowCount'; -import { help as savedMap } from './saved_map'; -import { help as savedSearch } from './saved_search'; -import { help as savedVisualization } from './saved_visualization'; -import { help as seriesStyle } from './seriesStyle'; -import { help as shape } from './shape'; -import { help as sort } from './sort'; -import { help as staticColumn } from './staticColumn'; -import { help as string } from './string'; -import { help as switchFn } from './switch'; -import { help as table } from './table'; -import { help as tail } from './tail'; -import { help as timefilter } from './timefilter'; -import { help as timefilterControl } from './timefilterControl'; -import { help as urlparam } from './urlparam'; - -/** - * This type defines an entry in the `FunctionHelpMap`. It uses - * an `ExpressionFunction` to infer its `Arguments` in order to strongly-type that - * entry. - * - * For example: - * -``` - interface Arguments { - bar: string; - baz: number; - } - - function foo(): ExpressionFunction<'foo', Context, Arguments, Return> { - // ... - } - - const help: FunctionHelp = { - help: 'Some help for foo', - args: { - bar: 'Help for bar.', // pass; error if missing - baz: 'Help for baz.', // pass; error if missing - zap: 'Help for zap.`, // error: zap doesn't exist - } - }; -``` - * This allows one to ensure each argument is present, and no extraneous arguments - * remain. - */ -export type FunctionHelp = T extends ExpressionFunction< - infer Name, - infer Context, - infer Arguments, - infer Return -> - ? { - help: string; - args: { [key in keyof Arguments]: string }; - } - : never; - -// This internal type infers a Function name and uses `FunctionHelp` above to build -// a dictionary entry. This can be used to ensure every Function is defined and all -// Arguments have help strings. -// -// For example: -// -// function foo(): ExpressionFunction<'foo', Context, Arguments, Return> { -// // ... -// } -// -// const map: FunctionHelpMap = { -// foo: FunctionHelp, -// } -// -// Given a collection of functions, the map would contain each entry. -// -type FunctionHelpMap = T extends ExpressionFunction< - infer Name, - infer Context, - infer Arguments, - infer Return -> - ? { [key in Name]: FunctionHelp } - : never; - -// This internal type represents an exhaustive dictionary of `FunctionHelp` types, -// organized by Function Name and then Function Argument. -// -// This type indexes the existing function factories, reverses the union to an -// intersection, and produces the dictionary of strings. -type FunctionHelpDict = UnionToIntersection>; - -/** - * Help text for Canvas Functions should be properly localized. This function will - * return a dictionary of help strings, organized by `CanvasFunction` specification - * and then by available arguments within each `CanvasFunction`. - * - * This a function, rather than an object, to future-proof string initialization, - * if ever necessary. - */ -export const getFunctionHelp = (): FunctionHelpDict => ({ - all, - alterColumn, - any, - as: asFn, - axisConfig, - case: caseFn, - clear, - columns, - compare, - containerStyle, - context, - csv, - date, - demodata, - do: doFn, - dropdownControl, - eq, - escount, - esdocs, - essql, - exactly, - filterrows, - formatdate, - formatnumber, - getCell, - gt, - gte, - head, - if: ifFn, - joinRows, - image, - location, - lt, - lte, - mapColumn, - markdown, - math, - metric, - neq, - palette, - pie, - plot, - ply, - pointseries, - progress, - render, - repeatImage, - replace, - revealImage, - rounddate, - rowCount, - // TODO: elastic/kibana#44822 Disabling pending filters work - // @ts-ignore - savedMap, - // @ts-ignore - savedSearch, - // @ts-ignore - savedVisualization, - seriesStyle, - shape, - sort, - staticColumn, - string, - switch: switchFn, - table, - tail, - timefilter, - timefilterControl, - urlparam, -}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/extended_template.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/extended_template.tsx index 6061169cbe9f6..0ec722e370b40 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/extended_template.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/extended_template.tsx @@ -10,7 +10,7 @@ import { EuiSelect, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; import immutable from 'object-path-immutable'; import { get } from 'lodash'; import { ExpressionAST } from '../../../../types'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; const { AxisConfig: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/index.ts index 3f250afacc3be..488631d8b31fa 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/axis_config/index.ts @@ -7,7 +7,7 @@ import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { SimpleTemplate } from './simple_template'; import { ExtendedTemplate } from './extended_template'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; const { AxisConfig: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/index.js index ffa0c6983d573..773e41dc3abf3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/index.js @@ -12,7 +12,7 @@ import { sortBy } from 'lodash'; import { getType } from '@kbn/interpreter/common'; import { createStatefulPropHoc } from '../../../../public/components/enhance/stateful_prop'; import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; import { SimpleMathFunction } from './simple_math_function'; import { getFormObject } from './get_form_object'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/simple_math_function.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/simple_math_function.js index 219b3582a7526..c29e28f51e89c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/simple_math_function.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/datacolumn/simple_math_function.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiSelect } from '@elastic/eui'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; const { DataColumn: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts index 6bf6f38f7d3a2..cc49943832d07 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/date_format/index.ts @@ -11,7 +11,7 @@ import { AdvancedSettings } from '../../../../public/lib/kibana_advanced_setting // @ts-ignore untyped local lib import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { ArgumentFactory } from '../../../../types/arguments'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; const { DateFormat: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/filter_group.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/filter_group.js index f539727daff73..4b3c297a7e7f0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/filter_group.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/filter_group.js @@ -16,7 +16,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { FilterGroup: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/file.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/file.js index 23431747178d1..8bc5e1d68dcab 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/file.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/file.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiFilePicker } from '@elastic/eui'; import { Loading } from '../../../../../public/components/loading/loading'; -import { ArgumentStrings } from '../../../../strings'; +import { ArgumentStrings } from '../../../../../i18n'; const { ImageUpload: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/link.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/link.js index 7278e531bb6f0..a9beabcd1f8bd 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/link.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/forms/link.js @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiFormRow, EuiFlexGroup, EuiFlexItem, EuiFieldText, EuiButton } from '@elastic/eui'; -import { ArgumentStrings } from '../../../../strings'; +import { ArgumentStrings } from '../../../../../i18n'; const { ImageUpload: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/index.js index 53ae06bbe8d9a..e8c433fb8752d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/image_upload/index.js @@ -15,7 +15,7 @@ import { isValidHttpUrl } from '../../../../common/lib/httpurl'; import { encode } from '../../../../common/lib/dataurl'; import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { VALID_IMAGE_TYPES } from '../../../../common/lib/constants'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; import { FileForm, LinkForm } from './forms'; const { ImageUpload: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number.js index 1ed8f10f6b310..aae96f1266a36 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number.js @@ -11,7 +11,7 @@ import { EuiFieldNumber, EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/e import { get } from 'lodash'; import { createStatefulPropHoc } from '../../../public/components/enhance/stateful_prop'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Number: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts index 8f68042f0dca6..7654774901ff0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts @@ -10,7 +10,7 @@ import { AdvancedSettings } from '../../../../public/lib/kibana_advanced_setting // @ts-ignore untyped local lib import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component'; import { ArgumentFactory } from '../../../../types/arguments'; -import { ArgumentStrings } from '../../../strings'; +import { ArgumentStrings } from '../../../../i18n'; const { NumberFormat: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js index d95b73859e531..69f584af41556 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/palette.js @@ -10,7 +10,7 @@ import { get } from 'lodash'; import { getType } from '@kbn/interpreter/common'; import { PalettePicker } from '../../../public/components/palette_picker'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Palette: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/percentage.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/percentage.js index 82a234042f811..5fa2ca42ca78a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/percentage.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/percentage.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiRange } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Percentage: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/range.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/range.js index 7b99fc8c5442f..b3d1670c5d711 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/range.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/range.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiRange } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Range: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/select.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/select.js index 095aa2dc4f3fd..478acd41d797f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/select.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/select.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiSelect } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Select: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js index 7a3dad36dcf01..c056e7d1f2281 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; import { ShapePickerPopover } from '../../../public/components/shape_picker_popover/'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Shape: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/string.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/string.js index 46749cc8562e2..d8a7188dfab28 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/string.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/string.js @@ -11,7 +11,7 @@ import { EuiFlexItem, EuiFlexGroup, EuiFieldText, EuiButton } from '@elastic/eui import { get } from 'lodash'; import { createStatefulPropHoc } from '../../../public/components/enhance/stateful_prop'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { String: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/textarea.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/textarea.js index e7009300b32e6..a0af71585eed0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/textarea.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/textarea.js @@ -11,7 +11,7 @@ import { EuiFormRow, EuiTextArea, EuiSpacer, EuiButton } from '@elastic/eui'; import { get } from 'lodash'; import { createStatefulPropHoc } from '../../../public/components/enhance/stateful_prop'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Textarea: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js index e52679d80e908..462537e82b164 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/toggle.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiFormRow, EuiSwitch } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { ArgumentStrings } from '../../strings'; +import { ArgumentStrings } from '../../../i18n'; const { Toggle: strings } = ArgumentStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/demodata.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/demodata.js index 6438cc5689747..ec492f52747c1 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/demodata.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/demodata.js @@ -8,8 +8,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText } from '@elastic/eui'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { DataSourceStrings } from '../../strings'; -import { ComponentStrings, CANVAS } from '../../../i18n'; +import { ComponentStrings, CANVAS, DataSourceStrings } from '../../../i18n'; const { DemoData: strings } = DataSourceStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/essql.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/essql.js index 8b6b8695a74a3..43f2fa63aff70 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/essql.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/essql.js @@ -9,7 +9,7 @@ import PropTypes from 'prop-types'; import { EuiFormRow, EuiTextArea } from '@elastic/eui'; import { getSimpleArg, setSimpleArg } from '../../../public/lib/arg_helpers'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { DataSourceStrings } from '../../strings'; +import { DataSourceStrings } from '../../../i18n'; const { Essql: strings } = DataSourceStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/timelion.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/timelion.js index 3065f0fc71307..06efb6a791a2d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/timelion.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/datasources/timelion.js @@ -18,8 +18,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { getSimpleArg, setSimpleArg } from '../../../public/lib/arg_helpers'; import { templateFromReactComponent } from '../../../public/lib/template_from_react_component'; -import { DataSourceStrings } from '../../strings'; -import { TIMELION, CANVAS } from '../../../i18n'; +import { DataSourceStrings, TIMELION, CANVAS } from '../../../i18n'; const { Timelion: strings } = DataSourceStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/math.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/math.js index 47edded025cdd..ec6496da9e1b9 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/math.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/math.js @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { getState, getValue } from '../../../public/lib/resolved_arg'; -import { ModelStrings } from '../../strings'; +import { ModelStrings } from '../../../i18n'; const { Math: strings } = ModelStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/point_series.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/point_series.js index bea7e4d643568..5a6a74229d881 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/point_series.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/models/point_series.js @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { getState, getValue } from '../../../public/lib/resolved_arg'; -import { ModelStrings } from '../../strings'; +import { ModelStrings } from '../../../i18n'; const { PointSeries: strings } = ModelStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatdate.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatdate.ts index a2e440dd62e13..f871b1622b2d2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatdate.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatdate.ts @@ -6,7 +6,7 @@ import { TransformFactory } from '../../../types/transforms'; import { Arguments } from '../../functions/common/formatdate'; -import { TransformStrings } from '../../strings'; +import { TransformStrings } from '../../../i18n'; const { FormatDate: strings } = TransformStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatnumber.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatnumber.ts index a67695e635fb5..45dc0eec20e06 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatnumber.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/formatnumber.ts @@ -6,7 +6,7 @@ import { TransformFactory } from '../../../types/transforms'; import { Arguments } from '../../functions/common/formatnumber'; -import { TransformStrings } from '../../strings'; +import { TransformStrings } from '../../../i18n'; const { FormatNumber: strings } = TransformStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/rounddate.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/rounddate.ts index bc7c8b983bbae..362f9c6a63eba 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/rounddate.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/rounddate.ts @@ -6,7 +6,7 @@ import { TransformFactory } from '../../../types/transforms'; import { Arguments } from '../../functions/common/rounddate'; -import { TransformStrings } from '../../strings'; +import { TransformStrings } from '../../../i18n'; const { RoundDate: strings } = TransformStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/sort.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/sort.js index e15f216634223..184870dbd80b8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/sort.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/transforms/sort.js @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { getState, getValue } from '../../../public/lib/resolved_arg'; -import { TransformStrings } from '../../strings'; +import { TransformStrings } from '../../../i18n'; const { Sort: strings } = TransformStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/dropdownControl.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/dropdownControl.js index d8c4962d32aef..30ce5a2497f79 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/dropdownControl.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/dropdownControl.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { DropdownControl: strings } = ViewStrings; export const dropdownControl = () => ({ diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/getCell.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/getCell.js index 4e80c99553fd2..a8b1ab4cb3e4c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/getCell.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/getCell.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { GetCell: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/image.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/image.js index 82ec071b510d7..4dcdc650d1404 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/image.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/image.js @@ -6,7 +6,7 @@ import { elasticLogo } from '../../lib/elastic_logo'; import { resolveFromArgs } from '../../../common/lib/resolve_dataurl'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Image: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js index 0475c437ba657..1c46bc6dd57c2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/markdown.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Markdown: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js index 6aa7d795ecc3e..213a2e0dd3b81 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/metric.js @@ -6,7 +6,7 @@ import { openSans } from '../../../common/lib/fonts'; import { AdvancedSettings } from '../../../public/lib/kibana_advanced_settings'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Metric: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js index ebbce9486d9e1..6a7a89231f250 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/pie.js @@ -7,7 +7,7 @@ import { map, uniq } from 'lodash'; import { legendOptions } from '../../../public/lib/legend_options'; import { getState, getValue } from '../../../public/lib/resolved_arg'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Pie: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/plot.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/plot.js index 6dec83f13d8e4..6e000336a6b34 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/plot.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/plot.js @@ -7,7 +7,7 @@ import { map, uniq } from 'lodash'; import { getState, getValue } from '../../../public/lib/resolved_arg'; import { legendOptions } from '../../../public/lib/legend_options'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Plot: strings } = ViewStrings; const styleProps = ['lines', 'bars', 'points', 'fill', 'stack']; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/progress.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/progress.js index f046c4c49efd8..ae68c9c5c6031 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/progress.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/progress.js @@ -6,7 +6,7 @@ import { openSans } from '../../../common/lib/fonts'; import { shapes } from '../../renderers/progress/shapes'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Progress: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/render.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/render.js index 0544a32fe9724..6ad83729a48ef 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/render.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/render.js @@ -6,7 +6,7 @@ import { DEFAULT_ELEMENT_CSS } from '../../../common/lib/constants'; import { CSS } from '../../../i18n/constants'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Render: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/repeatImage.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/repeatImage.js index 1f296e23d5904..48cab0837c57b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/repeatImage.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/repeatImage.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { RepeatImage: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/revealImage.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/revealImage.js index 254eff2e6e36b..28b25d54acd1b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/revealImage.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/revealImage.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { RevealImage: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/shape.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/shape.js index c4ecc150c508f..c3e97b4bd5dea 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/shape.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/shape.js @@ -5,7 +5,7 @@ */ import { shapes } from '../../renderers/shape/shapes'; -import { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Shape: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/table.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/table.js index 4a777daf3c8a8..03a13b3f8fe21 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/table.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/table.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Table: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/timefilterControl.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/timefilterControl.js index e79936d661cdf..eb8cbd41e6182 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/timefilterControl.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/views/timefilterControl.js @@ -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 { ViewStrings } from '../../strings'; +import { ViewStrings } from '../../../i18n'; const { Timefilter: strings } = ViewStrings; diff --git a/x-pack/legacy/plugins/canvas/i18n/constants.ts b/x-pack/legacy/plugins/canvas/i18n/constants.ts index b6df930a539f8..3659c369ba0b6 100644 --- a/x-pack/legacy/plugins/canvas/i18n/constants.ts +++ b/x-pack/legacy/plugins/canvas/i18n/constants.ts @@ -26,6 +26,7 @@ export const KIBANA = 'Kibana'; export const LUCENE = 'Lucene'; export const MARKDOWN = 'Markdown'; export const MOMENTJS = 'MomentJS'; +export const MOMENTJS_TIMEZONE_URL = 'https://momentjs.com/timezone/'; export const NUMERALJS = 'NumeralJS'; export const PDF = 'PDF'; export const POST = 'POST'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/apply_strings.ts b/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts similarity index 95% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/apply_strings.ts rename to x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts index 4464ed5dbf185..cfc4e750bc800 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/apply_strings.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/apply_strings.ts @@ -5,7 +5,7 @@ */ import { ElementFactory } from '../../types'; -import { getElementStrings } from './index'; +import { getElementStrings } from './element_strings'; /** * This function takes a set of Canvas Element specification factories, runs them, diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/element_strings.test.ts b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts similarity index 90% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/element_strings.test.ts rename to x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts index 1cc2e356e5fea..14196702824db 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/element_strings.test.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.test.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getElementStrings } from '.'; -import { elementSpecs } from '../elements'; +import { getElementStrings } from './element_strings'; +import { elementSpecs } from '../../canvas_plugin_src/elements'; describe('ElementStrings', () => { const elementStrings = getElementStrings(); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/element_strings.ts b/x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts similarity index 100% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/element_strings.ts rename to x-pack/legacy/plugins/canvas/i18n/elements/element_strings.ts diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/index.ts b/x-pack/legacy/plugins/canvas/i18n/elements/index.ts similarity index 85% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/index.ts rename to x-pack/legacy/plugins/canvas/i18n/elements/index.ts index 89da50363340c..1920ba986bf6c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/index.ts +++ b/x-pack/legacy/plugins/canvas/i18n/elements/index.ts @@ -6,5 +6,3 @@ export * from './apply_strings'; export * from './element_strings'; -export * from './functions'; -export * from './ui'; diff --git a/x-pack/legacy/plugins/canvas/i18n/expression_types.ts b/x-pack/legacy/plugins/canvas/i18n/expression_types.ts index 8060b49ef1ec0..6bc40a2758ab3 100644 --- a/x-pack/legacy/plugins/canvas/i18n/expression_types.ts +++ b/x-pack/legacy/plugins/canvas/i18n/expression_types.ts @@ -140,7 +140,7 @@ export const ArgTypesStrings = { }, }; -export const DataSourceStrings = { +export const ExpressionDataSourceStrings = { ESDocs: { getDisplayName: () => i18n.translate('xpack.canvas.expressionTypes.datasources.esdocsTitle', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/all.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/all.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/all.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/all.ts index 43bdc38340470..16bdf1056f0a8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/all.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/all.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { all } from '../../functions/common/all'; -import { FunctionHelp } from '.'; +import { all } from '../../../canvas_plugin_src/functions/common/all'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { BOOLEAN_TRUE } from '../../../i18n'; +import { BOOLEAN_TRUE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.allHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/alterColumn.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts similarity index 93% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/alterColumn.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts index b9c2c68a5ce26..836c7395ac448 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/alterColumn.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/alter_column.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { alterColumn } from '../../functions/common/alterColumn'; -import { FunctionHelp } from '.'; +import { alterColumn } from '../../../canvas_plugin_src/functions/common/alterColumn'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { DATATABLE_COLUMN_TYPES } from '../../../common/lib'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/any.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/any.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/any.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/any.ts index d071bc58e1d04..126d875aa2e80 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/any.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/any.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { any } from '../../functions/common/any'; -import { FunctionHelp } from '.'; +import { any } from '../../../canvas_plugin_src/functions/common/any'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { BOOLEAN_TRUE } from '../../../i18n'; +import { BOOLEAN_TRUE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.anyHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/as.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/as.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/as.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/as.ts index f40107dc4b388..e95aa641c71b8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/as.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/as.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { asFn } from '../../functions/common/as'; -import { FunctionHelp } from '.'; +import { asFn } from '../../../canvas_plugin_src/functions/common/as'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.asHelpText', { diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/asset.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/asset.ts new file mode 100644 index 0000000000000..1302a067a1453 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/asset.ts @@ -0,0 +1,34 @@ +/* + * 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'; +import { asset } from '../../../public/functions/asset'; +import { FunctionHelp } from '../function_help'; +import { FunctionFactory } from '../../../types'; + +export const help: FunctionHelp> = { + help: i18n.translate('xpack.canvas.functions.assetHelpText', { + defaultMessage: + 'Retrieves Canvas workpad asset objects to provide as argument values. Usually images.', + }), + args: { + id: i18n.translate('xpack.canvas.functions.asset.args.id', { + defaultMessage: 'The ID of the asset to retrieve.', + }), + }, +}; + +export const errors = { + invalidAssetId: (assetId: string) => + new Error( + i18n.translate('xpack.canvas.functions.asset.invalidAssetId', { + defaultMessage: "Could not get the asset by ID: '{assetId}'", + values: { assetId }, + description: + 'This error occurs when there is no asset object associated with the given ID.', + }) + ), +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/axisConfig.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/axis_config.ts similarity index 94% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/axisConfig.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/axis_config.ts index 30ecae13fdc64..15708dd949b22 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/axisConfig.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/axis_config.ts @@ -5,11 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { axisConfig } from '../../functions/common/axisConfig'; -import { FunctionHelp } from '.'; +import { axisConfig } from '../../../canvas_plugin_src/functions/common/axisConfig'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { Position } from '../../../types'; -import { ISO8601 } from '../../../i18n'; +import { ISO8601 } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.axisConfigHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/case.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/case.ts similarity index 90% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/case.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/case.ts index 1a2824fb6af66..8f0689e5e3837 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/case.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/case.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { caseFn } from '../../functions/common/case'; -import { FunctionHelp } from '.'; +import { caseFn } from '../../../canvas_plugin_src/functions/common/case'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; const IF_ARG = '`if`'; const WHEN_ARG = '`when`'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/clear.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/clear.ts similarity index 77% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/clear.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/clear.ts index 6448d0bd4427b..3a88c5af08527 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/clear.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/clear.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { clear } from '../../functions/common/clear'; -import { FunctionHelp } from '.'; +import { clear } from '../../../canvas_plugin_src/functions/common/clear'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT, TYPE_NULL } from '../../../i18n'; +import { CONTEXT, TYPE_NULL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.clearHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/columns.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/columns.ts similarity index 86% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/columns.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/columns.ts index 807acb5b187d9..4d077c5447532 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/columns.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/columns.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { columns } from '../../functions/common/columns'; -import { FunctionHelp } from '.'; +import { columns } from '../../../canvas_plugin_src/functions/common/columns'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.columnsHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/compare.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/compare.ts similarity index 93% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/compare.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/compare.ts index d974526804955..5697881503b84 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/compare.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/compare.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { compare, Operation } from '../../functions/common/compare'; -import { FunctionHelp } from '.'; +import { compare, Operation } from '../../../canvas_plugin_src/functions/common/compare'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { CONTEXT, @@ -16,7 +16,7 @@ import { BOOLEAN_TRUE, BOOLEAN_FALSE, TYPE_NULL, -} from '../../../i18n'; +} from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.compareHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/containerStyle.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/container_style.ts similarity index 94% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/containerStyle.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/container_style.ts index 39eb38a66279c..bef2ccc2a8e3b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/containerStyle.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/container_style.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { containerStyle } from '../../functions/common/containerStyle'; -import { FunctionHelp } from '.'; +import { containerStyle } from '../../../canvas_plugin_src/functions/common/containerStyle'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CSS } from '../../../i18n'; +import { CSS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.containerStyleHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/context.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/context.ts similarity index 80% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/context.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/context.ts index 6dc01030c5755..775264e00dc52 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/context.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/context.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { context } from '../../functions/common/context'; -import { FunctionHelp } from '.'; +import { context } from '../../../canvas_plugin_src/functions/common/context'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.contextHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/csv.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/csv.ts similarity index 88% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/csv.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/csv.ts index 0dbfbc8324842..2c53f1095a822 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/csv.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/csv.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { csv } from '../../functions/common/csv'; -import { FunctionHelp } from '.'; +import { csv } from '../../../canvas_plugin_src/functions/common/csv'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE, CSV } from '../../../i18n'; +import { DATATABLE, CSV } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.csvHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/date.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/date.ts similarity index 90% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/date.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/date.ts index 3df56c51258f1..6964b62bcc582 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/date.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/date.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { date } from '../../functions/common/date'; -import { FunctionHelp } from '.'; +import { date } from '../../../canvas_plugin_src/functions/common/date'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ISO8601, MOMENTJS, JS } from '../../../i18n'; +import { ISO8601, MOMENTJS, JS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.dateHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/demodata.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts similarity index 83% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/demodata.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts index 6b2c81fb3681a..20c7a88ea4f4d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/demodata.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { demodata } from '../../functions/server/demodata'; -import { FunctionHelp } from '.'; +import { demodata } from '../../../canvas_plugin_src/functions/server/demodata'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DemoRows } from '../../functions/server/demodata/get_demo_rows'; +import { DemoRows } from '../../../canvas_plugin_src/functions/server/demodata/get_demo_rows'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.demodataHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/do.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/do.ts similarity index 86% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/do.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/do.ts index fc424192ca01b..2590feb95adb0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/do.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/do.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { doFn } from '../../functions/common/do'; -import { FunctionHelp } from '.'; +import { doFn } from '../../../canvas_plugin_src/functions/common/do'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.doHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/dropdownControl.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/dropdown_control.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/dropdownControl.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/dropdown_control.ts index 789981d396a08..0d051a4f5f068 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/dropdownControl.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/dropdown_control.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { dropdownControl } from '../../functions/common/dropdownControl'; -import { FunctionHelp } from '.'; +import { dropdownControl } from '../../../canvas_plugin_src/functions/common/dropdownControl'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/eq.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/eq.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/eq.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/eq.ts index a67d223dc9bec..a856a81452cd7 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/eq.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/eq.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { eq } from '../../functions/common/eq'; -import { FunctionHelp } from '.'; +import { eq } from '../../../canvas_plugin_src/functions/common/eq'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.eqHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/escount.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/escount.ts similarity index 84% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/escount.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/escount.ts index 68f80f6b1e55f..e8f66978f3716 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/escount.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/escount.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { escount } from '../../functions/server/escount'; -import { FunctionHelp } from '.'; +import { escount } from '../../../canvas_plugin_src/functions/server/escount'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ELASTICSEARCH, LUCENE } from '../../../i18n'; +import { ELASTICSEARCH, LUCENE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.escountHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/esdocs.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/esdocs.ts similarity index 92% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/esdocs.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/esdocs.ts index a61ad06d9426f..5bc44cc147afa 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/esdocs.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/esdocs.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { esdocs } from '../../functions/server/esdocs'; -import { FunctionHelp } from '.'; +import { esdocs } from '../../../canvas_plugin_src/functions/server/esdocs'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ELASTICSEARCH, LUCENE } from '../../../i18n'; +import { ELASTICSEARCH, LUCENE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.esdocsHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/essql.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/essql.ts similarity index 87% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/essql.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/essql.ts index d12c9f3632313..5c4a3559a428e 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/essql.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/essql.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { essql } from '../../functions/server/essql'; -import { FunctionHelp } from '.'; +import { essql } from '../../../canvas_plugin_src/functions/server/essql'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ELASTICSEARCH, SQL, ISO8601, UTC } from '../../../i18n'; +import { ELASTICSEARCH, SQL, ISO8601, UTC } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.essqlHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/exactly.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/exactly.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/exactly.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/exactly.ts index 75b676b5f57be..9ba7285aa9275 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/exactly.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/exactly.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { exactly } from '../../functions/common/exactly'; -import { FunctionHelp } from '.'; +import { exactly } from '../../../canvas_plugin_src/functions/common/exactly'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/filterrows.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/filterrows.ts similarity index 87% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/filterrows.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/filterrows.ts index dc5e6a48c8f97..3c1b6d87a9be5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/filterrows.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/filterrows.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { filterrows } from '../../functions/common/filterrows'; -import { FunctionHelp } from '.'; +import { filterrows } from '../../../canvas_plugin_src/functions/common/filterrows'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE, TYPE_BOOLEAN, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../../../i18n'; +import { DATATABLE, TYPE_BOOLEAN, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.filterrowsHelpText', { diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/filters.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/filters.ts new file mode 100644 index 0000000000000..4f5cb395b9d88 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/filters.ts @@ -0,0 +1,25 @@ +/* + * 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'; +import { filters } from '../../../public/functions/filters'; +import { FunctionHelp } from '../function_help'; +import { FunctionFactory } from '../../../types'; + +export const help: FunctionHelp> = { + help: i18n.translate('xpack.canvas.functions.filtersHelpText', { + defaultMessage: + 'Aggregates element filters from the workpad for use elsewhere, usually a data source.', + }), + args: { + group: i18n.translate('xpack.canvas.functions.filters.args.group', { + defaultMessage: 'The name of the filter group to use.', + }), + ungrouped: i18n.translate('xpack.canvas.functions.filters.args.ungrouped', { + defaultMessage: 'Exclude filters that belong to a filter group?', + }), + }, +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatdate.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/formatdate.ts similarity index 84% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatdate.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/formatdate.ts index 3aa0fb1d421e7..9b60c2f69f120 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatdate.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/formatdate.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { formatdate } from '../../functions/common/formatdate'; -import { FunctionHelp } from '.'; +import { formatdate } from '../../../canvas_plugin_src/functions/common/formatdate'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ISO8601, MOMENTJS } from '../../../i18n'; +import { ISO8601, MOMENTJS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.formatdateHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatnumber.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/formatnumber.ts similarity index 84% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatnumber.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/formatnumber.ts index 9944d6cff89dc..df306eb1c04ed 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/formatnumber.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/formatnumber.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { formatnumber } from '../../functions/common/formatnumber'; -import { FunctionHelp } from '.'; +import { formatnumber } from '../../../canvas_plugin_src/functions/common/formatnumber'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { NUMERALJS } from '../../../i18n'; +import { NUMERALJS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.formatnumberHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/getCell.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/get_cell.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/getCell.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/get_cell.ts index a2e50160218c8..79cc4f7e5c303 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/getCell.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/get_cell.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { getCell } from '../../functions/common/getCell'; -import { FunctionHelp } from '.'; +import { getCell } from '../../../canvas_plugin_src/functions/common/getCell'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.getCellHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gt.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/gt.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gt.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/gt.ts index c03672955b975..caa74e6072539 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gt.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/gt.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { gt } from '../../functions/common/gt'; -import { FunctionHelp } from '.'; +import { gt } from '../../../canvas_plugin_src/functions/common/gt'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.gtHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gte.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/gte.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gte.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/gte.ts index 41098683981d3..f0580ad3d3b3a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/gte.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/gte.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { gte } from '../../functions/common/gte'; -import { FunctionHelp } from '.'; +import { gte } from '../../../canvas_plugin_src/functions/common/gte'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.gteHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/head.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/head.ts similarity index 83% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/head.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/head.ts index 8fe1248f9fc8c..4c61339c29c28 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/head.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/head.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { head } from '../../functions/common/head'; -import { FunctionHelp } from '.'; +import { head } from '../../../canvas_plugin_src/functions/common/head'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.headHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/if.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/if.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/if.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/if.ts index e58f11ad1bccd..9cac3d10b2834 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/if.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/if.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { ifFn } from '../../functions/common/if'; -import { FunctionHelp } from '.'; +import { ifFn } from '../../../canvas_plugin_src/functions/common/if'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { BOOLEAN_TRUE, BOOLEAN_FALSE, CONTEXT } from '../../../i18n'; +import { BOOLEAN_TRUE, BOOLEAN_FALSE, CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.ifHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/image.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/image.ts similarity index 90% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/image.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/image.ts index e34fba940ae08..510f25e4a0d42 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/image.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/image.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { image, ImageMode } from '../../functions/common/image'; -import { FunctionHelp } from '.'; +import { image, ImageMode } from '../../../canvas_plugin_src/functions/common/image'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { URL, BASE64 } from '../../../i18n'; +import { URL, BASE64 } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.imageHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/join_rows.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/join_rows.ts similarity index 91% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/join_rows.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/join_rows.ts index 79937294bca16..59684f7cf1cd8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/join_rows.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/join_rows.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { joinRows } from '../../functions/common/join_rows'; -import { FunctionHelp } from '.'; +import { joinRows } from '../../../canvas_plugin_src/functions/common/join_rows'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/location.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/location.ts similarity index 86% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/location.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/location.ts index 8a70208c025af..7c0497da8361d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/location.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/location.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { location } from '../../functions/browser/location'; -import { FunctionHelp } from '.'; +import { location } from '../../../canvas_plugin_src/functions/browser/location'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lt.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/lt.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lt.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/lt.ts index e05b8a260d4f9..203ba7412d4d5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lt.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/lt.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { lt } from '../../functions/common/lt'; -import { FunctionHelp } from '.'; +import { lt } from '../../../canvas_plugin_src/functions/common/lt'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.ltHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lte.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/lte.ts similarity index 83% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lte.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/lte.ts index 659e91aa99c16..7db9d332e9659 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/lte.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/lte.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { lte } from '../../functions/common/lte'; -import { FunctionHelp } from '.'; +import { lte } from '../../../canvas_plugin_src/functions/common/lte'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.lteHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/mapColumn.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/map_column.ts similarity index 86% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/mapColumn.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/map_column.ts index c3330132bb4b4..e16a2ac67a54e 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/mapColumn.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/map_column.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { mapColumn } from '../../functions/common/mapColumn'; -import { FunctionHelp } from '.'; +import { mapColumn } from '../../../canvas_plugin_src/functions/common/mapColumn'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CANVAS, DATATABLE } from '../../../i18n'; +import { CANVAS, DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.mapColumnHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/markdown.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts similarity index 88% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/markdown.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts index 043c2b1dc135e..d5271e14436e2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/markdown.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/markdown.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { markdown } from '../../functions/browser/markdown'; -import { FunctionHelp } from '.'; +import { markdown } from '../../../canvas_plugin_src/functions/browser/markdown'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { MARKDOWN, CSS } from '../../../i18n'; +import { MARKDOWN, CSS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.markdownHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/math.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/math.ts similarity index 93% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/math.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/math.ts index 1da6ec0c73dce..752009fb9c320 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/math.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/math.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { math } from '../../functions/common/math'; -import { FunctionHelp } from '.'; +import { math } from '../../../canvas_plugin_src/functions/common/math'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE, CONTEXT, TINYMATH, TINYMATH_URL } from '../../../i18n'; +import { DATATABLE, CONTEXT, TINYMATH, TINYMATH_URL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.mathHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/metric.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/metric.ts similarity index 92% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/metric.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/metric.ts index b4f1e2448d31a..8fa98667212a5 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/metric.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/metric.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { metric } from '../../functions/common/metric'; -import { FunctionHelp } from '.'; +import { metric } from '../../../canvas_plugin_src/functions/common/metric'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../../../i18n'; +import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.metricHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/neq.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/neq.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/neq.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/neq.ts index de5ab3a2c301d..88095814c4062 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/neq.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/neq.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { neq } from '../../functions/common/neq'; -import { FunctionHelp } from '.'; +import { neq } from '../../../canvas_plugin_src/functions/common/neq'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.neqHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/palette.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/palette.ts similarity index 90% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/palette.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/palette.ts index 59627f9d28d90..ff252ec9c0ac8 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/palette.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/palette.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { palette } from '../../functions/common/palette'; -import { FunctionHelp } from '.'; +import { palette } from '../../../canvas_plugin_src/functions/common/palette'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pie.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/pie.ts similarity index 95% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pie.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/pie.ts index f8b0250a9687c..1a93cdee05749 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pie.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/pie.ts @@ -5,11 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { pie } from '../../functions/common/pie'; -import { FunctionHelp } from '.'; +import { pie } from '../../../canvas_plugin_src/functions/common/pie'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { Position } from '../../../types'; -import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../../i18n'; +import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.pieHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/plot.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/plot.ts similarity index 94% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/plot.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/plot.ts index 45a764ce32ef9..3d4624bd2495e 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/plot.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/plot.ts @@ -5,11 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { plot } from '../../functions/common/plot'; -import { FunctionHelp } from '.'; +import { plot } from '../../../canvas_plugin_src/functions/common/plot'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { Position } from '../../../types'; -import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../../i18n'; +import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.plotHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/ply.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/ply.ts similarity index 92% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/ply.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/ply.ts index f0a208f256fa8..f341965aaa8b2 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/ply.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/ply.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { ply } from '../../functions/common/ply'; -import { FunctionHelp } from '.'; +import { ply } from '../../../canvas_plugin_src/functions/common/ply'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.plyHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pointseries.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/pointseries.ts similarity index 91% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pointseries.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/pointseries.ts index 044c4c59eab01..1e7c67bb750e3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/pointseries.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/pointseries.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { pointseries } from '../../functions/server/pointseries'; -import { FunctionHelp } from '.'; +import { pointseries } from '../../../canvas_plugin_src/functions/server/pointseries'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE, TINYMATH, TINYMATH_URL } from '../../../i18n'; +import { DATATABLE, TINYMATH, TINYMATH_URL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.pointseriesHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/progress.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/progress.ts similarity index 92% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/progress.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/progress.ts index e300314629594..7e441df9bd246 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/progress.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/progress.ts @@ -5,12 +5,12 @@ */ import { i18n } from '@kbn/i18n'; -import { progress } from '../../functions/common/progress'; -import { FunctionHelp } from '.'; +import { progress } from '../../../canvas_plugin_src/functions/common/progress'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { Shape } from '../../functions/common/progress'; -import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../../../i18n'; +import { Shape } from '../../../canvas_plugin_src/functions/common/progress'; +import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_TRUE, BOOLEAN_FALSE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.progressHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/render.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/render.ts similarity index 88% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/render.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/render.ts index 1fea040292626..bf0a5a50b8726 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/render.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/render.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { render } from '../../functions/common/render'; -import { FunctionHelp } from '.'; +import { render } from '../../../canvas_plugin_src/functions/common/render'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT, CSS } from '../../../i18n'; +import { CONTEXT, CSS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.renderHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/repeatImage.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/repeat_image.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/repeatImage.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/repeat_image.ts index 3ba2f369225b2..aafaec11c4d1d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/repeatImage.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/repeat_image.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { repeatImage } from '../../functions/common/repeatImage'; -import { FunctionHelp } from '.'; +import { repeatImage } from '../../../canvas_plugin_src/functions/common/repeatImage'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT, BASE64, URL } from '../../../i18n'; +import { CONTEXT, BASE64, URL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.repeatImageHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/replace.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/replace.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/replace.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/replace.ts index 680379c233078..085f42b439c46 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/replace.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/replace.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { replace } from '../../functions/common/replace'; -import { FunctionHelp } from '.'; +import { replace } from '../../../canvas_plugin_src/functions/common/replace'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { JS } from '../../../i18n'; +import { JS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.replaceImageHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/revealImage.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/reveal_image.ts similarity index 91% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/revealImage.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/reveal_image.ts index 85464abd8c349..f210ec8c1d882 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/revealImage.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/reveal_image.ts @@ -5,11 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { revealImage } from '../../functions/common/revealImage'; -import { FunctionHelp } from '.'; +import { revealImage } from '../../../canvas_plugin_src/functions/common/revealImage'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; import { Position } from '../../../types'; -import { BASE64, URL } from '../../../i18n'; +import { BASE64, URL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.revealImageHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rounddate.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/rounddate.ts similarity index 85% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rounddate.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/rounddate.ts index 6fe1901ed9c8d..4805fe16a94f0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rounddate.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/rounddate.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { rounddate } from '../../functions/common/rounddate'; -import { FunctionHelp } from '.'; +import { rounddate } from '../../../canvas_plugin_src/functions/common/rounddate'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { MOMENTJS } from '../../../i18n'; +import { MOMENTJS } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.rounddateHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rowCount.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/row_count.ts similarity index 84% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rowCount.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/row_count.ts index 8957d9e97645d..5b0cecd47fd79 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/rowCount.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/row_count.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { rowCount } from '../../functions/common/rowCount'; -import { FunctionHelp } from '.'; +import { rowCount } from '../../../canvas_plugin_src/functions/common/rowCount'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_map.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_map.ts similarity index 82% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_map.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_map.ts index c63890002da46..d01b77e1cfd51 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_map.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_map.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { savedMap } from '../../functions/common/saved_map'; -import { FunctionHelp } from '.'; +import { savedMap } from '../../../canvas_plugin_src/functions/common/saved_map'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_search.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_search.ts similarity index 81% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_search.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_search.ts index fb213275be233..718deea5df788 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_search.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_search.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { savedSearch } from '../../functions/common/saved_search'; -import { FunctionHelp } from '.'; +import { savedSearch } from '../../../canvas_plugin_src/functions/common/saved_search'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_visualization.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_visualization.ts similarity index 80% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_visualization.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_visualization.ts index 9d782bbf69252..e3b412284442d 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/saved_visualization.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/saved_visualization.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { savedVisualization } from '../../functions/common/saved_visualization'; -import { FunctionHelp } from '.'; +import { savedVisualization } from '../../../canvas_plugin_src/functions/common/saved_visualization'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/seriesStyle.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/series_style.ts similarity index 93% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/seriesStyle.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/series_style.ts index 377bcedde122e..7b3855b528201 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/seriesStyle.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/series_style.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { seriesStyle } from '../../functions/common/seriesStyle'; -import { FunctionHelp } from '.'; +import { seriesStyle } from '../../../canvas_plugin_src/functions/common/seriesStyle'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/shape.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/shape.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/shape.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/shape.ts index 0e2081ffd6aaa..bcd6d90faa3f0 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/shape.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/shape.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { shape } from '../../functions/common/shape'; -import { FunctionHelp } from '.'; +import { shape } from '../../../canvas_plugin_src/functions/common/shape'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { SVG } from '../../../i18n'; +import { SVG } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.shapeHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/sort.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/sort.ts similarity index 86% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/sort.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/sort.ts index c377d4900279a..d539449253058 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/sort.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/sort.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { sort } from '../../functions/common/sort'; -import { FunctionHelp } from '.'; +import { sort } from '../../../canvas_plugin_src/functions/common/sort'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.sortHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/staticColumn.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/static_column.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/staticColumn.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/static_column.ts index f967b6520ecf7..82dbd9910ea3b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/staticColumn.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/static_column.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { staticColumn } from '../../functions/common/staticColumn'; -import { FunctionHelp } from '.'; +import { staticColumn } from '../../../canvas_plugin_src/functions/common/staticColumn'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/string.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/string.ts similarity index 85% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/string.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/string.ts index 7e87d0669f421..f4a304c07b12a 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/string.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/string.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { string } from '../../functions/common/string'; -import { FunctionHelp } from '.'; +import { string } from '../../../canvas_plugin_src/functions/common/string'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/switch.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/switch.ts similarity index 87% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/switch.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/switch.ts index 45a7fb3f372da..f65ff7c6fd240 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/switch.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/switch.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { switchFn } from '../../functions/common/switch'; -import { FunctionHelp } from '.'; +import { switchFn } from '../../../canvas_plugin_src/functions/common/switch'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CONTEXT } from '../../../i18n'; +import { CONTEXT } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.switchHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/table.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/table.ts similarity index 91% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/table.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/table.ts index 7472f2d0aa4a8..91a9ae7488234 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/table.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/table.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { table } from '../../functions/common/table'; -import { FunctionHelp } from '.'; +import { table } from '../../../canvas_plugin_src/functions/common/table'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../../i18n'; +import { CSS, FONT_FAMILY, FONT_WEIGHT, BOOLEAN_FALSE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.tableHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/tail.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/tail.ts similarity index 83% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/tail.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/tail.ts index 4e84b13dbf086..5c3bbe24c8e4b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/tail.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/tail.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { tail } from '../../functions/common/tail'; -import { FunctionHelp } from '.'; +import { tail } from '../../../canvas_plugin_src/functions/common/tail'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DATATABLE } from '../../../i18n'; +import { DATATABLE } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.tailHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilter.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter.ts similarity index 89% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilter.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter.ts index c202873457022..aedcdc9441885 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilter.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { timefilter } from '../../functions/common/timefilter'; -import { FunctionHelp } from '.'; +import { timefilter } from '../../../canvas_plugin_src/functions/common/timefilter'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { ISO8601, ELASTICSEARCH, DATEMATH } from '../../../i18n'; +import { ISO8601, ELASTICSEARCH, DATEMATH } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.timefilterHelpText', { diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilterControl.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter_control.ts similarity index 88% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilterControl.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter_control.ts index 4d524c589550a..099c3a4f26c8b 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/timefilterControl.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timefilter_control.ts @@ -5,8 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { timefilterControl } from '../../functions/common/timefilterControl'; -import { FunctionHelp } from '.'; +import { timefilterControl } from '../../../canvas_plugin_src/functions/common/timefilterControl'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; export const help: FunctionHelp> = { diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/timelion.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timelion.ts new file mode 100644 index 0000000000000..e105f7e678af3 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/timelion.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 { i18n } from '@kbn/i18n'; +import { timelion } from '../../../public/functions/timelion'; +import { FunctionHelp } from '../function_help'; +import { FunctionFactory } from '../../../types'; +import { ELASTICSEARCH, DATEMATH, MOMENTJS_TIMEZONE_URL } from '../../constants'; + +export const help: FunctionHelp> = { + help: i18n.translate('xpack.canvas.functions.timelionHelpText', { + defaultMessage: 'Use Timelion to extract one or more timeseries from many sources.', + }), + args: { + query: i18n.translate('xpack.canvas.functions.timelion.args.query', { + defaultMessage: 'A Timelion query', + }), + interval: i18n.translate('xpack.canvas.functions.timelion.args.interval', { + defaultMessage: 'The bucket interval for the time series.', + }), + from: i18n.translate('xpack.canvas.functions.timelion.args.from', { + defaultMessage: 'The {ELASTICSEARCH} {DATEMATH} string for the beginning of the time range.', + values: { + ELASTICSEARCH, + DATEMATH, + }, + }), + to: i18n.translate('xpack.canvas.functions.timelion.args.to', { + defaultMessage: 'The {ELASTICSEARCH} {DATEMATH} string for the end of the time range.', + values: { + ELASTICSEARCH, + DATEMATH, + }, + }), + timezone: i18n.translate('xpack.canvas.functions.timelion.args.timezone', { + defaultMessage: 'The timezone for the time range. See {MOMENTJS_TIMEZONE_URL}.', + values: { + MOMENTJS_TIMEZONE_URL, + }, + }), + }, +}; diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/to.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/to.ts new file mode 100644 index 0000000000000..2cf812cfb13db --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/to.ts @@ -0,0 +1,34 @@ +/* + * 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'; +import { to } from '../../../public/functions/to'; +import { FunctionHelp } from '../function_help'; +import { FunctionFactory } from '../../../types'; +import { CONTEXT } from '../../constants'; + +export const help: FunctionHelp> = { + help: i18n.translate('xpack.canvas.functions.toHelpText', { + defaultMessage: 'Explicitly casts the type of the {CONTEXT} to the specified type.', + values: { + CONTEXT, + }, + }), + args: { + type: i18n.translate('xpack.canvas.functions.to.args.type', { + defaultMessage: 'A known data type in the expression language.', + }), + }, +}; + +export const errors = { + missingType: () => + new Error( + i18n.translate('xpack.canvas.functions.to.missingType', { + defaultMessage: 'Must specify a casting type', + }) + ), +}; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/urlparam.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/urlparam.ts similarity index 88% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/urlparam.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/dict/urlparam.ts index 918527320868a..b8c044f521029 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/urlparam.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/urlparam.ts @@ -5,10 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { urlparam } from '../../functions/browser/urlparam'; -import { FunctionHelp } from '.'; +import { urlparam } from '../../../canvas_plugin_src/functions/browser/urlparam'; +import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { TYPE_STRING, URL } from '../../../i18n'; +import { TYPE_STRING, URL } from '../../constants'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.urlparamHelpText', { diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/function_errors.ts b/x-pack/legacy/plugins/canvas/i18n/functions/function_errors.ts new file mode 100644 index 0000000000000..5b11a7d407345 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/function_errors.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 { errors as alterColumn } from './dict/alter_column'; +import { errors as asset } from './dict/asset'; +import { errors as axisConfig } from './dict/axis_config'; +import { errors as compare } from './dict/compare'; +import { errors as containerStyle } from './dict/container_style'; +import { errors as csv } from './dict/csv'; +import { errors as date } from './dict/date'; +import { errors as demodata } from './dict/demodata'; +import { errors as getCell } from './dict/get_cell'; +import { errors as image } from './dict/image'; +import { errors as joinRows } from './dict/join_rows'; +import { errors as math } from './dict/math'; +import { errors as ply } from './dict/ply'; +import { errors as pointseries } from './dict/pointseries'; +import { errors as progress } from './dict/progress'; +import { errors as revealImage } from './dict/reveal_image'; +import { errors as timefilter } from './dict/timefilter'; +import { errors as to } from './dict/to'; + +export const getFunctionErrors = () => ({ + alterColumn, + asset, + axisConfig, + compare, + containerStyle, + csv, + date, + demodata, + getCell, + image, + joinRows, + math, + ply, + pointseries, + progress, + revealImage, + timefilter, + to, +}); diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts b/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts new file mode 100644 index 0000000000000..de0d62b3d4453 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts @@ -0,0 +1,236 @@ +/* + * 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 { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; +import { CanvasFunction } from '../../types'; +import { UnionToIntersection } from '../../types'; + +import { help as all } from './dict/all'; +import { help as alterColumn } from './dict/alter_column'; +import { help as any } from './dict/any'; +import { help as asFn } from './dict/as'; +import { help as asset } from './dict/asset'; +import { help as axisConfig } from './dict/axis_config'; +import { help as caseFn } from './dict/case'; +import { help as clear } from './dict/clear'; +import { help as columns } from './dict/columns'; +import { help as compare } from './dict/compare'; +import { help as containerStyle } from './dict/container_style'; +import { help as context } from './dict/context'; +import { help as csv } from './dict/csv'; +import { help as date } from './dict/date'; +import { help as demodata } from './dict/demodata'; +import { help as doFn } from './dict/do'; +import { help as dropdownControl } from './dict/dropdown_control'; +import { help as eq } from './dict/eq'; +import { help as escount } from './dict/escount'; +import { help as esdocs } from './dict/esdocs'; +import { help as essql } from './dict/essql'; +import { help as exactly } from './dict/exactly'; +import { help as filterrows } from './dict/filterrows'; +import { help as filters } from './dict/filters'; +import { help as formatdate } from './dict/formatdate'; +import { help as formatnumber } from './dict/formatnumber'; +import { help as getCell } from './dict/get_cell'; +import { help as gt } from './dict/gt'; +import { help as gte } from './dict/gte'; +import { help as head } from './dict/head'; +import { help as ifFn } from './dict/if'; +import { help as image } from './dict/image'; +import { help as joinRows } from './dict/join_rows'; +import { help as location } from './dict/location'; +import { help as lt } from './dict/lt'; +import { help as lte } from './dict/lte'; +import { help as mapColumn } from './dict/map_column'; +import { help as markdown } from './dict/markdown'; +import { help as math } from './dict/math'; +import { help as metric } from './dict/metric'; +import { help as neq } from './dict/neq'; +import { help as palette } from './dict/palette'; +import { help as pie } from './dict/pie'; +import { help as plot } from './dict/plot'; +import { help as ply } from './dict/ply'; +import { help as pointseries } from './dict/pointseries'; +import { help as progress } from './dict/progress'; +import { help as render } from './dict/render'; +import { help as repeatImage } from './dict/repeat_image'; +import { help as replace } from './dict/replace'; +import { help as revealImage } from './dict/reveal_image'; +import { help as rounddate } from './dict/rounddate'; +import { help as rowCount } from './dict/row_count'; +import { help as savedMap } from './dict/saved_map'; +import { help as savedSearch } from './dict/saved_search'; +import { help as savedVisualization } from './dict/saved_visualization'; +import { help as seriesStyle } from './dict/series_style'; +import { help as shape } from './dict/shape'; +import { help as sort } from './dict/sort'; +import { help as staticColumn } from './dict/static_column'; +import { help as string } from './dict/string'; +import { help as switchFn } from './dict/switch'; +import { help as table } from './dict/table'; +import { help as tail } from './dict/tail'; +import { help as timefilter } from './dict/timefilter'; +import { help as timefilterControl } from './dict/timefilter_control'; +import { help as timelion } from './dict/timelion'; +import { help as to } from './dict/to'; +import { help as urlparam } from './dict/urlparam'; + +/** + * This type defines an entry in the `FunctionHelpMap`. It uses + * an `ExpressionFunction` to infer its `Arguments` in order to strongly-type that + * entry. + * + * For example: + * +``` + interface Arguments { + bar: string; + baz: number; + } + + function foo(): ExpressionFunction<'foo', Context, Arguments, Return> { + // ... + } + + const help: FunctionHelp = { + help: 'Some help for foo', + args: { + bar: 'Help for bar.', // pass; error if missing + baz: 'Help for baz.', // pass; error if missing + zap: 'Help for zap.`, // error: zap doesn't exist + } + }; +``` + * This allows one to ensure each argument is present, and no extraneous arguments + * remain. + */ +export type FunctionHelp = T extends ExpressionFunction< + infer Name, + infer Context, + infer Arguments, + infer Return +> + ? { + help: string; + args: { [key in keyof Arguments]: string }; + } + : never; + +// This internal type infers a Function name and uses `FunctionHelp` above to build +// a dictionary entry. This can be used to ensure every Function is defined and all +// Arguments have help strings. +// +// For example: +// +// function foo(): ExpressionFunction<'foo', Context, Arguments, Return> { +// // ... +// } +// +// const map: FunctionHelpMap = { +// foo: FunctionHelp, +// } +// +// Given a collection of functions, the map would contain each entry. +// +type FunctionHelpMap = T extends ExpressionFunction< + infer Name, + infer Context, + infer Arguments, + infer Return +> + ? { [key in Name]: FunctionHelp } + : never; + +// This internal type represents an exhaustive dictionary of `FunctionHelp` types, +// organized by Function Name and then Function Argument. +// +// This type indexes the existing function factories, reverses the union to an +// intersection, and produces the dictionary of strings. +type FunctionHelpDict = UnionToIntersection>; + +/** + * Help text for Canvas Functions should be properly localized. This function will + * return a dictionary of help strings, organized by `CanvasFunction` specification + * and then by available arguments within each `CanvasFunction`. + * + * This a function, rather than an object, to future-proof string initialization, + * if ever necessary. + */ +export const getFunctionHelp = (): FunctionHelpDict => ({ + all, + alterColumn, + any, + as: asFn, + asset, + axisConfig, + case: caseFn, + clear, + columns, + compare, + containerStyle, + context, + csv, + date, + demodata, + do: doFn, + dropdownControl, + eq, + escount, + esdocs, + essql, + exactly, + filterrows, + filters, + formatdate, + formatnumber, + getCell, + gt, + gte, + head, + if: ifFn, + joinRows, + image, + location, + lt, + lte, + mapColumn, + markdown, + math, + metric, + neq, + palette, + pie, + plot, + ply, + pointseries, + progress, + render, + repeatImage, + replace, + revealImage, + rounddate, + rowCount, + // TODO: elastic/kibana#44822 Disabling pending filters work + // @ts-ignore + savedMap, + // @ts-ignore + savedSearch, + // @ts-ignore + savedVisualization, + seriesStyle, + shape, + sort, + staticColumn, + string, + switch: switchFn, + table, + tail, + timefilter, + timefilterControl, + timelion, + to, + urlparam, +}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/index.ts b/x-pack/legacy/plugins/canvas/i18n/functions/index.ts similarity index 100% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/index.ts rename to x-pack/legacy/plugins/canvas/i18n/functions/index.ts diff --git a/x-pack/legacy/plugins/canvas/i18n/index.ts b/x-pack/legacy/plugins/canvas/i18n/index.ts index e6c3034fedfd9..9e5ed624ccfd3 100644 --- a/x-pack/legacy/plugins/canvas/i18n/index.ts +++ b/x-pack/legacy/plugins/canvas/i18n/index.ts @@ -11,9 +11,12 @@ export * from './components'; export * from './constants'; export * from './errors'; export * from './expression_types'; +export * from './elements'; +export * from './functions'; export * from './renderers'; export * from './shortcuts'; export * from './transitions'; +export * from './ui'; export * from './units'; export const getAppDescription = () => diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/ui.ts b/x-pack/legacy/plugins/canvas/i18n/ui.ts similarity index 99% rename from x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/ui.ts rename to x-pack/legacy/plugins/canvas/i18n/ui.ts index 43f204c766bf8..b65a666aa8809 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/ui.ts +++ b/x-pack/legacy/plugins/canvas/i18n/ui.ts @@ -23,7 +23,7 @@ import { SQL, TIMELION, URL, -} from '../../i18n'; +} from './constants'; export const ArgumentStrings = { AxisConfig: { diff --git a/x-pack/legacy/plugins/canvas/public/expression_types/datasources/esdocs.js b/x-pack/legacy/plugins/canvas/public/expression_types/datasources/esdocs.js index 57ae8c28835ee..7bb3c6101ff20 100644 --- a/x-pack/legacy/plugins/canvas/public/expression_types/datasources/esdocs.js +++ b/x-pack/legacy/plugins/canvas/public/expression_types/datasources/esdocs.js @@ -12,9 +12,9 @@ import { ESFieldsSelect } from '../../components/es_fields_select'; import { ESFieldSelect } from '../../components/es_field_select'; import { ESIndexSelect } from '../../components/es_index_select'; import { templateFromReactComponent } from '../../lib/template_from_react_component'; -import { DataSourceStrings } from '../../../i18n'; +import { ExpressionDataSourceStrings } from '../../../i18n'; -const { ESDocs: strings } = DataSourceStrings; +const { ESDocs: strings } = ExpressionDataSourceStrings; const EsdocsDatasource = ({ args, updateArgs, defaultIndex }) => { const setArg = (name, value) => { diff --git a/x-pack/legacy/plugins/canvas/public/functions/asset.js b/x-pack/legacy/plugins/canvas/public/functions/asset.js deleted file mode 100644 index 49162b16e0c8b..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/functions/asset.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getState } from '../state/store'; -import { getAssetById } from '../state/selectors/assets'; - -export const asset = () => ({ - name: 'asset', - aliases: [], - type: 'string', - help: 'Retrieves Canvas workpad asset objects to provide as argument values. Usually images.', - context: { - types: ['null'], - }, - args: { - id: { - aliases: ['_'], - types: ['string'], - help: 'The ID of the asset to retrieve.', - required: true, - }, - }, - fn: (_context, args) => { - const assetId = args.id; - const asset = getAssetById(getState(), assetId); - if (asset !== undefined) { - return asset.value; - } - - throw new Error('Could not get the asset by ID: ' + assetId); - }, -}); diff --git a/x-pack/legacy/plugins/canvas/public/functions/asset.ts b/x-pack/legacy/plugins/canvas/public/functions/asset.ts new file mode 100644 index 0000000000000..e1eb652ca0324 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/functions/asset.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 { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; +// @ts-ignore unconverted local lib +import { getState } from '../state/store'; +import { getAssetById } from '../state/selectors/assets'; +import { getFunctionHelp, getFunctionErrors } from '../../i18n'; + +interface Arguments { + id: string; +} + +export function asset(): ExpressionFunction<'asset', null, Arguments, string> { + const { help, args: argHelp } = getFunctionHelp().asset; + const errors = getFunctionErrors().asset; + + return { + name: 'asset', + aliases: [], + type: 'string', + help, + context: { + types: ['null'], + }, + args: { + id: { + aliases: ['_'], + types: ['string'], + help: argHelp.id, + required: true, + }, + }, + fn: (_context, args) => { + const assetId = args.id; + const storedAsset = getAssetById(getState(), assetId); + if (storedAsset !== undefined) { + return storedAsset.value; + } + + throw errors.invalidAssetId(assetId); + }, + }; +} diff --git a/x-pack/legacy/plugins/canvas/public/functions/filters.js b/x-pack/legacy/plugins/canvas/public/functions/filters.js deleted file mode 100644 index 2e84be61a9667..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/functions/filters.js +++ /dev/null @@ -1,68 +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 { fromExpression } from '@kbn/interpreter/common'; -import { get } from 'lodash'; -import { interpretAst } from 'plugins/interpreter/interpreter'; -import { registries } from 'plugins/interpreter/registries'; -import { getState } from '../state/store'; -import { getGlobalFilters } from '../state/selectors/workpad'; - -function getFiltersByGroup(filters, groups = [], ungrouped = false) { - if (!groups || groups.length === 0) { - if (!ungrouped) { - return filters; - } - - // remove all filters that belong to a group - return filters.filter(filter => { - const ast = fromExpression(filter); - const expGroups = get(ast, 'chain[0].arguments.filterGroup', []); - return expGroups.length === 0; - }); - } - - return filters.filter(filter => { - const ast = fromExpression(filter); - const expGroups = get(ast, 'chain[0].arguments.filterGroup', []); - return expGroups.length > 0 && expGroups.every(expGroup => groups.includes(expGroup)); - }); -} - -export const filters = () => ({ - name: 'filters', - type: 'filter', - help: 'Aggregates element filters from the workpad for use elsewhere, usually a data source.', - context: { - types: ['null'], - }, - args: { - group: { - aliases: ['_'], - types: ['string'], - help: 'The name of the filter group to use.', - multi: true, - }, - ungrouped: { - aliases: ['nogroup', 'nogroups'], - types: ['boolean'], - help: 'Exclude filters that belong to a filter group?', - default: false, - }, - }, - fn: (_, { group, ungrouped }) => { - const filterList = getFiltersByGroup(getGlobalFilters(getState()), group, ungrouped); - - if (filterList && filterList.length) { - const filterExpression = filterList.join(' | '); - const filterAST = fromExpression(filterExpression); - return interpretAst(filterAST); - } else { - const filterType = registries.types.get('filter'); - return filterType.from(null); - } - }, -}); diff --git a/x-pack/legacy/plugins/canvas/public/functions/filters.ts b/x-pack/legacy/plugins/canvas/public/functions/filters.ts new file mode 100644 index 0000000000000..031e98ab80c5b --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/functions/filters.ts @@ -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 { fromExpression } from '@kbn/interpreter/common'; +import { get } from 'lodash'; +// @ts-ignore untyped Elastic lib +import { interpretAst } from 'plugins/interpreter/interpreter'; +// @ts-ignore untyped Elastic lib +import { registries } from 'plugins/interpreter/registries'; +import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; +// @ts-ignore untyped local +import { getState } from '../state/store'; +import { getGlobalFilters } from '../state/selectors/workpad'; +import { Filter } from '../../types'; +import { getFunctionHelp } from '../../i18n'; + +interface Arguments { + group: string[]; + ungrouped: boolean; +} + +function getFiltersByGroup(allFilters: string[], groups?: string[], ungrouped = false): string[] { + if (!groups || groups.length === 0) { + if (!ungrouped) { + return allFilters; + } + + // remove all allFilters that belong to a group + return allFilters.filter((filter: string) => { + const ast = fromExpression(filter); + const expGroups = get(ast, 'chain[0].arguments.filterGroup', []); + return expGroups.length === 0; + }); + } + + return allFilters.filter((filter: string) => { + const ast = fromExpression(filter); + const expGroups = get(ast, 'chain[0].arguments.filterGroup', []); + return expGroups.length > 0 && expGroups.every(expGroup => groups.includes(expGroup)); + }); +} + +export function filters(): ExpressionFunction<'filters', null, Arguments, Filter> { + const { help, args: argHelp } = getFunctionHelp().filters; + + return { + name: 'filters', + type: 'filter', + help, + context: { + types: ['null'], + }, + args: { + group: { + aliases: ['_'], + types: ['string'], + help: argHelp.group, + multi: true, + }, + ungrouped: { + aliases: ['nogroup', 'nogroups'], + types: ['boolean'], + help: argHelp.ungrouped, + default: false, + }, + }, + fn: (_context, { group, ungrouped }) => { + const filterList = getFiltersByGroup(getGlobalFilters(getState()), group, ungrouped); + + if (filterList && filterList.length) { + const filterExpression = filterList.join(' | '); + const filterAST = fromExpression(filterExpression); + return interpretAst(filterAST); + } else { + const filterType = registries.types.get('filter'); + return filterType.from(null); + } + }, + }; +} diff --git a/x-pack/legacy/plugins/canvas/public/functions/index.js b/x-pack/legacy/plugins/canvas/public/functions/index.ts similarity index 100% rename from x-pack/legacy/plugins/canvas/public/functions/index.js rename to x-pack/legacy/plugins/canvas/public/functions/index.ts diff --git a/x-pack/legacy/plugins/canvas/public/functions/timelion.js b/x-pack/legacy/plugins/canvas/public/functions/timelion.js deleted file mode 100644 index 1c8e46723edac..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/functions/timelion.js +++ /dev/null @@ -1,99 +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 { flatten } from 'lodash'; -import chrome from 'ui/chrome'; -import { fetch } from '../../common/lib/fetch'; -import { buildBoolArray } from '../../server/lib/build_bool_array'; - -export const timelion = () => ({ - name: 'timelion', - type: 'datatable', - help: 'Use Timelion to extract one or more timeseries from many sources.', - context: { - types: ['filter'], - }, - args: { - query: { - types: ['string'], - aliases: ['_', 'q'], - help: 'A Timelion query', - default: '".es(*)"', - }, - interval: { - types: ['string'], - help: 'The bucket interval for the time series.', - default: 'auto', - }, - from: { - type: ['string'], - help: 'The Elasticsearch `datemath` string for the beginning of the time range.', - default: 'now-1y', - }, - to: { - type: ['string'], - help: 'The Elasticsearch `datemath` string for the end of the time range.', - default: 'now', - }, - timezone: { - type: ['string'], - help: - 'The timezone for the time range. See [Moment Timezone](https://momentjs.com/timezone/).', - default: 'UTC', - }, - }, - fn: (context, args) => { - // 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 = context.and.find(and => and.type === 'time'); - const range = timeFilter - ? { from: timeFilter.from, to: timeFilter.to } - : { from: args.from, to: args.to }; - - const body = { - extended: { - es: { - filter: { - bool: { - must: buildBoolArray(context.and), - }, - }, - }, - }, - sheet: [args.query], - time: { - from: range.from, - to: range.to, - interval: args.interval, - timezone: args.timezone, - }, - }; - - return fetch(chrome.addBasePath(`/api/timelion/run`), { - method: 'POST', - responseType: 'json', - data: body, - }).then(resp => { - const seriesList = resp.data.sheet[0].list; - - const rows = flatten( - seriesList.map(series => - series.data.map(row => ({ '@timestamp': row[0], value: row[1], label: series.label })) - ) - ); - - return { - type: 'datatable', - columns: [ - { name: '@timestamp', type: 'date' }, - { name: 'value', type: 'number' }, - { name: 'label', type: 'string' }, - ], - rows: rows, - }; - }); - }, -}); diff --git a/x-pack/legacy/plugins/canvas/public/functions/timelion.ts b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts new file mode 100644 index 0000000000000..f998777fdfa30 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/functions/timelion.ts @@ -0,0 +1,113 @@ +/* + * 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 { flatten } from 'lodash'; +import chrome from 'ui/chrome'; +import { ExpressionFunction, DatatableRow } from 'src/legacy/core_plugins/interpreter/public'; +import { fetch } from '../../common/lib/fetch'; +// @ts-ignore untyped local +import { buildBoolArray } from '../../server/lib/build_bool_array'; +import { Datatable, Filter } from '../../types'; +import { getFunctionHelp } from '../../i18n'; + +interface Arguments { + query: string; + interval: string; + from: string; + to: string; + timezone: string; +} + +export function timelion(): ExpressionFunction<'timelion', Filter, Arguments, Promise> { + const { help, args: argHelp } = getFunctionHelp().timelion; + + return { + name: 'timelion', + type: 'datatable', + help, + context: { + types: ['filter'], + }, + args: { + query: { + types: ['string'], + aliases: ['_', 'q'], + help: argHelp.query, + default: '".es(*)"', + }, + interval: { + types: ['string'], + help: argHelp.interval, + default: 'auto', + }, + from: { + types: ['string'], + help: argHelp.from, + default: 'now-1y', + }, + to: { + types: ['string'], + help: argHelp.to, + default: 'now', + }, + timezone: { + types: ['string'], + help: argHelp.timezone, + default: 'UTC', + }, + }, + fn: (context, 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 = context.and.find(and => and.type === 'time'); + const range = timeFilter + ? { from: timeFilter.from, to: timeFilter.to } + : { from: args.from, to: args.to }; + + const body = { + extended: { + es: { + filter: { + bool: { + must: buildBoolArray(context.and), + }, + }, + }, + }, + sheet: [args.query], + time: { + from: range.from, + to: range.to, + interval: args.interval, + timezone: args.timezone, + }, + }; + + return fetch(chrome.addBasePath(`/api/timelion/run`), { + method: 'POST', + responseType: 'json', + data: body, + }).then(resp => { + const seriesList = resp.data.sheet[0].list; + const rows = flatten( + seriesList.map((series: { data: any[]; label: string }) => + series.data.map(row => ({ '@timestamp': row[0], value: row[1], label: series.label })) + ) + ) as DatatableRow[]; + + return { + type: 'datatable', + columns: [ + { name: '@timestamp', type: 'date' }, + { name: 'value', type: 'number' }, + { name: 'label', type: 'string' }, + ], + rows, + }; + }); + }, + }; +} diff --git a/x-pack/legacy/plugins/canvas/public/functions/to.js b/x-pack/legacy/plugins/canvas/public/functions/to.js deleted file mode 100644 index feb72969a39dd..0000000000000 --- a/x-pack/legacy/plugins/canvas/public/functions/to.js +++ /dev/null @@ -1,30 +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 { castProvider } from '@kbn/interpreter/common'; -import { registries } from 'plugins/interpreter/registries'; - -export const to = () => ({ - name: 'to', - aliases: [], - help: 'Explicitly casts the type of the _context_ to the specified type.', - context: {}, - args: { - type: { - types: ['string'], - help: 'A known type', - aliases: ['_'], - multi: true, - }, - }, - fn: (context, args) => { - if (!args.type) { - throw new Error('Must specify a casting type'); - } - - return castProvider(registries.types.toJS())(context, args.type); - }, -}); diff --git a/x-pack/legacy/plugins/canvas/public/functions/to.ts b/x-pack/legacy/plugins/canvas/public/functions/to.ts new file mode 100644 index 0000000000000..e647b24430324 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/functions/to.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// @ts-ignore untyped Elastic library +import { castProvider } from '@kbn/interpreter/common'; +import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; +// @ts-ignore untyped Elastic library +import { registries } from 'plugins/interpreter/registries'; +import { getFunctionHelp, getFunctionErrors } from '../../i18n'; + +interface Arguments { + type: string[]; +} + +export function to(): ExpressionFunction<'to', any, Arguments, any> { + const { help, args: argHelp } = getFunctionHelp().to; + const errors = getFunctionErrors().to; + + return { + name: 'to', + aliases: [], + help, + args: { + type: { + types: ['string'], + help: argHelp.type, + aliases: ['_'], + multi: true, + }, + }, + fn: (context, args) => { + if (!args.type) { + throw errors.missingType(); + } + + return castProvider(registries.types.toJS())(context, args.type); + }, + }; +} diff --git a/x-pack/legacy/plugins/canvas/public/state/selectors/assets.ts b/x-pack/legacy/plugins/canvas/public/state/selectors/assets.ts index a0c733e0d8207..c4f5b4672787a 100644 --- a/x-pack/legacy/plugins/canvas/public/state/selectors/assets.ts +++ b/x-pack/legacy/plugins/canvas/public/state/selectors/assets.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { State } from '../../../types'; +import { State, AssetType } from '../../../types'; const assetRoot = 'assets'; @@ -17,6 +17,6 @@ export function getAssetIds(state: State): Array { return Object.keys(getAssets(state)); } -export function getAssetById(state: State, id: string) { +export function getAssetById(state: State, id: string): AssetType | undefined { return state[assetRoot][id]; } diff --git a/x-pack/legacy/plugins/canvas/types/functions.ts b/x-pack/legacy/plugins/canvas/types/functions.ts index 6ebd521fc2301..a147622ae583a 100644 --- a/x-pack/legacy/plugins/canvas/types/functions.ts +++ b/x-pack/legacy/plugins/canvas/types/functions.ts @@ -8,6 +8,7 @@ import { ExpressionFunction } from 'src/legacy/core_plugins/interpreter/public'; import { functions as commonFunctions } from '../canvas_plugin_src/functions/common'; import { functions as browserFunctions } from '../canvas_plugin_src/functions/browser'; import { functions as serverFunctions } from '../canvas_plugin_src/functions/server'; +import { clientFunctions } from '../public/functions'; /** * Utility type for converting a union of types into an intersection. @@ -102,17 +103,15 @@ export type FunctionFactory = ExpressionFunction : never; -// A type containing all of the raw Function definitions in Canvas. -// prettier-ignore -type Functions = - typeof commonFunctions[number] & - typeof serverFunctions[number] & - typeof browserFunctions[number]; +type CommonFunction = FunctionFactory; +type BrowserFunction = FunctionFactory; +type ServerFunction = FunctionFactory; +type ClientFunctions = FunctionFactory; /** - * A union type of all Canvas Functions. + * A collection of all Canvas Functions. */ -export type CanvasFunction = FunctionFactory; +export type CanvasFunction = CommonFunction | BrowserFunction | ServerFunction | ClientFunctions; /** * A union type of all Canvas Function names. diff --git a/x-pack/legacy/plugins/code/public/components/code_block/code_block.tsx b/x-pack/legacy/plugins/code/public/components/code_block/code_block.tsx index 24524e9188105..394d2dfb97b0e 100644 --- a/x-pack/legacy/plugins/code/public/components/code_block/code_block.tsx +++ b/x-pack/legacy/plugins/code/public/components/code_block/code_block.tsx @@ -17,14 +17,19 @@ export interface Position { } export interface Props { - content: string; + lines: string[]; language: string; + /** + * Returns whether to highlight the given line. + * @param lineIndex The index of the line (0-based) + */ + highlightLine: (lineIndex: number) => boolean; highlightRanges: IRange[]; onClick: (event: Position) => void; folding: boolean; /** * Returns the line number to display for a given line. - * @param lineIndex The index of the given line (0-indexed) + * @param lineIndex The index of the line (0-based) */ lineNumber: (lineIndex: number) => string; } @@ -32,6 +37,7 @@ export interface Props { export class CodeBlock extends React.PureComponent { static defaultProps = { folding: false, + highlightLine: () => {}, highlightRanges: [], language: 'text', lineNumber: String, @@ -41,13 +47,13 @@ export class CodeBlock extends React.PureComponent { private el = createRef(); private ed?: editor.IStandaloneCodeEditor; private resizeChecker?: ResizeChecker; - private currentHighlightDecorations: string[] = []; + private currentDecorations: string[] = []; public async componentDidMount() { - const { content, highlightRanges, language, onClick } = this.props; + const { language, onClick } = this.props; if (this.el.current) { - await this.tryLoadFile(content, language); + await this.tryLoadFile(this.text, language); this.ed!.onMouseDown((e: editor.IEditorMouseEvent) => { if ( onClick && @@ -65,17 +71,8 @@ export class CodeBlock extends React.PureComponent { }); registerEditor(this.ed!); - if (highlightRanges.length) { - const decorations = highlightRanges.map((range: IRange) => { - return { - range, - options: { - inlineClassName: 'codeSearch__highlight', - }, - }; - }); - this.currentHighlightDecorations = this.ed!.deltaDecorations([], decorations); - } + this.setDecorations(); + this.resizeChecker = new ResizeChecker(this.el.current!); this.resizeChecker.on('resize', () => { setTimeout(() => { @@ -85,18 +82,18 @@ export class CodeBlock extends React.PureComponent { } } - private async tryLoadFile(code: string, language: string) { + private async tryLoadFile(text: string, language: string) { try { - await monaco.editor.colorize(code, language, {}); - this.loadFile(code, language); + await monaco.editor.colorize(text, language, {}); + this.loadFile(text, language); } catch (e) { - this.loadFile(code); + this.loadFile(text); } } - private loadFile(code: string, language: string = 'text') { + private loadFile(text: string, language: string = 'text') { this.ed = monaco.editor.create(this.el.current!, { - value: code, + value: text, language, lineNumbers: this.lineNumber, readOnly: true, @@ -122,28 +119,15 @@ export class CodeBlock extends React.PureComponent { } public componentDidUpdate(prevProps: Readonly) { - const { content, highlightRanges } = this.props; + const { highlightRanges } = this.props; + const prevText = prevProps.lines.join('\n'); - if (prevProps.content !== content || prevProps.highlightRanges !== highlightRanges) { + if (prevText !== this.text || prevProps.highlightRanges !== highlightRanges) { if (this.ed) { const model = this.ed.getModel(); if (model) { - model.setValue(content); - - if (highlightRanges.length) { - const decorations = highlightRanges!.map((range: IRange) => { - return { - range, - options: { - inlineClassName: 'codeSearch__highlight', - }, - }; - }); - this.currentHighlightDecorations = this.ed.deltaDecorations( - this.currentHighlightDecorations, - decorations - ); - } + model.setValue(this.text); + this.setDecorations(); } } } @@ -156,14 +140,45 @@ export class CodeBlock extends React.PureComponent { } public render() { - const height = this.lines.length * 18; + const height = this.props.lines.length * 18; return
; } private lineNumber = (lineIndex: number) => this.props.lineNumber(lineIndex - 1); - private get lines(): string[] { - return this.props.content.split('\n'); + private get text(): string { + return this.props.lines.join('\n'); + } + + private setDecorations() { + const decorations = this.decorations; + if (decorations.length) { + this.currentDecorations = this.ed!.deltaDecorations(this.currentDecorations, decorations); + } + } + + private get decorations(): editor.IModelDeltaDecoration[] { + const { lines, highlightRanges, highlightLine } = this.props; + + const rangeHighlights = highlightRanges.map(range => ({ + range, + options: { + inlineClassName: 'codeSearch__highlight', + }, + })); + + const lineHighlights = lines + .map((line, lineIndex) => ({ + range: new monaco.Range(lineIndex + 1, 0, lineIndex + 1, 0), + options: { + isWholeLine: true, + className: 'codeBlock__line--highlighted', + linesDecorationsClassName: 'codeBlock__line--highlighted', + }, + })) + .filter((decorations, lineIndex) => highlightLine(lineIndex)); + + return [...rangeHighlights, ...lineHighlights]; } } diff --git a/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx b/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx index bdcefe8802d54..6294c0973a33c 100644 --- a/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx +++ b/x-pack/legacy/plugins/code/public/components/editor/references_panel.tsx @@ -127,7 +127,7 @@ export class ReferencesPanel extends React.Component { className="referencesPanel__code-block" key={key} header={header} - content={file.code} + lines={file.code.split('\n')} language={file.language} lineNumber={i => file.lineNumbers[i]} highlightRanges={file.highlights} diff --git a/x-pack/legacy/plugins/code/public/components/integrations/data.ts b/x-pack/legacy/plugins/code/public/components/integrations/data.ts index 83137dff158ee..5af3cd3839a28 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/data.ts +++ b/x-pack/legacy/plugins/code/public/components/integrations/data.ts @@ -17,119 +17,112 @@ export interface Snippet { export type Results = Record; export const results: Results = { - 'ringside.ts#L18': { - uri: 'github.com/rylnd/ringside', - filePath: 'src/ringside.ts', + 'user.ts#L15': { + uri: 'github.com/Microsoft/Typescript-Node-Starter', + filePath: 'src/controllers/user.ts', language: 'typescript', compositeContent: { content: - "\nimport { fitsInside, fitsOutside } from './fitting';\n\nexport interface RingsideInterface {\n positions(): FittedPosition[];\n}\n\nclass Ringside implements RingsideInterface {\n readonly innerBounds: FullRect;\n readonly outerBounds: FullRect;\n\n}\n\nexport default Ringside;\n", + '\nimport nodemailer from "nodemailer";\nimport passport from "passport";\nimport { User, UserDocument, AuthToken } from "../models/User";\nimport { Request, Response, NextFunction } from "express";\nimport { IVerifyOptions } from "passport-local";\n\n */\nexport const getLogin = (req: Request, res: Response) => {\n if (req.user) {\n return res.redirect("/");\n }\n\n }\n\n passport.authenticate("local", (err: Error, user: UserDocument, info: IVerifyOptions) => {\n if (err) { return next(err); }\n if (!user) {\n', lineMapping: [ '..', - '13', - '14', + '3', + '4', + '5', + '6', + '7', + '..', '15', '16', '17', '18', '19', - '20', - '21', '..', - '67', - '68', - '69', - '70', + '40', + '41', + '42', + '43', + '44', + '..', ], }, }, - 'ringside.story.tsx#L12': { - uri: 'github.com/rylnd/ringside', - filePath: 'stories/ringside.story.tsx', + 'User.ts#L8': { + uri: 'github.com/Microsoft/Typescript-Node-Starter', + filePath: 'src/models/User.ts', language: 'typescript', compositeContent: { content: - "\nimport { interpolateRainbow } from 'd3-scale-chromatic';\n\nimport { Ringside } from '../src';\nimport { XAlignment, YAlignment, XBasis, YBasis } from '../src/types';\n\nlet ringside: Ringside;\n\nconst enumKeys: (e: any) => string[] = e =>\n\n\nconst color = position => {\n const combos = ringside.positions().map(p => JSON.stringify(p));\n const hash = combos.indexOf(JSON.stringify(position)) / combos.length;\n\n\n};\n\nconst Stories = storiesOf('Ringside', module).addDecorator(withKnobs);\n\nStories.add('Ringside', () => {\n", + '\nimport mongoose from "mongoose";\n\nexport type UserDocument = mongoose.Document & {\n email: string;\n password: string;\n\n}\n\nconst userSchema = new mongoose.Schema({\n email: { type: String, unique: true },\n password: String,\n\n * Password hash middleware.\n */\nuserSchema.pre("save", function save(next) {\n const user = this as UserDocument;\n if (!user.isModified("password")) { return next(); }\n bcrypt.genSalt(10, (err, salt) => {\n', lineMapping: [ '..', + '3', + '4', '5', '6', '7', - '8', - '9', - '10', - '11', - '12', '..', - '14', - '15', - '16', - '17', - '18', + '31', + '32', + '33', + '34', + '35', '..', - '20', - '21', - '22', - '23', - '24', + '54', + '55', + '56', + '57', + '58', + '59', '..', ], }, }, - - 'ringside.story.tsx#L8': { - uri: 'github.com/rylnd/ringside', - filePath: 'stories/ringside.story.tsx', + 'passport.ts#L10': { + uri: 'github.com/Microsoft/Typescript-Node-Starter', + filePath: 'src/config/passport.ts', language: 'typescript', compositeContent: { content: - "import { Ringside } from '../src';\n\ndescribe('Ringside', () => {\n let inner;\n let outer;\n let height;\n let width;\n let ringside: Ringside;\n\n beforeEach(() => {\n\n width = 50;\n\n ringside = new Ringside(inner, outer, height, width);\n });\n\n", - lineMapping: [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '..', - '14', - '15', - '16', - '17', - '18', - '..', - ], + '\nimport _ from "lodash";\n\n// import { User, UserType } from \'../models/User\';\nimport { User, UserDocument } from "../models/User";\nimport { Request, Response, NextFunction } from "express";\n\n', + lineMapping: ['..', '4', '5', '6', '7', '8', '9', '..'], }, }, - - 'ringside.story.tsx#L14': { - uri: 'github.com/rylnd/ringside', - filePath: 'stories/ringside.story.tsx', + 'user.test.ts#L3': { + uri: 'github.com/Microsoft/Typescript-Node-Starter', + filePath: 'test/user.test.ts', + language: 'typescript', + compositeContent: { + content: + 'import request from "supertest";\nimport app from "../src/app";\nimport { expect } from "chai";\n', + lineMapping: ['1', '2', '3', '..'], + }, + }, + 'app.ts#L60': { + uri: 'github.com/Microsoft/Typescript-Node-Starter', + filePath: 'src/app.ts', language: 'typescript', compositeContent: { content: - "import { Ringside } from '../src';\n\ndescribe('Ringside', () => {\n let inner;\n let outer;\n let height;\n let width;\n let ringside: Ringside;\n\n beforeEach(() => {\n\n width = 50;\n\n ringside = new Ringside(inner, outer, height, width);\n });\n\n", + '\n// Controllers (route handlers)\nimport * as homeController from "./controllers/home";\nimport * as userController from "./controllers/user";\nimport * as apiController from "./controllers/api";\nimport * as contactController from "./controllers/contact";\n\napp.use(lusca.xssProtection(true));\napp.use((req, res, next) => {\n res.locals.user = req.user;\n next();\n});\napp.use((req, res, next) => {\n // After successful login, redirect back to the intended page\n if (!req.user &&\n req.path !== "/login" &&\n req.path !== "/signup" &&\n', lineMapping: [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', '..', - '14', - '15', '16', '17', '18', + '19', + '20', + '..', + '60', + '61', + '62', + '63', + '64', + '65', + '66', + '67', + '68', + '69', '..', ], }, @@ -143,13 +136,14 @@ export interface Frame { } export const frames: Frame[] = [ - { fileName: 'ringside.ts', lineNumber: 18 }, - { fileName: 'node_modules/library_code.js', lineNumber: 100 }, - { fileName: 'ringside.story.tsx', lineNumber: 8 }, - { fileName: 'node_modules/other_stuff.js', lineNumber: 58 }, - { fileName: 'node_modules/other/other.js', lineNumber: 3 }, - { fileName: 'ringside.story.tsx', lineNumber: 12 }, - { fileName: 'ringside.story.tsx', lineNumber: 14 }, + { fileName: 'user.ts', lineNumber: 15 }, + { fileName: 'user.ts', lineNumber: 25 }, + { fileName: 'User.ts', lineNumber: 8 }, + { fileName: 'passport.ts', lineNumber: 10 }, + { fileName: 'app.ts', lineNumber: 60 }, + { fileName: 'app.ts', lineNumber: 2 }, + { fileName: 'user.test.ts', lineNumber: 18 }, + { fileName: 'user.test.ts', lineNumber: 3 }, ]; export const repos = [ diff --git a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx index 58e48b8014728..e8a7ae25de3de 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx @@ -31,6 +31,7 @@ export const Integrations = () => ( const { compositeContent, filePath, language, uri } = snippet; const { content, lineMapping } = compositeContent; const fileUrl = externalFileURI(uri, filePath); + const lines = content.split('\n'); return (
@@ -41,7 +42,7 @@ export const Integrations = () => ( lineNumber={lineNumber} onClick={() => history.push(fileUrl)} /> - lineMapping[i]} /> + lineMapping[i]} />
); diff --git a/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx b/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx index 1f21e9bdae4d6..022388f141b65 100644 --- a/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx +++ b/x-pack/legacy/plugins/code/public/components/search_page/code_result.tsx @@ -28,6 +28,7 @@ export class CodeResult extends React.PureComponent { const key = `${uri}-${filePath}-${query}`; const repoLinkUrl = `/${uri}/tree/HEAD/`; const fileLinkUrl = `/${uri}/blob/HEAD/${filePath}`; // TODO(rylnd) move these to link helpers + const lines = content.split('\n'); return (
@@ -74,7 +75,7 @@ export class CodeResult extends React.PureComponent { lineMapping[i]} diff --git a/x-pack/legacy/plugins/code/public/monaco/monaco_helper.ts b/x-pack/legacy/plugins/code/public/monaco/monaco_helper.ts index 3dee78b9be803..79e1141945708 100644 --- a/x-pack/legacy/plugins/code/public/monaco/monaco_helper.ts +++ b/x-pack/legacy/plugins/code/public/monaco/monaco_helper.ts @@ -136,8 +136,8 @@ export class MonacoHelper { range: new this.monaco!.Range(line, 0, line, 0), options: { isWholeLine: true, - className: `code-monaco-highlight-line code-line-number-${line}`, - linesDecorationsClassName: 'code-mark-line-number', + className: 'codeBlock__line--highlighted', + linesDecorationsClassName: 'codeBlock__line--highlighted', }, }, ]); diff --git a/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss b/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss index a79d7da47ca02..fbd9e8ad61dec 100644 --- a/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss +++ b/x-pack/legacy/plugins/code/public/monaco/override_monaco_styles.scss @@ -13,7 +13,6 @@ background-color: $euiColorLightestShade; } - .cldr.code-mark-line-number + .cldr.folding, .cldr.code-line-decoration + .cldr.folding { left: -124px !important; opacity: 1; diff --git a/x-pack/legacy/plugins/code/public/style/_monaco.scss b/x-pack/legacy/plugins/code/public/style/_monaco.scss index c1c47a8d990d7..51c8308ce42a7 100644 --- a/x-pack/legacy/plugins/code/public/style/_monaco.scss +++ b/x-pack/legacy/plugins/code/public/style/_monaco.scss @@ -8,14 +8,8 @@ cursor: pointer; } - -.code-monaco-highlight-line { - background: $euiCodeBlockBuiltInColor; -} - -.code-mark-line-number { - background: $euiCodeBlockBuiltInColor; - width: $euiSizeXS; +.codeBlock__line--highlighted { + background-color: $euiColorLightShade; } .monaco-editor .margin-view-overlays .line-numbers { @@ -71,11 +65,6 @@ display: none; } -.code-mark-line-number, -.code-monaco-highlight-line { - background-color: $euiColorLightShade; -} - .text-placeholder { width: 100%; height: 18px; diff --git a/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js b/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js index 54d77b2aa1d05..79a88a0099c2f 100644 --- a/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js +++ b/x-pack/legacy/plugins/file_upload/public/components/json_index_file_picker.js @@ -10,6 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { parseFile } from '../util/file_parser'; import { MAX_FILE_SIZE } from '../../common/constants/file_import'; +import _ from 'lodash'; const ACCEPTABLE_FILETYPES = [ 'json', @@ -20,7 +21,10 @@ export class JsonIndexFilePicker extends Component { state = { fileUploadError: '', - fileParsingProgress: '', + percentageProcessed: 0, + featuresProcessed: 0, + fileParseActive: false, + currentFileTracker: null, }; async componentDidMount() { @@ -31,16 +35,28 @@ export class JsonIndexFilePicker extends Component { this._isMounted = false; } + getFileParseActive = () => this._isMounted && this.state.fileParseActive; + _fileHandler = fileList => { const fileArr = Array.from(fileList); this.props.resetFileAndIndexSettings(); - this.setState({ fileUploadError: '' }); + this.setState({ + fileUploadError: '', + percentageProcessed: 0, + featuresProcessed: 0, + }); if (fileArr.length === 0) { // Remove + this.setState({ + fileParseActive: false + }); return; } const file = fileArr[0]; - this._parseFile(file); + this.setState({ + fileParseActive: true, + currentFileTracker: Symbol() + }, () => this._parseFile(file)); }; _checkFileSize = ({ size }) => { @@ -106,7 +122,17 @@ export class JsonIndexFilePicker extends Component { return fileNameOnly.toLowerCase(); } + // It's necessary to throttle progress. Updates that are too frequent cause + // issues (update failure) in the nested progress component + setFileProgress = _.debounce(({ featuresProcessed, bytesProcessed, totalBytes }) => { + const percentageProcessed = parseInt((100 * bytesProcessed) / totalBytes); + if (this.getFileParseActive()) { + this.setState({ featuresProcessed, percentageProcessed }); + } + }, 150); + async _parseFile(file) { + const { currentFileTracker } = this.state; const { setFileRef, setParsedFile, resetFileAndIndexSettings, onFileUpload, transformDetails, setIndexName @@ -119,15 +145,19 @@ export class JsonIndexFilePicker extends Component { return; } // Parse file - this.setState({ fileParsingProgress: i18n.translate( - 'xpack.fileUpload.jsonIndexFilePicker.parsingFile', - { defaultMessage: 'Parsing file...' }) - }); - const parsedFileResult = await parseFile( - file, transformDetails, onFileUpload - ).catch(err => { + + const fileResult = await parseFile({ + file, + transformDetails, + onFileUpload, + setFileProgress: this.setFileProgress, + getFileParseActive: this.getFileParseActive + }).catch(err => { if (this._isMounted) { this.setState({ + fileParseActive: false, + percentageProcessed: 0, + featuresProcessed: 0, fileUploadError: ( + ) + }); + } setIndexName(defaultIndexName); setFileRef(file); - setParsedFile(parsedFileResult); + setParsedFile(parsedGeojson); } render() { - const { fileParsingProgress, fileUploadError } = this.state; + const { + fileUploadError, + percentageProcessed, + featuresProcessed, + } = this.state; return ( - {fileParsingProgress ? : null} + {percentageProcessed + ? + : null + } {i18n.translate('xpack.fileUpload.jsonIndexFilePicker.formatsAccepted', { diff --git a/x-pack/legacy/plugins/file_upload/public/util/file_parser.js b/x-pack/legacy/plugins/file_upload/public/util/file_parser.js index 8af4150f503da..b53a7c67992ad 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/file_parser.js +++ b/x-pack/legacy/plugins/file_upload/public/util/file_parser.js @@ -7,39 +7,127 @@ import _ from 'lodash'; import { geoJsonCleanAndValidate } from './geo_json_clean_and_validate'; import { i18n } from '@kbn/i18n'; +import { PatternReader } from './pattern_reader'; -export async function readFile(file) { - const readPromise = new Promise((resolve, reject) => { - if (!file) { - reject(new Error(i18n.translate( - 'xpack.fileUpload.fileParser.noFileProvided', { +// In local testing, performance improvements leveled off around this size +export const FILE_BUFFER = 1024000; + +const readSlice = (fileReader, file, start, stop) => { + const blob = file.slice(start, stop); + fileReader.readAsBinaryString(blob); +}; + +let prevFileReader; +let prevPatternReader; +export const fileHandler = async ({ + file, + setFileProgress, + cleanAndValidate, + getFileParseActive, + fileReader = new FileReader() +}) => { + + if (!file) { + return Promise.reject( + new Error( + i18n.translate('xpack.fileUpload.fileParser.noFileProvided', { defaultMessage: 'Error, no file provided', - }))); - } - const fr = new window.FileReader(); - fr.onload = e => resolve(e.target.result); - fr.onerror = () => { - fr.abort(); + }) + ) + ); + } + + + // Halt any previous file reading & pattern checking activity + if (prevFileReader) { + prevFileReader.abort(); + } + if (prevPatternReader) { + prevPatternReader.abortStream(); + } + + // Set up feature tracking + let featuresProcessed = 0; + const onFeatureRead = feature => { + // TODO: Add handling and tracking for cleanAndValidate fails + featuresProcessed++; + return cleanAndValidate(feature); + }; + + let start; + let stop = FILE_BUFFER; + + prevFileReader = fileReader; + + const filePromise = new Promise((resolve, reject) => { + const onStreamComplete = fileResults => { + if (!featuresProcessed) { + reject( + new Error( + i18n.translate('xpack.fileUpload.fileParser.noFeaturesDetected', { + defaultMessage: 'Error, no features detected', + }) + ) + ); + } else { + resolve(fileResults); + } + }; + const patternReader = new PatternReader({ onFeatureDetect: onFeatureRead, onStreamComplete }); + prevPatternReader = patternReader; + + fileReader.onloadend = ({ target: { readyState, result } }) => { + if (readyState === FileReader.DONE) { + if (!getFileParseActive() || !result) { + fileReader.abort(); + patternReader.abortStream(); + resolve(null); + return; + } + setFileProgress({ + featuresProcessed, + bytesProcessed: stop || file.size, + totalBytes: file.size + }); + patternReader.writeDataToPatternStream(result); + if (!stop) { + return; + } + + start = stop; + const newStop = stop + FILE_BUFFER; + // Check EOF + stop = newStop > file.size ? undefined : newStop; + readSlice(fileReader, file, start, stop); + } + }; + fileReader.onerror = () => { + fileReader.abort(); + patternReader.abortStream(); reject(new Error(i18n.translate( 'xpack.fileUpload.fileParser.errorReadingFile', { defaultMessage: 'Error reading file', }))); }; - fr.readAsText(file); }); + readSlice(fileReader, file, start, stop); + return filePromise; +}; - return await readPromise; -} - -export function jsonPreview(json, previewFunction) { - // Call preview (if any) - if (json && previewFunction) { - const defaultName = _.get(json, 'name', 'Import File'); - previewFunction(json, defaultName); +export function jsonPreview(fileResults, previewFunction) { + if (fileResults && fileResults.parsedGeojson && previewFunction) { + const defaultName = _.get(fileResults.parsedGeojson, 'name', 'Import File'); + previewFunction(fileResults.parsedGeojson, defaultName); } } -export async function parseFile(file, transformDetails, previewCallback = null) { +export async function parseFile({ + file, + transformDetails, + onFileUpload: previewCallback = null, + setFileProgress, + getFileParseActive +}) { let cleanAndValidate; if (typeof transformDetails === 'object') { cleanAndValidate = transformDetails.cleanAndValidate; @@ -56,15 +144,15 @@ export async function parseFile(file, transformDetails, previewCallback = null) values: { transformDetails } }) ); - return; } } - const rawResults = await readFile(file); - const parsedJson = JSON.parse(rawResults); - const jsonResult = cleanAndValidate(parsedJson); - jsonPreview(jsonResult, previewCallback); - - return jsonResult; + const fileResults = await fileHandler({ + file, + setFileProgress, + cleanAndValidate, + getFileParseActive + }); + jsonPreview(fileResults, previewCallback); + return fileResults; } - diff --git a/x-pack/legacy/plugins/file_upload/public/util/file_parser.test.js b/x-pack/legacy/plugins/file_upload/public/util/file_parser.test.js index ecbc67f1999a9..8127b544dc1c6 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/file_parser.test.js +++ b/x-pack/legacy/plugins/file_upload/public/util/file_parser.test.js @@ -4,61 +4,128 @@ * you may not use this file except in compliance with the Elastic License. */ -import { parseFile } from './file_parser'; +import { fileHandler } from './file_parser'; +jest.mock('./pattern_reader', () => ({})); -describe('parse file', () => { - const cleanAndValidate = jest.fn(a => a); - const previewFunction = jest.fn(); - const transformDetails = { - cleanAndValidate +const cleanAndValidate = jest.fn(a => a); +const setFileProgress = jest.fn(a => a); + +const getFileReader = () => { + const fileReader = { + abort: jest.fn(), + }; + fileReader.readAsBinaryString = jest.fn( + (binaryString = '123') => fileReader.onloadend( + { target: { readyState: FileReader.DONE, result: binaryString } } + ) + ); + return fileReader; +}; +const getPatternReader = () => { + const patternReader = { + writeDataToPatternStream: jest.fn(), + abortStream: jest.fn(), }; - const getFileRef = fileContent => - new File([fileContent], 'test.json', { type: 'text/json' }); + require('./pattern_reader').PatternReader = function () { + this.writeDataToPatternStream = () => patternReader.writeDataToPatternStream(); + this.abortStream = () => patternReader.abortStream(); + }; + return patternReader; +}; + +const testJson = { + 'type': 'Feature', + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[ + [-104.05, 78.99], + [-87.22, 78.98], + [-86.58, 75.94], + [-104.03, 75.94], + [-104.05, 78.99] + ]] + }, +}; + +const getFileRef = (geoJsonObj = testJson) => { + const fileContent = JSON.stringify(geoJsonObj); + return new File([fileContent], 'test.json', { type: 'text/json' }); +}; + +const getFileParseActiveFactory = (boolActive = true) => { + return jest.fn(() => boolActive); +}; + +describe('parse file', () => { - beforeEach(() => { - cleanAndValidate.mockClear(); - previewFunction.mockClear(); + afterEach(() => { + jest.resetAllMocks(); + jest.restoreAllMocks(); }); - it('should parse valid JSON', async () => { - const validJsonFileResult = JSON.stringify({ - 'type': 'Feature', - 'geometry': { - 'type': 'Polygon', - 'coordinates': [[ - [-104.05, 78.99], - [-87.22, 78.98], - [-86.58, 75.94], - [-104.03, 75.94], - [-104.05, 78.99] - ]] - }, + it('should reject and throw error if no file provided', async () => { + expect(fileHandler(null)).rejects.toThrow(); + }); + + it('should abort and resolve to null if file parse cancelled', async () => { + const fileRef = getFileRef(); + const cancelledActionFileReader = getFileReader(); + const cancelledActionPatternReader = getPatternReader(); + + // Cancel file parse + const getFileParseActive = getFileParseActiveFactory(false); + + const fileHandlerResult = await fileHandler({ + file: fileRef, + setFileProgress, + cleanAndValidate, + getFileParseActive, + fileReader: cancelledActionFileReader }); - await parseFile(getFileRef(validJsonFileResult), transformDetails); - // Confirm cleanAndValidate called - expect(cleanAndValidate.mock.calls.length).toEqual(1); - // Confirm preview function not called - expect(previewFunction.mock.calls.length).toEqual(0); + expect(fileHandlerResult).toBeNull(); + expect(cancelledActionFileReader.abort.mock.calls.length).toEqual(1); + expect(cancelledActionPatternReader.abortStream.mock.calls.length).toEqual(1); + }); + + it('should abort on file reader error', () => { + const fileRef = getFileRef(); + + const fileReaderWithErrorCall = getFileReader(); + const patternReaderWithErrorCall = getPatternReader(); + + // Trigger on error on read + fileReaderWithErrorCall.readAsBinaryString = + () => fileReaderWithErrorCall.onerror(); + const getFileParseActive = getFileParseActiveFactory(); + expect(fileHandler({ + file: fileRef, + setFileProgress, + cleanAndValidate, + getFileParseActive, + fileReader: fileReaderWithErrorCall + })).rejects.toThrow(); + + expect(fileReaderWithErrorCall.abort.mock.calls.length).toEqual(1); + expect(patternReaderWithErrorCall.abortStream.mock.calls.length).toEqual(1); }); - it('should call preview callback function if provided', async () => { - const validJsonFileResult = JSON.stringify({ - 'type': 'Feature', - 'geometry': { - 'type': 'Polygon', - 'coordinates': [[ - [-104.05, 78.99], - [-87.22, 78.98], - [-86.58, 75.94], - [-104.03, 75.94], - [-104.05, 78.99] - ]] - }, + // Expect 2 calls, one reads file, next is 'undefined' to + // both fileReader and patternReader + it('should normally read binary and emit to patternReader for valid data', async () => { + const fileRef = getFileRef(); + const fileReaderForValidFile = getFileReader(); + const patternReaderForValidFile = getPatternReader(); + const getFileParseActive = getFileParseActiveFactory(); + fileHandler({ + file: fileRef, + setFileProgress, + cleanAndValidate, + getFileParseActive, + fileReader: fileReaderForValidFile }); - await parseFile(getFileRef(validJsonFileResult), transformDetails, previewFunction); - // Confirm preview function called - expect(previewFunction.mock.calls.length).toEqual(1); + expect(fileReaderForValidFile.readAsBinaryString.mock.calls.length).toEqual(2); + expect(patternReaderForValidFile.writeDataToPatternStream.mock.calls.length).toEqual(2); }); }); diff --git a/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.js b/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.js index b4a05e52e664a..dda4b77edb319 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.js +++ b/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.js @@ -7,47 +7,35 @@ import * as jsts from 'jsts'; import rewind from 'geojson-rewind'; -export function geoJsonCleanAndValidate(parsedFile) { - - // Remove bbox property pending fix of bbox parsing issue in jsts lib - const { bbox, ...handledGeoJsonProperties } = parsedFile; // eslint-disable-line no-unused-vars - - const reader = new jsts.io.GeoJSONReader(); - const geoJson = reader.read(handledGeoJsonProperties); - const isSingleFeature = parsedFile.type === 'Feature'; - const features = isSingleFeature - ? [{ ...geoJson }] - : geoJson.features; - - // Pass features for cleaning - const cleanedFeatures = cleanFeatures(features); - - // Put clean features back in geoJson object - const cleanGeoJson = { - ...parsedFile, - ...(isSingleFeature - ? cleanedFeatures[0] - : { features: cleanedFeatures } - ), - }; +const geoJSONReader = new jsts.io.GeoJSONReader(); +const geoJSONWriter = new jsts.io.GeoJSONWriter(); + +export function geoJsonCleanAndValidate(feature) { + + const geometryReadResult = geoJSONReader.read(feature); + + const cleanedGeometry = cleanGeometry(geometryReadResult); + + // For now, return the feature unmodified + // TODO: Consider more robust UI feedback and general handling + // for features that fail cleaning and/or validation + if (!cleanedGeometry) { + return feature; + } - // Pass entire geoJson object for winding // JSTS does not enforce winding order, wind in clockwise order - const correctlyWindedGeoJson = rewind(cleanGeoJson, false); - return correctlyWindedGeoJson; + const correctlyWindedGeometry = rewind(cleanedGeometry, false); + + return { + ...feature, + geometry: correctlyWindedGeometry + }; } -export function cleanFeatures(features) { - const writer = new jsts.io.GeoJSONWriter(); - return features.map(({ id, geometry, properties }) => { - const geojsonGeometry = (geometry.isSimple() || geometry.isValid()) - ? writer.write(geometry) - : writer.write(geometry.buffer(0)); - return ({ - type: 'Feature', - geometry: geojsonGeometry, - ...(id ? { id } : {}), - ...(properties ? { properties } : {}), - }); - }); +export function cleanGeometry({ geometry }) { + if (!geometry) { + return null; + } + const geometryToWrite = (geometry.isSimple() || geometry.isValid()) ? geometry : geometry.buffer(0); + return geoJSONWriter.write(geometryToWrite); } diff --git a/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js b/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js index 5c25120a7f5de..936ea97439d20 100644 --- a/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js +++ b/x-pack/legacy/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js @@ -5,7 +5,7 @@ */ import { - cleanFeatures, + cleanGeometry, geoJsonCleanAndValidate, } from './geo_json_clean_and_validate'; const jsts = require('jsts'); @@ -36,8 +36,8 @@ describe('geo_json_clean_and_validate', () => { expect(isSimpleOrValid).toEqual(true); // Confirm no change to features - const cleanedFeatures = cleanFeatures([geoJson]); - expect(cleanedFeatures[0]).toEqual(goodFeatureGeoJson); + const cleanedFeature = cleanGeometry(geoJson); + expect(cleanedFeature).toEqual(goodFeatureGeoJson.geometry); }); it('should modify incorrect features', () => { @@ -85,8 +85,13 @@ describe('geo_json_clean_and_validate', () => { }); // Confirm changes to object - const cleanedFeatures = cleanFeatures(geoJson.features); - expect(cleanedFeatures).not.toEqual(badFeaturesGeoJson.features); + const cleanedFeatures = geoJson.features.map(feature => ({ + ...feature, + geometry: cleanGeometry(feature) + })); + cleanedFeatures.forEach((feature, idx) => + expect(feature).not.toEqual(badFeaturesGeoJson.features[idx]) + ); // Confirm now valid features geometry geoJson = reader.read({ ...badFeaturesGeoJson, features: cleanedFeatures }); diff --git a/x-pack/legacy/plugins/file_upload/public/util/pattern_reader.js b/x-pack/legacy/plugins/file_upload/public/util/pattern_reader.js new file mode 100644 index 0000000000000..9c3189c233afe --- /dev/null +++ b/x-pack/legacy/plugins/file_upload/public/util/pattern_reader.js @@ -0,0 +1,66 @@ +/* + * 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'; +const oboe = require('oboe'); + + +export class PatternReader { + + constructor({ onFeatureDetect, onStreamComplete }) { + this._oboeStream = oboe(); + this._registerFeaturePatternHandler(onFeatureDetect); + this._registerStreamCompleteHandler(onStreamComplete); + this._errors = []; + } + + getErrors() { + return this._errors; + } + + _registerFeaturePatternHandler(featurePatternCallback) { + this._oboeStream.node({ + 'features.*': feature => { + if (!feature.geometry || !feature.geometry.type) { + // Only add this error once + // TODO: Give feedback on which features failed + if (!this._errors.length) { + this._errors.push( + new Error( + i18n.translate('xpack.fileUpload.patternReader.featuresOmitted', { + defaultMessage: 'Some features without geometry omitted', + }) + ) + ); + } + return oboe.drop; + } + return featurePatternCallback(feature); + }, + // Handle single feature files + '!.geometry': (geom, path, ancestors) => { + const feature = ancestors[0]; + const { geometry } = featurePatternCallback(feature); + return geometry; + } + }); + } + + _registerStreamCompleteHandler(streamCompleteCallback) { + this._oboeStream.done(parsedGeojson => { + streamCompleteCallback({ parsedGeojson, errors: this.getErrors() }); + }); + } + + writeDataToPatternStream(data) { + this._oboeStream.emit('data', data); + } + + abortStream() { + this._oboeStream.abort(); + } + +} diff --git a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts index 870d9f50d44de..89e42f69c9daf 100644 --- a/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts +++ b/x-pack/legacy/plugins/infra/common/log_analysis/log_analysis.ts @@ -12,10 +12,45 @@ export const jobTypeRT = rt.keyof({ export type JobType = rt.TypeOf; -export const jobStatusRT = rt.keyof({ - created: null, - missing: null, - running: null, -}); +// combines and abstracts job and datafeed status +export type JobStatus = + | 'unknown' + | 'missing' + | 'initializing' + | 'stopped' + | 'started' + | 'finished' + | 'failed'; + +export type SetupStatus = + | 'initializing' // acquiring job statuses to determine setup status + | 'unknown' // job status could not be acquired (failed request etc) + | 'required' // jobs are missing + | 'requiredForReconfiguration' // the configurations don't match the source configurations + | 'requiredForUpdate' // the definitions don't match the module definitions + | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response + | 'succeeded' // setup succeeded, notifying user + | 'failed' // setup failed, notifying user + | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time + | 'skipped' // setup hidden because the module is in a correct state already + | 'skippedButReconfigurable' // setup hidden even though the job configurations are outdated + | 'skippedButUpdatable'; // setup hidden even though the job definitions are outdated + +/** + * Maps a job status to the possibility that results have already been produced + * before this state was reached. + */ +export const isJobStatusWithResults = (jobStatus: JobStatus) => + ['started', 'finished', 'stopped', 'failed'].includes(jobStatus); + +export const isHealthyJobStatus = (jobStatus: JobStatus) => + ['started', 'finished'].includes(jobStatus); -export type JobStatus = rt.TypeOf; +/** + * Maps a setup status to the possibility that results have already been + * produced before this state was reached. + */ +export const isSetupStatusWithResults = (setupStatus: SetupStatus) => + ['skipped', 'hiddenAfterSuccess', 'skippedButReconfigurable', 'skippedButUpdatable'].includes( + setupStatus + ); diff --git a/x-pack/legacy/plugins/siem/public/containers/network/index.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts similarity index 81% rename from x-pack/legacy/plugins/siem/public/containers/network/index.tsx rename to x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts index ca06a9c21cddb..06229a26afd19 100644 --- a/x-pack/legacy/plugins/siem/public/containers/network/index.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { NetworkFilter } from './filter'; +export * from './log_analysis_job_problem_indicator'; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx new file mode 100644 index 0000000000000..13b7d1927f676 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { RecreateJobCallout } from './recreate_job_callout'; + +export const JobConfigurationOutdatedCallout: React.FC<{ + onRecreateMlJob: () => void; +}> = ({ onRecreateMlJob }) => ( + + + +); + +const jobConfigurationOutdatedTitle = i18n.translate( + 'xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle', + { + defaultMessage: 'ML job configuration outdated', + } +); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx new file mode 100644 index 0000000000000..5072fb09cdceb --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { RecreateJobCallout } from './recreate_job_callout'; + +export const JobDefinitionOutdatedCallout: React.FC<{ + onRecreateMlJob: () => void; +}> = ({ onRecreateMlJob }) => ( + + + +); + +const jobDefinitionOutdatedTitle = i18n.translate( + 'xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle', + { + defaultMessage: 'ML job definition outdated', + } +); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx new file mode 100644 index 0000000000000..33f0a5b1399a1 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx @@ -0,0 +1,24 @@ +/* + * 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 { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const JobStoppedCallout: React.FC = () => ( + + + +); + +const jobStoppedTitle = i18n.translate('xpack.infra.logs.analysis.jobStoppedCalloutTitle', { + defaultMessage: 'ML job stopped', +}); diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx new file mode 100644 index 0000000000000..018c5f5e0570d --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { JobStatus, SetupStatus } from '../../../../common/log_analysis'; +import { JobConfigurationOutdatedCallout } from './job_configuration_outdated_callout'; +import { JobDefinitionOutdatedCallout } from './job_definition_outdated_callout'; +import { JobStoppedCallout } from './job_stopped_callout'; + +export const LogAnalysisJobProblemIndicator: React.FC<{ + jobStatus: JobStatus; + setupStatus: SetupStatus; + onRecreateMlJobForReconfiguration: () => void; + onRecreateMlJobForUpdate: () => void; +}> = ({ jobStatus, setupStatus, onRecreateMlJobForReconfiguration, onRecreateMlJobForUpdate }) => { + if (jobStatus === 'stopped') { + return ; + } else if (setupStatus === 'skippedButUpdatable') { + return ; + } else if (setupStatus === 'skippedButReconfigurable') { + return ; + } + + return null; // no problem to indicate +}; diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx new file mode 100644 index 0000000000000..b95054bbd6a9b --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx @@ -0,0 +1,24 @@ +/* + * 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 { EuiCallOut, EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const RecreateJobCallout: React.FC<{ + onRecreateMlJob: () => void; + title?: React.ReactNode; +}> = ({ children, onRecreateMlJob, title }) => ( + +

{children}

+ + + +
+); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts new file mode 100644 index 0000000000000..deb3d528e42c2 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts @@ -0,0 +1,16 @@ +/* + * 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 rt from 'io-ts'; + +export const jobCustomSettingsRT = rt.partial({ + job_revision: rt.number, + logs_source_config: rt.partial({ + indexPattern: rt.string, + timestampField: rt.string, + bucketSpan: rt.number, + }), +}); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 0b6e0981c7f9f..477e75fc3b90c 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as rt from 'io-ts'; -import { kfetch } from 'ui/kfetch'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + +import { jobCustomSettingsRT } from './ml_api_types'; import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; import { getAllModuleJobIds } from '../../../../../common/log_analysis'; @@ -56,11 +58,14 @@ export const jobSummaryRT = rt.intersection([ datafeedIndices: rt.array(rt.string), datafeedState: datafeedStateRT, fullJob: rt.partial({ + custom_settings: jobCustomSettingsRT, finished_time: rt.number, }), }), ]); +export type JobSummary = rt.TypeOf; + export const fetchJobStatusResponsePayloadRT = rt.array(jobSummaryRT); export type FetchJobStatusResponsePayload = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts new file mode 100644 index 0000000000000..b58677ffa844e --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts @@ -0,0 +1,42 @@ +/* + * 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 { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { jobCustomSettingsRT } from './ml_api_types'; + +export const callGetMlModuleAPI = async (moduleId: string) => { + const response = await kfetch({ + method: 'GET', + pathname: `/api/ml/modules/get_module/${moduleId}`, + }); + + return pipe( + getMlModuleResponsePayloadRT.decode(response), + fold(throwErrors(createPlainError), identity) + ); +}; + +const jobDefinitionRT = rt.type({ + id: rt.string, + config: rt.type({ + custom_settings: jobCustomSettingsRT, + }), +}); + +export type JobDefinition = rt.TypeOf; + +const getMlModuleResponsePayloadRT = rt.type({ + id: rt.string, + jobs: rt.array(jobDefinitionRT), +}); + +export type GetMlModuleResponsePayload = rt.TypeOf; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts index 722d19d99bd23..92078b23085c3 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts @@ -4,27 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as rt from 'io-ts'; -import { kfetch } from 'ui/kfetch'; - import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { kfetch } from 'ui/kfetch'; + import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; import { getJobIdPrefix } from '../../../../../common/log_analysis'; - -const MODULE_ID = 'logs_ui_analysis'; - -// This is needed due to: https://github.com/elastic/kibana/issues/43671 -const removeSampleDataIndex = (indexPattern: string) => { - const SAMPLE_DATA_INDEX = 'kibana_sample_data_logs*'; - return indexPattern - .split(',') - .filter(index => index !== SAMPLE_DATA_INDEX) - .join(','); -}; +import { jobCustomSettingsRT } from './ml_api_types'; export const callSetupMlModuleAPI = async ( + moduleId: string, start: number | undefined, end: number | undefined, spaceId: string, @@ -35,23 +26,30 @@ export const callSetupMlModuleAPI = async ( ) => { const response = await kfetch({ method: 'POST', - pathname: `/api/ml/modules/setup/${MODULE_ID}`, + pathname: `/api/ml/modules/setup/${moduleId}`, body: JSON.stringify( setupMlModuleRequestPayloadRT.encode({ start, end, - indexPatternName: removeSampleDataIndex(indexPattern), + indexPatternName: indexPattern, prefix: getJobIdPrefix(spaceId, sourceId), startDatafeed: true, jobOverrides: [ { - job_id: 'log-entry-rate', + job_id: 'log-entry-rate' as const, analysis_config: { bucket_span: `${bucketSpan}ms`, }, data_description: { time_field: timeField, }, + custom_settings: { + logs_source_config: { + indexPattern, + timestampField: timeField, + bucketSpan, + }, + }, }, ], datafeedOverrides: [], @@ -70,11 +68,22 @@ const setupMlModuleTimeParamsRT = rt.partial({ end: rt.number, }); +const setupMlModuleLogEntryRateJobOverridesRT = rt.type({ + job_id: rt.literal('log-entry-rate'), + analysis_config: rt.type({ + bucket_span: rt.string, + }), + data_description: rt.type({ + time_field: rt.string, + }), + custom_settings: jobCustomSettingsRT, +}); + const setupMlModuleRequestParamsRT = rt.type({ indexPatternName: rt.string, prefix: rt.string, startDatafeed: rt.boolean, - jobOverrides: rt.array(rt.object), + jobOverrides: rt.array(setupMlModuleLogEntryRateJobOverridesRT), datafeedOverrides: rt.array(rt.object), }); diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx index 2c9e16de6a06a..83d4760259d9b 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_jobs.tsx @@ -5,7 +5,9 @@ */ import createContainer from 'constate-latest'; -import { useEffect, useMemo, useCallback } from 'react'; +import { useMemo, useCallback, useEffect } from 'react'; + +import { callGetMlModuleAPI } from './api/ml_get_module'; import { bucketSpan } from '../../../../common/log_analysis'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callJobsSummaryAPI } from './api/ml_get_jobs_summary_api'; @@ -13,6 +15,9 @@ import { callSetupMlModuleAPI, SetupMlModuleResponsePayload } from './api/ml_set import { useLogAnalysisCleanup } from './log_analysis_cleanup'; import { useStatusState } from './log_analysis_status_state'; +const MODULE_ID = 'logs_ui_analysis'; +const SAMPLE_DATA_INDEX = 'kibana_sample_data_logs*'; + export const useLogAnalysisJobs = ({ indexPattern, sourceId, @@ -24,8 +29,35 @@ export const useLogAnalysisJobs = ({ spaceId: string; timeField: string; }) => { + const filteredIndexPattern = useMemo(() => removeSampleDataIndex(indexPattern), [indexPattern]); const { cleanupMLResources } = useLogAnalysisCleanup({ sourceId, spaceId }); - const [statusState, dispatch] = useStatusState(); + const [statusState, dispatch] = useStatusState({ + bucketSpan, + indexPattern: filteredIndexPattern, + timestampField: timeField, + }); + + const [fetchModuleDefinitionRequest, fetchModuleDefinition] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + dispatch({ type: 'fetchingModuleDefinition' }); + return await callGetMlModuleAPI(MODULE_ID); + }, + onResolve: response => { + dispatch({ + type: 'fetchedModuleDefinition', + spaceId, + sourceId, + moduleDefinition: response, + }); + }, + onReject: () => { + dispatch({ type: 'failedFetchingModuleDefinition' }); + }, + }, + [] + ); const [setupMlModuleRequest, setupMlModule] = useTrackedPromise( { @@ -33,11 +65,12 @@ export const useLogAnalysisJobs = ({ createPromise: async (start, end) => { dispatch({ type: 'startedSetup' }); return await callSetupMlModuleAPI( + MODULE_ID, start, end, spaceId, sourceId, - indexPattern, + filteredIndexPattern, timeField, bucketSpan ); @@ -49,7 +82,7 @@ export const useLogAnalysisJobs = ({ dispatch({ type: 'failedSetup' }); }, }, - [indexPattern, spaceId, sourceId, timeField, bucketSpan] + [filteredIndexPattern, spaceId, sourceId, timeField, bucketSpan] ); const [fetchJobStatusRequest, fetchJobStatus] = useTrackedPromise( @@ -66,22 +99,20 @@ export const useLogAnalysisJobs = ({ dispatch({ type: 'failedFetchingJobStatuses' }); }, }, - [indexPattern, spaceId, sourceId] + [filteredIndexPattern, spaceId, sourceId] ); - useEffect(() => { - fetchJobStatus(); - }, []); - - const isLoadingSetupStatus = useMemo(() => fetchJobStatusRequest.state === 'pending', [ - fetchJobStatusRequest.state, - ]); + const isLoadingSetupStatus = useMemo( + () => + fetchJobStatusRequest.state === 'pending' || fetchModuleDefinitionRequest.state === 'pending', + [fetchJobStatusRequest.state, fetchModuleDefinitionRequest.state] + ); const viewResults = useCallback(() => { dispatch({ type: 'viewedResults' }); }, []); - const retry = useCallback( + const cleanupAndSetup = useCallback( (start, end) => { dispatch({ type: 'startedSetup' }); cleanupMLResources() @@ -95,16 +126,38 @@ export const useLogAnalysisJobs = ({ [cleanupMLResources, setupMlModule] ); + const viewSetupForReconfiguration = useCallback(() => { + dispatch({ type: 'requestedJobConfigurationUpdate' }); + }, []); + + const viewSetupForUpdate = useCallback(() => { + dispatch({ type: 'requestedJobDefinitionUpdate' }); + }, []); + + useEffect(() => { + fetchModuleDefinition(); + }, [fetchModuleDefinition]); + return { - setupMlModuleRequest, - jobStatus: statusState.jobStatus, + fetchJobStatus, isLoadingSetupStatus, + jobStatus: statusState.jobStatus, + cleanupAndSetup, setup: setupMlModule, - retry, + setupMlModuleRequest, setupStatus: statusState.setupStatus, + viewSetupForReconfiguration, + viewSetupForUpdate, viewResults, - fetchJobStatus, }; }; export const LogAnalysisJobs = createContainer(useLogAnalysisJobs); +// +// This is needed due to: https://github.com/elastic/kibana/issues/43671 +const removeSampleDataIndex = (indexPattern: string) => { + return indexPattern + .split(',') + .filter(index => index !== SAMPLE_DATA_INDEX) + .join(','); +}; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx index db0af883a046e..9f108b0c50f53 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.tsx @@ -5,28 +5,33 @@ */ import { useState, useCallback } from 'react'; +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; + interface Props { - setupModule: (startTime?: number | undefined, endTime?: number | undefined) => void; - retrySetup: (startTime?: number | undefined, endTime?: number | undefined) => void; + cleanupAndSetupModule: SetupHandler; + setupModule: SetupHandler; } const fourWeeksInMs = 86400000 * 7 * 4; -export const useAnalysisSetupState = ({ setupModule, retrySetup }: Props) => { +export const useAnalysisSetupState = ({ setupModule, cleanupAndSetupModule }: Props) => { const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs); const [endTime, setEndTime] = useState(undefined); + const setup = useCallback(() => { return setupModule(startTime, endTime); }, [setupModule, startTime, endTime]); - const retry = useCallback(() => { - return retrySetup(startTime, endTime); - }, [retrySetup, startTime, endTime]); + + const cleanupAndSetup = useCallback(() => { + return cleanupAndSetupModule(startTime, endTime); + }, [cleanupAndSetupModule, startTime, endTime]); + return { - setup, - retry, - setStartTime, + cleanupAndSetup, + endTime, setEndTime, + setStartTime, + setup, startTime, - endTime, }; }; diff --git a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx index efe5b245517ab..02606c223d86d 100644 --- a/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx +++ b/x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/log_analysis_status_state.tsx @@ -5,33 +5,26 @@ */ import { useReducer } from 'react'; -import { getDatafeedId, getJobId, JobType } from '../../../../common/log_analysis'; -import { FetchJobStatusResponsePayload } from './api/ml_get_jobs_summary_api'; -import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; -// combines and abstracts job and datafeed status -type JobStatus = - | 'unknown' - | 'missing' - | 'initializing' - | 'stopped' - | 'started' - | 'finished' - | 'failed'; - -export type SetupStatus = - | 'initializing' // acquiring job statuses to determine setup status - | 'unknown' // job status could not be acquired (failed request etc) - | 'required' // jobs are missing - | 'pending' // In the process of setting up the module for the first time or retrying, waiting for response - | 'succeeded' // setup succeeded, notifying user - | 'failed' // setup failed, notifying user - | 'hiddenAfterSuccess' // hide the setup screen and we show the results for the first time - | 'skipped'; // setup hidden because the module is in a correct state already +import { + getDatafeedId, + getJobId, + isJobStatusWithResults, + JobStatus, + JobType, + jobTypeRT, + SetupStatus, +} from '../../../../common/log_analysis'; +import { FetchJobStatusResponsePayload, JobSummary } from './api/ml_get_jobs_summary_api'; +import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; +import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; interface StatusReducerState { + jobDefinitions: JobDefinition[]; jobStatus: Record; + jobSummaries: JobSummary[]; setupStatus: SetupStatus; + sourceConfiguration: JobSourceConfiguration; } type StatusReducerAction = @@ -52,19 +45,39 @@ type StatusReducerAction = payload: FetchJobStatusResponsePayload; } | { type: 'failedFetchingJobStatuses' } + | { type: 'fetchingModuleDefinition' } + | { + type: 'fetchedModuleDefinition'; + spaceId: string; + sourceId: string; + moduleDefinition: GetMlModuleResponsePayload; + } + | { type: 'failedFetchingModuleDefinition' } + | { + type: 'updatedSourceConfiguration'; + spaceId: string; + sourceId: string; + sourceConfiguration: JobSourceConfiguration; + } + | { type: 'requestedJobConfigurationUpdate' } + | { type: 'requestedJobDefinitionUpdate' } | { type: 'viewedResults' }; -const initialState: StatusReducerState = { +const createInitialState = (sourceConfiguration: JobSourceConfiguration): StatusReducerState => ({ + jobDefinitions: [], jobStatus: { 'log-entry-rate': 'unknown', }, + jobSummaries: [], setupStatus: 'initializing', -}; + sourceConfiguration, +}); function statusReducer(state: StatusReducerState, action: StatusReducerAction): StatusReducerState { switch (action.type) { case 'startedSetup': { return { + ...state, jobStatus: { 'log-entry-rate': 'initializing', }, @@ -89,12 +102,14 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): ? 'succeeded' : 'failed'; return { + ...state, jobStatus: nextJobStatus, setupStatus: nextSetupStatus, }; } case 'failedSetup': { return { + ...state, jobStatus: { ...state.jobStatus, 'log-entry-rate': 'failed', @@ -102,30 +117,40 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): setupStatus: 'failed', }; } + case 'fetchingModuleDefinition': case 'fetchingJobStatuses': { return { ...state, - setupStatus: 'initializing', + setupStatus: state.setupStatus === 'unknown' ? 'initializing' : state.setupStatus, }; } case 'fetchedJobStatuses': { - const { payload, spaceId, sourceId } = action; + const { payload: jobSummaries, spaceId, sourceId } = action; + const { jobDefinitions, setupStatus, sourceConfiguration } = state; + const nextJobStatus = { ...state.jobStatus, - 'log-entry-rate': getJobStatus(getJobId(spaceId, sourceId, 'log-entry-rate'))(payload), + 'log-entry-rate': getJobStatus(getJobId(spaceId, sourceId, 'log-entry-rate'))(jobSummaries), }; - const nextSetupStatus = Object.values(nextJobStatus).every(jobState => - ['started', 'finished'].includes(jobState) - ) - ? 'skipped' - : 'required'; + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + nextJobStatus, + jobDefinitions, + jobSummaries + )(setupStatus); + return { + ...state, + jobSummaries, jobStatus: nextJobStatus, setupStatus: nextSetupStatus, }; } case 'failedFetchingJobStatuses': { return { + ...state, setupStatus: 'unknown', jobStatus: { ...state.jobStatus, @@ -133,6 +158,56 @@ function statusReducer(state: StatusReducerState, action: StatusReducerAction): }, }; } + case 'fetchedModuleDefinition': { + const { spaceId, sourceId, moduleDefinition } = action; + const { jobStatus, jobSummaries, setupStatus, sourceConfiguration } = state; + + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + jobStatus, + moduleDefinition.jobs, + jobSummaries + )(setupStatus); + + return { + ...state, + jobDefinitions: moduleDefinition.jobs, + setupStatus: nextSetupStatus, + }; + } + case 'updatedSourceConfiguration': { + const { spaceId, sourceId, sourceConfiguration } = action; + const { jobDefinitions, jobStatus, jobSummaries, setupStatus } = state; + + const nextSetupStatus = getSetupStatus( + spaceId, + sourceId, + sourceConfiguration, + jobStatus, + jobDefinitions, + jobSummaries + )(setupStatus); + + return { + ...state, + setupStatus: nextSetupStatus, + sourceConfiguration, + }; + } + case 'requestedJobConfigurationUpdate': { + return { + ...state, + setupStatus: 'requiredForReconfiguration', + }; + } + case 'requestedJobDefinitionUpdate': { + return { + ...state, + setupStatus: 'requiredForUpdate', + }; + } case 'viewedResults': { return { ...state, @@ -194,6 +269,96 @@ const getJobStatus = (jobId: string) => (jobSummaries: FetchJobStatusResponsePay } )[0] || 'missing'; -export const useStatusState = () => { - return useReducer(statusReducer, initialState); +const getSetupStatus = ( + spaceId: string, + sourceId: string, + sourceConfiguration: JobSourceConfiguration, + everyJobStatus: Record, + jobDefinitions: JobDefinition[], + jobSummaries: JobSummary[] +) => (previousSetupStatus: SetupStatus) => + Object.entries(everyJobStatus).reduce((setupStatus, [jobType, jobStatus]) => { + if (!jobTypeRT.is(jobType)) { + return setupStatus; + } + + const jobId = getJobId(spaceId, sourceId, jobType); + const jobDefinition = jobDefinitions.find(({ id }) => id === jobType); + + if (jobStatus === 'missing') { + return 'required'; + } else if ( + setupStatus === 'required' || + setupStatus === 'requiredForUpdate' || + setupStatus === 'requiredForReconfiguration' + ) { + return setupStatus; + } else if ( + setupStatus === 'skippedButUpdatable' || + (jobDefinition && + !isJobRevisionCurrent(jobId, jobDefinition.config.custom_settings.job_revision || 0)( + jobSummaries + )) + ) { + return 'skippedButUpdatable'; + } else if ( + setupStatus === 'skippedButReconfigurable' || + !isJobConfigurationConsistent(jobId, sourceConfiguration)(jobSummaries) + ) { + return 'skippedButReconfigurable'; + } else if (setupStatus === 'hiddenAfterSuccess') { + return setupStatus; + } else if (setupStatus === 'skipped' || isJobStatusWithResults(jobStatus)) { + return 'skipped'; + } + + return setupStatus; + }, previousSetupStatus); + +const isJobRevisionCurrent = (jobId: string, currentRevision: number) => ( + jobSummaries: FetchJobStatusResponsePayload +): boolean => + jobSummaries + .filter(jobSummary => jobSummary.id === jobId) + .every( + jobSummary => + jobSummary.fullJob && + jobSummary.fullJob.custom_settings && + jobSummary.fullJob.custom_settings.job_revision && + jobSummary.fullJob.custom_settings.job_revision >= currentRevision + ); + +const isJobConfigurationConsistent = ( + jobId: string, + sourceConfiguration: { + bucketSpan: number; + indexPattern: string; + timestampField: string; + } +) => (jobSummaries: FetchJobStatusResponsePayload): boolean => + jobSummaries + .filter(jobSummary => jobSummary.id === jobId) + .every(jobSummary => { + if (!jobSummary.fullJob || !jobSummary.fullJob.custom_settings) { + return false; + } + + const jobConfiguration = jobSummary.fullJob.custom_settings.logs_source_config; + + return ( + jobConfiguration && + jobConfiguration.bucketSpan === sourceConfiguration.bucketSpan && + jobConfiguration.indexPattern === sourceConfiguration.indexPattern && + jobConfiguration.timestampField === sourceConfiguration.timestampField + ); + }); + +export const useStatusState = (sourceConfiguration: JobSourceConfiguration) => { + return useReducer(statusReducer, sourceConfiguration, createInitialState); }; + +interface JobSourceConfiguration { + bucketSpan: number; + indexPattern: string; + timestampField: string; +} diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx index bf3ac99a92bf7..2b83007e2ab99 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_content.tsx @@ -5,8 +5,9 @@ */ import { i18n } from '@kbn/i18n'; -import React, { useContext } from 'react'; +import React, { useContext, useEffect } from 'react'; +import { isSetupStatusWithResults } from '../../../../common/log_analysis'; import { LoadingPage } from '../../../components/loading_page'; import { LogAnalysisCapabilities, LogAnalysisJobs } from '../../../containers/logs/log_analysis'; import { Source } from '../../../containers/source'; @@ -19,10 +20,14 @@ export const AnalysisPageContent = () => { const { sourceId, source } = useContext(Source.Context); const { hasLogAnalysisCapabilites } = useContext(LogAnalysisCapabilities.Context); - const { setup, retry, setupStatus, viewResults, fetchJobStatus } = useContext( + const { setup, cleanupAndSetup, setupStatus, viewResults, fetchJobStatus } = useContext( LogAnalysisJobs.Context ); + useEffect(() => { + fetchJobStatus(); + }, []); + if (!hasLogAnalysisCapabilites) { return ; } else if (setupStatus === 'initializing') { @@ -35,7 +40,7 @@ export const AnalysisPageContent = () => { ); } else if (setupStatus === 'unknown') { return ; - } else if (setupStatus === 'skipped' || setupStatus === 'hiddenAfterSuccess') { + } else if (isSetupStatusWithResults(setupStatus)) { return ( { return ( { + fetchJobStatus(); + }, JOB_STATUS_POLLING_INTERVAL); + return ( <> {isLoading && !logEntryRate ? ( @@ -191,8 +207,12 @@ export const AnalysisResultsContent = ({ diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx index 120ae11b69f91..16da61bd1d9c1 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_setup_content.tsx @@ -18,24 +18,26 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import euiStyled from '../../../../../../common/eui_styled_components'; +import { SetupStatus } from '../../../../common/log_analysis'; import { useTrackPageview } from '../../../hooks/use_track_metric'; import { AnalysisSetupSteps } from './setup/steps'; -import { SetupStatus } from '../../../containers/logs/log_analysis'; + +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; interface AnalysisSetupContentProps { - setup: (startTime?: number | undefined, endTime?: number | undefined) => void; - retry: (startTime?: number | undefined, endTime?: number | undefined) => void; + cleanupAndSetup: SetupHandler; indexPattern: string; - viewResults: () => void; + setup: SetupHandler; setupStatus: SetupStatus; + viewResults: () => void; } export const AnalysisSetupContent: React.FunctionComponent = ({ - setup, + cleanupAndSetup, indexPattern, - viewResults, - retry, + setup, setupStatus, + viewResults, }) => { useTrackPageview({ app: 'infra_logs', path: 'analysis_setup' }); useTrackPageview({ app: 'infra_logs', path: 'analysis_setup', delay: 15000 }); @@ -70,7 +72,7 @@ export const AnalysisSetupContent: React.FunctionComponent void; + setupStatus: SetupStatus; timeRange: TimeRange; + viewSetupForReconfiguration: () => void; + viewSetupForUpdate: () => void; +}> = ({ + isLoading, + jobStatus, + results, + setTimeRange, + setupStatus, + timeRange, + viewSetupForReconfiguration, + viewSetupForUpdate, }) => { const title = i18n.translate('xpack.infra.logs.analysis.anomaliesSectionTitle', { defaultMessage: 'Anomalies', @@ -86,10 +98,29 @@ export const AnomaliesResults = ({ return ( <> - -

{title}

-
- + + + +

{title}

+
+
+ + + + + +
+ + + {isLoading ? ( @@ -187,18 +218,16 @@ const AnnotationTooltip: React.FunctionComponent<{ details: string }> = ({ detai {overallAnomalyScoreLabel}
    - {parsedDetails.anomalyScoresByPartition.map( - ({ partitionId, maximumAnomalyScore }, index) => { - return ( -
  • - - {`${partitionId}: `} - {maximumAnomalyScore} - -
  • - ); - } - )} + {parsedDetails.anomalyScoresByPartition.map(({ partitionId, maximumAnomalyScore }) => { + return ( +
  • + + {`${partitionId}: `} + {maximumAnomalyScore} + +
  • + ); + })}
); diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx new file mode 100644 index 0000000000000..0232c1167f194 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export const RecreateMLJobsButton: React.FunctionComponent<{ + onClick: () => void; +}> = ({ onClick }) => { + return ( + <> + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx index b33a609e19153..f755251def965 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/index.tsx @@ -4,32 +4,42 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +import { SetupStatus } from '../../../../../../common/log_analysis'; +import { useAnalysisSetupState } from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; import { InitialConfiguration } from './initial_configuration'; import { SetupProcess } from './setup_process'; -import { useAnalysisSetupState } from '../../../../../containers/logs/log_analysis/log_analysis_setup_state'; -import { SetupStatus } from '../../../../../containers/logs/log_analysis'; + +type SetupHandler = (startTime?: number | undefined, endTime?: number | undefined) => void; interface AnalysisSetupStepsProps { - setup: (startTime?: number | undefined, endTime?: number | undefined) => void; - retry: (startTime?: number | undefined, endTime?: number | undefined) => void; - viewResults: () => void; + cleanupAndSetup: SetupHandler; indexPattern: string; + setup: SetupHandler; setupStatus: SetupStatus; + viewResults: () => void; } export const AnalysisSetupSteps: React.FunctionComponent = ({ - setup: setupModule, - retry: retrySetup, - viewResults, + cleanupAndSetup: cleanupAndSetupModule, indexPattern, + setup: setupModule, setupStatus, + viewResults, }: AnalysisSetupStepsProps) => { - const { setup, retry, setStartTime, setEndTime, startTime, endTime } = useAnalysisSetupState({ + const { + setup, + cleanupAndSetup, + setStartTime, + setEndTime, + startTime, + endTime, + } = useAnalysisSetupState({ setupModule, - retrySetup, + cleanupAndSetupModule, }); const steps = [ @@ -55,7 +65,7 @@ export const AnalysisSetupSteps: React.FunctionComponent ), diff --git a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx index 806a049eb5d1e..7d3b602f906fb 100644 --- a/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx +++ b/x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/steps/setup_process.tsx @@ -4,23 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; import { - EuiLoadingSpinner, EuiButton, - EuiSpacer, EuiFlexGroup, EuiFlexItem, + EuiLoadingSpinner, + EuiSpacer, EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; + +import { SetupStatus } from '../../../../../../common/log_analysis'; import { CreateMLJobsButton } from '../create_ml_jobs_button'; -import { SetupStatus } from '../../../../../containers/logs/log_analysis'; +import { RecreateMLJobsButton } from '../recreate_ml_jobs_button'; interface Props { viewResults: () => void; setup: () => void; - retry: () => void; + cleanupAndSetup: () => void; indexPattern: string; setupStatus: SetupStatus; } @@ -28,7 +30,7 @@ interface Props { export const SetupProcess: React.FunctionComponent = ({ viewResults, setup, - retry, + cleanupAndSetup, indexPattern, setupStatus, }: Props) => { @@ -57,7 +59,7 @@ export const SetupProcess: React.FunctionComponent = ({ }} /> - + = ({ /> + ) : setupStatus === 'requiredForUpdate' || setupStatus === 'requiredForReconfiguration' ? ( + ) : ( )} diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index 83d6e273578d5..77d0d5a5305aa 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -288,6 +288,11 @@ describe('Lens App', () => { }); describe('save button', () => { + interface SaveProps { + newCopyOnSave: boolean; + newTitle: string; + } + function getButton(instance: ReactWrapper): TopNavMenuData { return (instance .find('[data-test-subj="lnsApp_topNav"]') @@ -296,6 +301,68 @@ describe('Lens App', () => { )!; } + function testSave(instance: ReactWrapper, saveProps: SaveProps) { + act(() => { + getButton(instance).run(instance.getDOMNode()); + }); + + instance.update(); + + const handler = instance.findWhere(el => el.prop('onSave')).prop('onSave') as (( + p: unknown + ) => void); + handler(saveProps); + } + + async function save({ + initialDocId, + ...saveProps + }: SaveProps & { + initialDocId?: string; + }) { + const args = { + ...makeDefaultArgs(), + docId: initialDocId, + }; + args.editorFrame = frame; + (args.docStorage.load as jest.Mock).mockResolvedValue({ + id: '1234', + state: { + query: 'fake query', + datasourceMetaData: { filterableIndexPatterns: [{ id: '1', title: 'saved' }] }, + }, + }); + (args.docStorage.save as jest.Mock).mockImplementation(async ({ id }) => ({ + id: id || 'aaa', + })); + + const instance = mount(); + + await waitForPromises(); + + if (initialDocId) { + expect(args.docStorage.load).toHaveBeenCalledTimes(1); + } else { + expect(args.docStorage.load).not.toHaveBeenCalled(); + } + + const onChange = frame.mount.mock.calls[0][1].onChange; + onChange({ + filterableIndexPatterns: [], + doc: ({ id: initialDocId } as unknown) as Document, + }); + + instance.update(); + + expect(getButton(instance).disableButton).toEqual(false); + + testSave(instance, saveProps); + + await waitForPromises(); + + return { args, instance }; + } + it('shows a disabled save button when the user does not have permissions', async () => { const args = makeDefaultArgs(); args.core.application = { @@ -335,37 +402,61 @@ describe('Lens App', () => { expect(getButton(instance).disableButton).toEqual(false); }); - it('saves the latest doc and then prevents more saving', async () => { - const args = makeDefaultArgs(); - args.editorFrame = frame; - (args.docStorage.save as jest.Mock).mockResolvedValue({ id: '1234' }); + it('saves new docs', async () => { + const { args, instance } = await save({ + initialDocId: undefined, + newCopyOnSave: false, + newTitle: 'hello there', + }); - const instance = mount(); + expect(args.docStorage.save).toHaveBeenCalledWith({ + id: undefined, + title: 'hello there', + }); - expect(frame.mount).toHaveBeenCalledTimes(1); + expect(args.redirectTo).toHaveBeenCalledWith('aaa'); - const onChange = frame.mount.mock.calls[0][1].onChange; - onChange({ filterableIndexPatterns: [], doc: ({ id: undefined } as unknown) as Document }); + instance.setProps({ docId: 'aaa' }); - instance.update(); + expect(args.docStorage.load).not.toHaveBeenCalled(); + }); - expect(getButton(instance).disableButton).toEqual(false); + it('saves the latest doc as a copy', async () => { + const { args, instance } = await save({ + initialDocId: '1234', + newCopyOnSave: true, + newTitle: 'hello there', + }); - act(() => { - getButton(instance).run(instance.getDOMNode()); + expect(args.docStorage.save).toHaveBeenCalledWith({ + id: undefined, + title: 'hello there', }); - expect(args.docStorage.save).toHaveBeenCalledWith({ id: undefined }); + expect(args.redirectTo).toHaveBeenCalledWith('aaa'); - await waitForPromises(); + instance.setProps({ docId: 'aaa' }); - expect(args.redirectTo).toHaveBeenCalledWith('1234'); + expect(args.docStorage.load).toHaveBeenCalledTimes(1); + }); - instance.setProps({ docId: '1234' }); + it('saves existing docs', async () => { + const { args, instance } = await save({ + initialDocId: '1234', + newCopyOnSave: false, + newTitle: 'hello there', + }); - expect(args.docStorage.load).not.toHaveBeenCalled(); + expect(args.docStorage.save).toHaveBeenCalledWith({ + id: '1234', + title: 'hello there', + }); - expect(getButton(instance).disableButton).toEqual(true); + expect(args.redirectTo).not.toHaveBeenCalled(); + + instance.setProps({ docId: '1234' }); + + expect(args.docStorage.load).toHaveBeenCalledTimes(1); }); it('handles save failure by showing a warning, but still allows another save', async () => { @@ -380,11 +471,8 @@ describe('Lens App', () => { instance.update(); - act(() => { - getButton(instance).run(instance.getDOMNode()); - }); + testSave(instance, { newCopyOnSave: false, newTitle: 'hello there' }); - await waitForPromises(); await waitForPromises(); expect(args.core.notifications.toasts.addDanger).toHaveBeenCalled(); diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx index 7208b37cd9f83..1152a3de77181 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.tsx @@ -5,12 +5,12 @@ */ import _ from 'lodash'; -import React, { useState, useEffect, useCallback, useRef } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { I18nProvider } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { Storage } from 'ui/storage'; import { DataPublicPluginStart } from 'src/plugins/data/public'; - +import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal'; import { CoreStart, NotificationsStart } from 'src/core/public'; import { DataStart, @@ -29,9 +29,10 @@ import { trackUiEvent } from '../lens_ui_telemetry'; interface State { isLoading: boolean; - isDirty: boolean; + isSaveModalVisible: boolean; indexPatternsForTopNav: IndexPatternInstance[]; persistedDoc?: Document; + lastKnownDoc?: Document; // Properties needed to interface with TopNav dateRange: { @@ -68,9 +69,8 @@ export function App({ const [state, setState] = useState({ isLoading: !!docId, - isDirty: false, + isSaveModalVisible: false, indexPatternsForTopNav: [], - query: { query: '', language }, dateRange: { fromDate: timeDefaults.from, @@ -79,7 +79,7 @@ export function App({ filters: [], }); - const lastKnownDocRef = useRef(undefined); + const { lastKnownDoc } = state; useEffect(() => { const subscription = dataShim.filter.filterManager.getUpdates$().subscribe({ @@ -127,6 +127,7 @@ export function App({ ...s, isLoading: false, persistedDoc: doc, + lastKnownDoc: doc, query: doc.state.query, filters: doc.state.filters, indexPatternsForTopNav: indexPatterns, @@ -142,7 +143,7 @@ export function App({ setState(s => ({ ...s, isLoading: false })); core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.editorFrame.docLoadingError', { + i18n.translate('xpack.lens.app.docLoadingError', { defaultMessage: 'Error loading saved document', }) ); @@ -152,10 +153,7 @@ export function App({ } }, [docId]); - // Can save if the frame has told us what it has, and there is either: - // a) No saved doc - // b) A saved doc that differs from the frame state - const isSaveable = state.isDirty && (core.application.capabilities.visualize.save as boolean); + const isSaveable = lastKnownDoc && core.application.capabilities.visualize.save; const onError = useCallback( (e: { message: string }) => @@ -180,33 +178,12 @@ export function App({ { - if (isSaveable && lastKnownDocRef.current) { - docStorage - .save(lastKnownDocRef.current) - .then(({ id }) => { - // Prevents unnecessary network request and disables save button - const newDoc = { ...lastKnownDocRef.current!, id }; - setState(s => ({ - ...s, - isDirty: false, - persistedDoc: newDoc, - })); - if (docId !== id) { - redirectTo(id); - } - }) - .catch(() => { - trackUiEvent('save_failed'); - core.notifications.toasts.addDanger( - i18n.translate('xpack.lens.editorFrame.docSavingError', { - defaultMessage: 'Error saving document', - }) - ); - }); + if (isSaveable && lastKnownDoc) { + setState(s => ({ ...s, isSaveModalVisible: true })); } }, testId: 'lnsApp_saveButton', @@ -292,10 +269,8 @@ export function App({ doc: state.persistedDoc, onError, onChange: ({ filterableIndexPatterns, doc }) => { - lastKnownDocRef.current = doc; - if (!_.isEqual(state.persistedDoc, doc)) { - setState(s => ({ ...s, isDirty: true })); + setState(s => ({ ...s, lastKnownDoc: doc })); } // Update the cached index patterns if the user made a change to any of them @@ -321,6 +296,49 @@ export function App({ /> )}
+ {lastKnownDoc && state.isSaveModalVisible && ( + { + const doc = { + ...lastKnownDoc, + id: props.newCopyOnSave ? undefined : lastKnownDoc.id, + title: props.newTitle, + }; + + docStorage + .save(doc) + .then(({ id }) => { + // Prevents unnecessary network request and disables save button + const newDoc = { ...doc, id }; + setState(s => ({ + ...s, + isSaveModalVisible: false, + persistedDoc: newDoc, + lastKnownDoc: newDoc, + })); + + if (docId !== id) { + redirectTo(id); + } + }) + .catch(() => { + trackUiEvent('save_failed'); + core.notifications.toasts.addDanger( + i18n.translate('xpack.lens.app.docSavingError', { + defaultMessage: 'Error saving document', + }) + ); + setState(s => ({ ...s, isSaveModalVisible: false })); + }); + }} + onClose={() => setState(s => ({ ...s, isSaveModalVisible: false }))} + title={lastKnownDoc.title || ''} + showCopyOnSave={true} + objectType={i18n.translate('xpack.lens.app.saveModalType', { + defaultMessage: 'Lens visualization', + })} + /> + )} ); @@ -335,7 +353,7 @@ export async function getAllIndexPatterns( return await Promise.all(ids.map(({ id }) => indexPatternsService.get(id))); } catch (e) { notifications.toasts.addDanger( - i18n.translate('xpack.lens.editorFrame.indexPatternLoadingError', { + i18n.translate('xpack.lens.app.indexPatternLoadingError', { defaultMessage: 'Error loading index patterns', }) ); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss index b8f5f95df8f08..7811df93ba8ce 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/_workspace_panel_wrapper.scss @@ -8,7 +8,8 @@ flex-direction: column; .lnsWorkspacePanelWrapper__pageContentHeader { - padding: $euiSizeS; + @include euiTitle('xs'); + padding: $euiSizeM; border-bottom: $euiBorderThin; // override EuiPage margin-bottom: 0 !important; // sass-lint:disable-line no-important @@ -31,11 +32,3 @@ } } } - -.lnsWorkspacePanelWrapper__titleInput { - @include euiTitle('xs'); - width: 100%; - max-width: none; - background: transparent; - box-shadow: none; -} diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx index 5d623fa86cd86..04c0b22c378d7 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/editor_frame.tsx @@ -259,7 +259,7 @@ export function EditorFrame(props: EditorFrameProps) { } workspacePanel={ allLoaded && ( - + ; children: React.ReactNode | React.ReactNode[]; } -export function WorkspacePanelWrapper({ children, title, dispatch }: Props) { +export function WorkspacePanelWrapper({ children, title }: Props) { return ( - dispatch({ type: 'UPDATE_TITLE', title: e.target.value })} - aria-label={i18n.translate('xpack.lens.chartTitleAriaLabel', { - defaultMessage: 'Title', - })} - /> + {title} {children} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss index 0f90d452de72e..4e60b8d4b7c4b 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss @@ -3,10 +3,6 @@ .euiCard__content { // sass-lint:disable-block no-important padding-top: 0 !important; - - .euiBetaBadge { - margin: 0 12px !important; - } } .euiCard__top + .euiCard__content { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js index 51c43f071817e..788b246be708b 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js @@ -11,10 +11,8 @@ import { EuiSpacer, EuiCard, EuiIcon, - EuiBetaBadge, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; import _ from 'lodash'; export function SourceSelect({ @@ -26,9 +24,7 @@ export function SourceSelect({ ? : null; - const sourceTitle = Source.isBeta - ?
{Source.title}{generateBetaBadge(Source.title)}
- : Source.title; + const sourceTitle = Source.title; return ( @@ -62,18 +58,3 @@ export function SourceSelect({ ); } - -function generateBetaBadge(appTitle) { - return ( - - ); -} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js index ba5993f28b360..cf876a59d0be4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js @@ -31,7 +31,6 @@ export class GeojsonFileSource extends AbstractVectorSource { }); static icon = 'importAction'; static isIndexingSource = true; - static isBeta = true; static layerDefaults = { applyGlobalQuery: DEFAULT_APPLY_GLOBAL_QUERY } diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts index d261832a92f2a..8ba20b3ec0048 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/ml_conditional_links/index.ts @@ -7,10 +7,10 @@ /* * These links are for different test scenarios that try and capture different drill downs into * ml-network and ml-hosts and are of the flavor of testing: - * A filter being null: (filterQuery:!n) - * A filter being set with single values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe%22%27,kind:kuery) - * A filter being set with multiple values: filterQuery:(expression:%27process.name%20:%20%22conhost.exe,sc.exe%22%27,kind:kuery) - * A filter containing variables not replaced: filterQuery:(expression:%27process.name%20:%20%$process.name$%22%27,kind:kuery) + * A filter being null: (query:!n) + * A filter being set with single values: query=(query:%27process.name%20:%20%22conhost.exe%22%27,language:kuery) + * A filter being set with multiple values: query=(query:%27process.name%20:%20%22conhost.exe,sc.exe%22%27,language:kuery) + * A filter containing variables not replaced: query=(query:%27process.name%20:%20%$process.name$%22%27,language:kuery) * * In different combination with: * network not being set: $ip$ @@ -23,54 +23,54 @@ * host having multiple values: suricata-iowa,siem-windows */ -// Single IP with a null for the filterQuery: -export const mlNetworkSingleIpNullFilterQuery = - "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// Single IP with a null for the Query: +export const mlNetworkSingleIpNullKqlQuery = + "/app/siem#/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// Single IP with a value for the filterQuery: -export const mlNetworkSingleIpFilterQuery = - "/app/siem#/ml-network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// Single IP with a value for the Query: +export const mlNetworkSingleIpKqlQuery = + "/app/siem#/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// Multiple IPs with a null for the filterQuery: -export const mlNetworkMultipleIpNullFilterQuery = - "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// Multiple IPs with a null for the Query: +export const mlNetworkMultipleIpNullKqlQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// Multiple IPs with a value for the filterQuery: -export const mlNetworkMultipleIpFilterQuery = - "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// Multiple IPs with a value for the Query: +export const mlNetworkMultipleIpKqlQuery = + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// $ip$ with a null filterQuery: -export const mlNetworkNullFilterQuery = - "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// $ip$ with a null Query: +export const mlNetworkNullKqlQuery = + "/app/siem#/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// $ip$ with a value for the filterQuery: -export const mlNetworkFilterQuery = - "/app/siem#/ml-network/ip/$ip$?kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; +// $ip$ with a value for the Query: +export const mlNetworkKqlQuery = + "/app/siem#/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; -// Single host name with a null for the filterQuery: -export const mlHostSingleHostNullFilterQuery = - "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Single host name with a null for the Query: +export const mlHostSingleHostNullKqlQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; -// Single host name with a variable in the filterQuery -export const mlHostSingleHostFilterQueryVariable = - "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22$process.name$%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Single host name with a variable in the Query: +export const mlHostSingleHostKqlQueryVariable = + "/app/siem#/ml-hosts/siem-windows?_g=()&query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; -// Single host name with a value for filterQuery: -export const mlHostSingleHostFilterQuery = - "/app/siem#/ml-hosts/siem-windows?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Single host name with a value for Query: +export const mlHostSingleHostKqlQuery = + "/app/siem#/ml-hosts/siem-windows?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; -// Multiple host names with null for filterQuery -export const mlHostMultiHostNullFilterQuery = - "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Multiple host names with null for Query: +export const mlHostMultiHostNullKqlQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; -// Multiple host names with a value for filterQuery -export const mlHostMultiHostFilterQuery = - "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Multiple host names with a value for Query: +export const mlHostMultiHostKqlQuery = + "/app/siem#/ml-hosts/siem-windows,siem-suricata?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name with a null for the KQL: -export const mlHostVariableHostNullFilterQuery = - "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +export const mlHostVariableHostNullKqlQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; -// Undefined/null host name but with a value for filterQuery -export const mlHostVariableHostFilterQuery = - "/app/siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:'process.name%20:%20%22conhost.exe,sc.exe%22',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; +// Undefined/null host name but with a value for Query: +export const mlHostVariableHostKqlQuery = + "/app/siem#/ml-hosts/$host.name$?_g=()&query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts index 68cfa507c1241..5c12bd528030e 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/url_state/index.ts @@ -31,14 +31,14 @@ export const ABSOLUTE_DATE_RANGE = { startTimeTimelineTyped: '2019-08-02 14:03:29.186', startTimeTyped: '2019-08-01 14:03:29.186', url: - '/app/siem#/network/?kqlQuery=(filterQuery:!n,queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', + '/app/siem#/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', urlUnlinked: - '/app/siem#/network/?kqlQuery=(filterQuery:!n,queryLocation:network.page)&timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', - urlKqlNetworkNetwork: `/app/siem#/network/?_g=()&kqlQuery=(filterQuery:(expression:'source.ip:%20"10.142.0.9"',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlNetworkHosts: `/app/siem#/network/?_g=()&kqlQuery=(filterQuery:(expression:'source.ip:%20"10.142.0.9"',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?_g=()&kqlQuery=(filterQuery:(expression:'source.ip:%20"10.142.0.9"',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsHosts: `/app/siem#/hosts/allHosts?_g=()&kqlQuery=(filterQuery:(expression:'source.ip:%20"10.142.0.9"',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + '/app/siem#/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', + urlKqlNetworkNetwork: `/app/siem#/network/?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlNetworkHosts: `/app/siem#/network/?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsNetwork: `/app/siem#/hosts/allHosts?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsHosts: `/app/siem#/hosts/allHosts?_g=()&query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlHost: '/app/siem#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', }; @@ -56,7 +56,7 @@ export const DATE_PICKER_APPLY_BUTTON = export const DATE_PICKER_APPLY_BUTTON_TIMELINE = '[data-test-subj="timeline-properties"] button[data-test-subj="superDatePickerApplyTimeButton"]'; export const DATE_PICKER_ABSOLUTE_INPUT = '[data-test-subj="superDatePickerAbsoluteDateInput"]'; -export const KQL_INPUT = '[data-test-subj="kqlInput"]'; +export const KQL_INPUT = '[data-test-subj="queryInput"]'; export const TIMELINE_TITLE = '[data-test-subj="timeline-title"]'; export const HOST_DETAIL_SIEM_KIBANA = '[data-test-subj="table-allHosts-loading-false"] a.euiLink'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts index 5eaf396ed3437..5485942c0f624 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/ml_conditional_links/ml_conditional_links.spec.ts @@ -6,19 +6,19 @@ import { logout } from '../../lib/logout'; import { - mlNetworkSingleIpNullFilterQuery, - mlNetworkSingleIpFilterQuery, - mlNetworkMultipleIpNullFilterQuery, - mlNetworkMultipleIpFilterQuery, - mlNetworkNullFilterQuery, - mlNetworkFilterQuery, - mlHostSingleHostNullFilterQuery, - mlHostSingleHostFilterQueryVariable, - mlHostSingleHostFilterQuery, - mlHostMultiHostNullFilterQuery, - mlHostMultiHostFilterQuery, - mlHostVariableHostNullFilterQuery, - mlHostVariableHostFilterQuery, + mlNetworkSingleIpNullKqlQuery, + mlNetworkSingleIpKqlQuery, + mlNetworkMultipleIpNullKqlQuery, + mlNetworkMultipleIpKqlQuery, + mlNetworkNullKqlQuery, + mlNetworkKqlQuery, + mlHostSingleHostNullKqlQuery, + mlHostSingleHostKqlQueryVariable, + mlHostSingleHostKqlQuery, + mlHostMultiHostNullKqlQuery, + mlHostMultiHostKqlQuery, + mlHostVariableHostNullKqlQuery, + mlHostVariableHostKqlQuery, } from '../../lib/ml_conditional_links'; import { loginAndWaitForPage } from '../../lib/util/helpers'; import { KQL_INPUT } from '../../lib/url_state'; @@ -28,8 +28,8 @@ describe('ml conditional links', () => { return logout(); }); - it('sets the KQL from a single IP with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + it('sets the KQL from a single IP with a value for the query', () => { + loginAndWaitForPage(mlNetworkSingleIpKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -37,8 +37,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a multiple IPs with a null for the filterQuery', () => { - loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + it('sets the KQL from a multiple IPs with a null for the query', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -46,8 +46,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a multiple IPs with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + it('sets the KQL from a multiple IPs with a value for the query', () => { + loginAndWaitForPage(mlNetworkMultipleIpKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -55,8 +55,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a $ip$ with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkFilterQuery); + it('sets the KQL from a $ip$ with a value for the query', () => { + loginAndWaitForPage(mlNetworkKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -64,8 +64,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a single host name with a value for filterQuery', () => { - loginAndWaitForPage(mlHostSingleHostFilterQuery); + it('sets the KQL from a single host name with a value for query', () => { + loginAndWaitForPage(mlHostSingleHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -73,8 +73,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a multiple host names with null for filterQuery', () => { - loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + it('sets the KQL from a multiple host names with null for query', () => { + loginAndWaitForPage(mlHostMultiHostNullKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -82,8 +82,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a multiple host names with a value for filterQuery', () => { - loginAndWaitForPage(mlHostMultiHostFilterQuery); + it('sets the KQL from a multiple host names with a value for query', () => { + loginAndWaitForPage(mlHostMultiHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -91,8 +91,8 @@ describe('ml conditional links', () => { ); }); - it('sets the KQL from a undefined/null host name but with a value for filterQuery', () => { - loginAndWaitForPage(mlHostVariableHostFilterQuery); + it('sets the KQL from a undefined/null host name but with a value for query', () => { + loginAndWaitForPage(mlHostVariableHostKqlQuery); cy.get(KQL_INPUT, { timeout: 5000 }).should( 'have.attr', 'value', @@ -100,107 +100,107 @@ describe('ml conditional links', () => { ); }); - it('redirects from a single IP with a null for the filterQuery', () => { - loginAndWaitForPage(mlNetworkSingleIpNullFilterQuery); + it('redirects from a single IP with a null for the query', () => { + loginAndWaitForPage(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', '/app/siem#/network/ip/127.0.0.1?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); - it('redirects from a single IP with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkSingleIpFilterQuery); + it('redirects from a single IP with a value for the query', () => { + loginAndWaitForPage(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - "/app/siem#/network/ip/127.0.0.1?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network/ip/127.0.0.1?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); - it('redirects from a multiple IPs with a null for the filterQuery', () => { - loginAndWaitForPage(mlNetworkMultipleIpNullFilterQuery); + it('redirects from a multiple IPs with a null for the query', () => { + loginAndWaitForPage(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - "/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))'),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); - it('redirects from a multiple IPs with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkMultipleIpFilterQuery); + it('redirects from a multiple IPs with a value for the query', () => { + loginAndWaitForPage(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - "/app/siem#/network?kqlQuery=(filterQuery:(expression:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); - it('redirects from a $ip$ with a null filterQuery', () => { - loginAndWaitForPage(mlNetworkNullFilterQuery); + it('redirects from a $ip$ with a null query', () => { + loginAndWaitForPage(mlNetworkNullKqlQuery); cy.url().should( 'include', '/app/siem#/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); - it('redirects from a $ip$ with a value for the filterQuery', () => { - loginAndWaitForPage(mlNetworkFilterQuery); + it('redirects from a $ip$ with a value for the query', () => { + loginAndWaitForPage(mlNetworkKqlQuery); cy.url().should( 'include', - "/app/siem#/network?kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/siem#/network?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); - it('redirects from a single host name with a null for the filterQuery', () => { - loginAndWaitForPage(mlHostSingleHostNullFilterQuery); + it('redirects from a single host name with a null for the query', () => { + loginAndWaitForPage(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', '/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); - it('redirects from a host name with a variable in the filterQuery', () => { - loginAndWaitForPage(mlHostSingleHostFilterQueryVariable); + it('redirects from a host name with a variable in the query', () => { + loginAndWaitForPage(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', '/app/siem#/hosts/siem-windows/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); - it('redirects from a single host name with a value for filterQuery', () => { - loginAndWaitForPage(mlHostSingleHostFilterQuery); + it('redirects from a single host name with a value for query', () => { + loginAndWaitForPage(mlHostSingleHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/siem-windows/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/siem-windows/anomalies?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); - it('redirects from a multiple host names with null for filterQuery', () => { - loginAndWaitForPage(mlHostMultiHostNullFilterQuery); + it('redirects from a multiple host names with null for query', () => { + loginAndWaitForPage(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)'),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); - it('redirects from a multiple host names with a value for filterQuery', () => { - loginAndWaitForPage(mlHostMultiHostFilterQuery); + it('redirects from a multiple host names with a value for query', () => { + loginAndWaitForPage(mlHostMultiHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); it('redirects from a undefined/null host name with a null for the KQL', () => { - loginAndWaitForPage(mlHostVariableHostNullFilterQuery); + loginAndWaitForPage(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', '/app/siem#/hosts/anomalies?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); - it('redirects from a undefined/null host name but with a value for filterQuery', () => { - loginAndWaitForPage(mlHostVariableHostFilterQuery); + it('redirects from a undefined/null host name but with a value for query', () => { + loginAndWaitForPage(mlHostVariableHostKqlQuery); cy.url().should( 'include', - "/app/siem#/hosts/anomalies?_g=()&kqlQuery=(filterQuery:(expression:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/siem#/hosts/anomalies?_g=()&query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); }); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts index 28891f3be71e5..63104d9804b6e 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/url_state/url_state.spec.ts @@ -51,7 +51,7 @@ describe('url state', () => { ); }); - it('sets the url state when start and end date are set', () => { + it.skip('sets the url state when start and end date are set', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url); cy.get(DATE_PICKER_START_DATE_POPOVER_BUTTON).click({ force: true }); @@ -127,7 +127,7 @@ describe('url state', () => { ); }); - it('sets the url state when timeline/global date pickers are unlinked and timeline start and end date are set', () => { + it.skip('sets the url state when timeline/global date pickers are unlinked and timeline start and end date are set', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlUnlinked); toggleTimelineVisibility(); @@ -165,33 +165,20 @@ describe('url state', () => { ); }); - it('sets kql on network page when queryLocation == network.page', () => { + it('sets kql on network page', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlNetworkNetwork); cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"'); }); - it('does not set kql on network page when queryLocation != network.page', () => { - loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlNetworkHosts); - cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', ''); - }); - - it('sets kql on hosts page when queryLocation == hosts.page', () => { + it('sets kql on hosts page', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts); cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"'); }); - it('does not set kql on hosts page when queryLocation != hosts.page', () => { - loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsNetwork); - cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', ''); - }); - it('sets the url state when kql is set', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.url); cy.get(KQL_INPUT, { timeout: 5000 }).type('source.ip: "10.142.0.9" {enter}'); - cy.url().should( - 'include', - `kqlQuery=(filterQuery:(expression:'source.ip:%20%2210.142.0.9%22%20',kind:kuery),queryLocation:network.page)` - ); + cy.url().should('include', `query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')`); }); it('sets the url state when kql is set and check if href reflect this change', () => { @@ -203,7 +190,7 @@ describe('url state', () => { cy.get(NAVIGATION_NETWORK).should( 'have.attr', 'href', - "#/link-to/network?kqlQuery=(filterQuery:(expression:'source.ip:%20%2210.142.0.9%22%20',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "#/link-to/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); }); @@ -214,49 +201,50 @@ describe('url state', () => { .first() .click({ force: true }); waitForAllHostsWidget(); - cy.get(HOST_DETAIL_SIEM_KIBANA, { timeout: 5000 }) - .first() - .invoke('text') - .should('eq', 'siem-kibana'); - cy.get(HOST_DETAIL_SIEM_KIBANA) - .first() - .click({ force: true }); - cy.get(KQL_INPUT, { timeout: 5000 }).type('agent.type: "auditbeat" {enter}'); cy.get(NAVIGATION_HOSTS).should( 'have.attr', 'href', - "#/link-to/hosts?kqlQuery=(filterQuery:(expression:'host.name:%20%22siem-kibana%22%20',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "#/link-to/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); cy.get(NAVIGATION_NETWORK).should( 'have.attr', 'href', - '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))' + "#/link-to/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); + cy.get(HOST_DETAIL_SIEM_KIBANA, { timeout: 5000 }) + .first() + .invoke('text') + .should('eq', 'siem-kibana'); + cy.get(HOST_DETAIL_SIEM_KIBANA) + .first() + .click({ force: true }); + cy.get(KQL_INPUT, { timeout: 5000 }).clear(); + cy.get(KQL_INPUT, { timeout: 5000 }).type('agent.type: "auditbeat" {enter}'); cy.get(NAVIGATION_HOSTS_ANOMALIES).should( 'have.attr', 'href', - "#/hosts/siem-kibana/anomalies?kqlQuery=(filterQuery:(expression:'agent.type:%20%22auditbeat%22%20',kind:kuery),queryLocation:hosts.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "#/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); cy.get(BREADCRUMBS) .eq(1) .should( 'have.attr', 'href', - "#/link-to/hosts?kqlQuery=(filterQuery:(expression:'host.name:%20%22siem-kibana%22%20',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "#/link-to/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - "#/link-to/hosts/siem-kibana?kqlQuery=(filterQuery:(expression:'agent.type:%20%22auditbeat%22%20',kind:kuery),queryLocation:hosts.details)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "#/link-to/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); }); - it('clears kql when navigating to a new page', () => { + it('Do not clears kql when navigating to a new page', () => { loginAndWaitForPage(ABSOLUTE_DATE_RANGE.urlKqlHostsHosts); cy.get(NAVIGATION_NETWORK).click({ force: true }); - cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', ''); + cy.get(KQL_INPUT, { timeout: 5000 }).should('have.attr', 'value', 'source.ip: "10.142.0.9"'); }); it('sets and reads the url state for timeline by id', () => { @@ -266,7 +254,7 @@ describe('url state', () => { assertAtLeastOneEventMatchesSearch(); const bestTimelineName = 'The Best Timeline'; cy.get(TIMELINE_TITLE, { timeout: 5000 }).type(bestTimelineName); - cy.url().should('include', 'timelineId='); + cy.url().should('include', 'timeline='); cy.visit( `/app/siem#/timelines?timerange=(global:(linkTo:!(),timerange:(from:1565274377369,kind:absolute,to:1565360777369)),timeline:(linkTo:!(),timerange:(from:1565274377369,kind:absolute,to:1565360777369)))` ).then(() => cy.get(TIMELINE_TITLE).should('have.attr', 'value', bestTimelineName)); diff --git a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js index b15124ed2ba8c..35c12c7f55246 100644 --- a/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js +++ b/x-pack/legacy/plugins/siem/dev_tools/circular_deps/run_check_circular_deps_cli.js @@ -18,11 +18,12 @@ run( }); const circularFound = result.circular(); - if (circularFound.length !== 0) { + // We can only care about SIEM code, we should not be penalyze for others + if (circularFound.filter(cf => cf.includes('siem')).length !== 0) { throw createFailError( 'SIEM circular dependencies of imports has been found:' + - '\n - ' + - circularFound.join('\n - ') + '\n - ' + + circularFound.join('\n - ') ); } else { log.success('No circular deps 👍'); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts index 148efa3182de2..1019bfd172846 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/__mocks__/mock.ts @@ -31,19 +31,30 @@ export const mockSourceLayer = { style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#3185FC' } }, - lineColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, - lineWidth: { type: 'STATIC', options: { size: 1 } }, - iconSize: { type: 'STATIC', options: { size: 6 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'arrow-es' } }, + fillColor: { + type: 'STATIC', + options: { color: '#3185FC' }, + }, + lineColor: { + type: 'STATIC', + options: { color: '#FFFFFF' }, + }, + lineWidth: { type: 'STATIC', options: { size: 2 } }, + iconSize: { type: 'STATIC', options: { size: 8 } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'icon', symbolId: 'home' }, + }, }, }, id: 'uuid.v4()', label: `filebeat-* | Source Point`, minZoom: 0, maxZoom: 24, - alpha: 0.75, + alpha: 1, visible: true, applyGlobalQuery: true, type: 'VECTOR', @@ -72,19 +83,30 @@ export const mockDestinationLayer = { style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#DB1374' } }, - lineColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, - lineWidth: { type: 'STATIC', options: { size: 1 } }, - iconSize: { type: 'STATIC', options: { size: 6 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'airfield' } }, + fillColor: { + type: 'STATIC', + options: { color: '#DB1374' }, + }, + lineColor: { + type: 'STATIC', + options: { color: '#FFFFFF' }, + }, + lineWidth: { type: 'STATIC', options: { size: 2 } }, + iconSize: { type: 'STATIC', options: { size: 8 } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'icon', symbolId: 'marker' }, + }, }, }, id: 'uuid.v4()', label: `filebeat-* | Destination Point`, minZoom: 0, maxZoom: 24, - alpha: 0.75, + alpha: 1, visible: true, applyGlobalQuery: true, type: 'VECTOR', @@ -106,33 +128,41 @@ export const mockLineLayer = { style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#e6194b' } }, + fillColor: { + type: 'STATIC', + options: { color: '#1EA593' }, + }, lineColor: { - type: 'DYNAMIC', - options: { - color: 'Green to Red', - field: { label: 'count', name: 'doc_count', origin: 'source' }, - useCustomColorRamp: false, - }, + type: 'STATIC', + options: { color: '#3185FC' }, }, lineWidth: { type: 'DYNAMIC', options: { + field: { + label: 'count', + name: 'doc_count', + origin: 'source', + }, minSize: 1, - maxSize: 4, - field: { label: 'count', name: 'doc_count', origin: 'source' }, + maxSize: 8, }, }, iconSize: { type: 'STATIC', options: { size: 10 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'airfield' } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'circle', symbolId: 'airfield' }, + }, }, }, id: 'uuid.v4()', label: `filebeat-* | Line`, minZoom: 0, maxZoom: 24, - alpha: 1, + alpha: 0.5, visible: true, applyGlobalQuery: true, type: 'VECTOR', diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx index 66636766f9872..bbbbd91513831 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.test.tsx @@ -3,15 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { Filter } from '@kbn/es-query'; import { get } from 'lodash/fp'; -import { - ApplySiemFilterAction, - getExpressionFromArray, - getFilterExpression, - ActionContext, -} from './apply_siem_filter_action'; +import { ApplySiemFilterAction, ActionContext } from './apply_siem_filter_action'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/common/constants'; import { @@ -19,7 +14,20 @@ import { EmbeddableOutput, IEmbeddable, } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { Filter } from '@kbn/es-query'; + +import { siemFilterManager } from '../../search_bar'; + +interface MockSiemFilterManager { + addFilters: (filters: Filter[]) => void; +} +const mockSiemFilterManager: MockSiemFilterManager = siemFilterManager as MockSiemFilterManager; +jest.mock('../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); +const mockAddFilters = jest.fn(); +mockSiemFilterManager.addFilters = mockAddFilters; // Using type narrowing to remove all the any's -- https://github.com/elastic/kibana/pull/43965/files#r318796100 const isEmbeddable = ( @@ -41,26 +49,20 @@ const isPartialFilterAction = (embeddable: unknown): embeddable is PartialAction }; describe('ApplySiemFilterAction', () => { - let applyFilterQueryFromKueryExpression: (expression: string) => void; - - beforeEach(() => { - applyFilterQueryFromKueryExpression = jest.fn(expression => {}); - }); - test('it has APPLY_SIEM_FILTER_ACTION_ID type and id', () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); expect(action.id).toBe('APPLY_SIEM_FILTER_ACTION_ID'); expect(action.type).toBe('APPLY_SIEM_FILTER_ACTION_ID'); }); test('it has expected display name', () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); expect(action.getDisplayName()).toMatchInlineSnapshot(`"Apply filter"`); }); describe('#isCompatible', () => { test('when embeddable type is MAP_SAVED_OBJECT_TYPE and filters exist, returns true', async () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); const embeddable = { type: MAP_SAVED_OBJECT_TYPE, }; @@ -76,7 +78,7 @@ describe('ApplySiemFilterAction', () => { }); test('when embeddable type is MAP_SAVED_OBJECT_TYPE and filters do not exist, returns false', async () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); const embeddable = { type: MAP_SAVED_OBJECT_TYPE, }; @@ -91,7 +93,7 @@ describe('ApplySiemFilterAction', () => { }); test('when embeddable type is not MAP_SAVED_OBJECT_TYPE, returns false', async () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); const embeddable = { type: 'defaultEmbeddable', }; @@ -109,9 +111,7 @@ describe('ApplySiemFilterAction', () => { describe('#execute', () => { test('it throws an error when triggerContext not set', async () => { - const action = new ApplySiemFilterAction({ - applyFilterQueryFromKueryExpression, - }); + const action = new ApplySiemFilterAction(); const embeddable = { type: MAP_SAVED_OBJECT_TYPE, }; @@ -127,7 +127,7 @@ describe('ApplySiemFilterAction', () => { }); test('it calls applyFilterQueryFromKueryExpression() with valid expression', async () => { - const action = new ApplySiemFilterAction({ applyFilterQueryFromKueryExpression }); + const action = new ApplySiemFilterAction(); const embeddable = { type: MAP_SAVED_OBJECT_TYPE, getInput: () => ({ @@ -157,47 +157,17 @@ describe('ApplySiemFilterAction', () => { filters, }); - expect( - (applyFilterQueryFromKueryExpression as jest.Mock<(expression: string) => void>).mock - .calls[0][0] - ).toBe('host.name: "zeek-newyork-sha-aa8df15"'); + expect(mockAddFilters.mock.calls[0][0]).toEqual([ + { + meta: { alias: '', disabled: false, negate: false }, + query: { + match: { 'host.name': { query: 'zeek-newyork-sha-aa8df15', type: 'phrase' } }, + }, + }, + ]); } else { throw new Error('Invalid embeddable in unit test'); } }); }); }); - -describe('#getFilterExpression', () => { - test('it returns an empty expression if no filterValue is provided', () => { - const layerList = getFilterExpression('host.id', undefined); - expect(layerList).toEqual('(NOT host.id:*)'); - }); - - test('it returns a valid expression when provided single filterValue', () => { - const layerList = getFilterExpression('host.id', 'aa8df15'); - expect(layerList).toEqual('host.id: "aa8df15"'); - }); - - test('it returns a valid expression when provided array filterValue', () => { - const layerList = getFilterExpression('host.id', ['xavier', 'angela', 'frank']); - expect(layerList).toEqual('(host.id: "xavier" OR host.id: "angela" OR host.id: "frank")'); - }); - - test('it returns a valid expression when provided array filterValue with a single value', () => { - const layerList = getFilterExpression('host.id', ['xavier']); - expect(layerList).toEqual('(host.id: "xavier")'); - }); -}); - -describe('#getExpressionFromArray', () => { - test('it returns an empty expression if no filterValues are provided', () => { - const layerList = getExpressionFromArray('host.id', []); - expect(layerList).toEqual(''); - }); - - test('it returns a valid expression when provided multiple filterValues', () => { - const layerList = getExpressionFromArray('host.id', ['xavier', 'angela', 'frank']); - expect(layerList).toEqual('(host.id: "xavier" OR host.id: "angela" OR host.id: "frank")'); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx index 8ee016f12deed..52368be60c516 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/actions/apply_siem_filter_action.tsx @@ -5,12 +5,12 @@ */ import { Filter } from '@kbn/es-query'; -import { getOr } from 'lodash/fp'; import { i18n } from '@kbn/i18n'; import { IAction } from 'src/plugins/ui_actions/public'; import { IEmbeddable } from '../../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../../maps/common/constants'; +import { siemFilterManager } from '../../search_bar'; export const APPLY_SIEM_FILTER_ACTION_ID = 'APPLY_SIEM_FILTER_ACTION_ID'; @@ -21,17 +21,8 @@ export interface ActionContext { export class ApplySiemFilterAction implements IAction { public readonly type = APPLY_SIEM_FILTER_ACTION_ID; - private readonly applyFilterQueryFromKueryExpression: (expression: string) => void; public id = APPLY_SIEM_FILTER_ACTION_ID; - constructor({ - applyFilterQueryFromKueryExpression, - }: { - applyFilterQueryFromKueryExpression: (filterQuery: string) => void; - }) { - this.applyFilterQueryFromKueryExpression = applyFilterQueryFromKueryExpression; - } - public getDisplayName() { return i18n.translate('xpack.siem.components.embeddables.actions.applySiemFilterActionTitle', { defaultMessage: 'Apply filter', @@ -50,37 +41,6 @@ export class ApplySiemFilterAction implements IAction { if (!filters) { throw new TypeError('Applying a filter requires a filter as context'); } - - // Parse queryExpression from queryDSL and apply to SIEM global KQL Bar via redux - const filterObject = getOr(null, '[0].query.match', filters); - - if (filterObject != null) { - const filterQuery = getOr('', 'query.query', embeddable.getInput()); - const filterKey = Object.keys(filterObject)[0]; - - const filterExpression = getFilterExpression(filterKey, filterObject[filterKey].query); - - this.applyFilterQueryFromKueryExpression( - filterQuery.length > 0 ? `${filterQuery} and ${filterExpression}` : filterExpression - ); - } + siemFilterManager.addFilters(filters); } } - -export const getFilterExpression = ( - filterKey: string, - filterValue: string | string[] | undefined -): string => { - if (Array.isArray(filterValue)) { - return getExpressionFromArray(filterKey, filterValue); - } else if (filterValue != null) { - return `${filterKey}: "${filterValue}"`; - } else { - return `(NOT ${filterKey}:*)`; - } -}; - -export const getExpressionFromArray = (filterKey: string, filterValues: string[]): string => - filterValues.length > 0 - ? `(${filterValues.map(filterValue => `${filterKey}: "${filterValue}"`).join(' OR ')})` - : ''; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx index 83253f6e4de4b..26755ecd4b5f6 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx @@ -10,6 +10,12 @@ import * as React from 'react'; import { EmbeddedMap } from './embedded_map'; import { SetQuery } from './types'; +jest.mock('../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); + jest.mock('ui/new_platform', () => ({ npStart: { core: { @@ -34,22 +40,20 @@ jest.mock('ui/new_platform', () => ({ })); describe('EmbeddedMap', () => { - let applyFilterQueryFromKueryExpression: (expression: string) => void; let setQuery: SetQuery; beforeEach(() => { - applyFilterQueryFromKueryExpression = jest.fn(expression => {}); setQuery = jest.fn(); }); test('renders correctly against snapshot', () => { const wrapper = shallow( ); expect(toJson(wrapper)).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index 0a29845b4f64c..b66d3f7055a4e 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -5,10 +5,12 @@ */ import { EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import { Filter } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; import { npStart } from 'ui/new_platform'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; import { createPortalNode, InPortal } from 'react-reverse-portal'; +import { Query } from 'src/plugins/data/common'; import styled from 'styled-components'; import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; @@ -37,15 +39,15 @@ const EmbeddableWrapper = styled(EuiFlexGroup)` `; export interface EmbeddedMapProps { - applyFilterQueryFromKueryExpression: (expression: string) => void; - queryExpression: string; + query: Query; + filters: Filter[]; startDate: number; endDate: number; setQuery: SetQuery; } export const EmbeddedMap = React.memo( - ({ applyFilterQueryFromKueryExpression, endDate, queryExpression, setQuery, startDate }) => { + ({ endDate, filters, query, setQuery, startDate }) => { const [embeddable, setEmbeddable] = React.useState(null); const [isLoading, setIsLoading] = useState(true); const [isError, setIsError] = useState(false); @@ -67,7 +69,7 @@ export const EmbeddedMap = React.memo( async function setupEmbeddable() { // Configure Embeddables API try { - setupEmbeddablesAPI(applyFilterQueryFromKueryExpression); + setupEmbeddablesAPI(); } catch (e) { displayErrorToast(i18n.ERROR_CONFIGURING_EMBEDDABLES_API, e.message, dispatchToaster); setIsLoading(false); @@ -88,8 +90,9 @@ export const EmbeddedMap = React.memo( // Create & set Embeddable try { const embeddableObject = await createEmbeddable( + filters, getIndexPatternTitleIdMapping(matchingIndexPatterns), - queryExpression, + query, startDate, endDate, setQuery, @@ -119,11 +122,16 @@ export const EmbeddedMap = React.memo( // queryExpression updated useEffect useEffect(() => { - if (embeddable != null && queryExpression != null) { - const query = { query: queryExpression, language: 'kuery' }; + if (embeddable != null) { embeddable.updateInput({ query }); } - }, [queryExpression]); + }, [query]); + + useEffect(() => { + if (embeddable != null) { + embeddable.updateInput({ filters }); + } + }, [filters]); // DateRange updated useEffect useEffect(() => { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx index 40c73337e2e41..83c5113841114 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.test.tsx @@ -51,8 +51,7 @@ describe('embedded_map_helpers', () => { describe('setupEmbeddablesAPI', () => { test('attaches SIEM_FILTER_ACTION, and detaches extra UI actions', () => { - const applyFilterMock = jest.fn(); - setupEmbeddablesAPI(applyFilterMock); + setupEmbeddablesAPI(); expect(npStart.plugins.uiActions.registerAction).toHaveBeenCalledTimes(1); expect(npStart.plugins.uiActions.detachAction).toHaveBeenCalledTimes(3); }); @@ -61,13 +60,29 @@ describe('embedded_map_helpers', () => { describe('createEmbeddable', () => { test('attaches refresh action', async () => { const setQueryMock = jest.fn(); - await createEmbeddable([], '', 0, 0, setQueryMock, createPortalNode()); + await createEmbeddable( + [], + [], + { query: '', language: 'kuery' }, + 0, + 0, + setQueryMock, + createPortalNode() + ); expect(setQueryMock).toHaveBeenCalledTimes(1); }); test('attaches refresh action with correct reference', async () => { const setQueryMock = jest.fn(({ id, inspect, loading, refetch }) => refetch); - const embeddable = await createEmbeddable([], '', 0, 0, setQueryMock, createPortalNode()); + const embeddable = await createEmbeddable( + [], + [], + { query: '', language: 'kuery' }, + 0, + 0, + setQueryMock, + createPortalNode() + ); expect(setQueryMock.mock.calls[0][0].refetch).not.toBe(embeddable.reload); setQueryMock.mock.results[0].value(); expect(embeddable.reload).toHaveBeenCalledTimes(1); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index 4a8d984a3ed72..bd14e65f0980b 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import uuid from 'uuid'; import React from 'react'; import { npStart } from 'ui/new_platform'; import { OutPortal, PortalNode } from 'react-reverse-portal'; +import { Query } from 'src/plugins/data/common'; + import { ActionToaster, AppToast } from '../toasters'; import { start } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { @@ -60,16 +63,12 @@ export const displayErrorToast = ( * * @throws Error if action is already registered */ -export const setupEmbeddablesAPI = ( - applyFilterQueryFromKueryExpression: (expression: string) => void -) => { +export const setupEmbeddablesAPI = () => { try { const actions = npStart.plugins.uiActions.getTriggerActions(APPLY_FILTER_TRIGGER); const actionLoaded = actions.some(a => a.id === APPLY_SIEM_FILTER_ACTION_ID); if (!actionLoaded) { - const siemFilterAction = new ApplySiemFilterAction({ - applyFilterQueryFromKueryExpression, - }); + const siemFilterAction = new ApplySiemFilterAction(); npStart.plugins.uiActions.registerAction(siemFilterAction); npStart.plugins.uiActions.attachAction(APPLY_FILTER_TRIGGER, siemFilterAction.id); @@ -86,7 +85,7 @@ export const setupEmbeddablesAPI = ( * Creates MapEmbeddable with provided initial configuration * * @param indexPatterns list of index patterns to configure layers for - * @param queryExpression initial query constraints as an expression + * @param query initial query constraints as Query * @param startDate * @param endDate * @param setQuery function as provided by the GlobalTime component for reacting to refresh @@ -95,8 +94,9 @@ export const setupEmbeddablesAPI = ( * @throws Error if EmbeddableFactory does not exist */ export const createEmbeddable = async ( + filters: Filter[], indexPatterns: IndexPatternMapping[], - queryExpression: string, + query: Query, startDate: number, endDate: number, setQuery: SetQuery, @@ -110,9 +110,9 @@ export const createEmbeddable = async ( }; const input = { id: uuid.v4(), - filters: [], + filters, hidePanelTitles: true, - query: { query: queryExpression, language: 'kuery' }, + query, refreshConfig: { value: 0, pause: true }, timeRange: { from: new Date(startDate).toISOString(), diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts index 11043c3c5bb88..e1d3a4aa62938 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_config.ts @@ -87,19 +87,30 @@ export const getSourceLayer = (indexPatternTitle: string, indexPatternId: string style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#3185FC' } }, - lineColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, - lineWidth: { type: 'STATIC', options: { size: 1 } }, - iconSize: { type: 'STATIC', options: { size: 6 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'arrow-es' } }, + fillColor: { + type: 'STATIC', + options: { color: '#3185FC' }, + }, + lineColor: { + type: 'STATIC', + options: { color: '#FFFFFF' }, + }, + lineWidth: { type: 'STATIC', options: { size: 2 } }, + iconSize: { type: 'STATIC', options: { size: 8 } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'icon', symbolId: 'home' }, + }, }, }, id: uuid.v4(), label: `${indexPatternTitle} | ${i18n.SOURCE_LAYER}`, minZoom: 0, maxZoom: 24, - alpha: 0.75, + alpha: 1, visible: true, applyGlobalQuery: true, type: 'VECTOR', @@ -129,19 +140,30 @@ export const getDestinationLayer = (indexPatternTitle: string, indexPatternId: s style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#DB1374' } }, - lineColor: { type: 'STATIC', options: { color: '#FFFFFF' } }, - lineWidth: { type: 'STATIC', options: { size: 1 } }, - iconSize: { type: 'STATIC', options: { size: 6 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'airfield' } }, + fillColor: { + type: 'STATIC', + options: { color: '#DB1374' }, + }, + lineColor: { + type: 'STATIC', + options: { color: '#FFFFFF' }, + }, + lineWidth: { type: 'STATIC', options: { size: 2 } }, + iconSize: { type: 'STATIC', options: { size: 8 } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'icon', symbolId: 'marker' }, + }, }, }, id: uuid.v4(), label: `${indexPatternTitle} | ${i18n.DESTINATION_LAYER}`, minZoom: 0, maxZoom: 24, - alpha: 0.75, + alpha: 1, visible: true, applyGlobalQuery: true, type: 'VECTOR', @@ -170,33 +192,41 @@ export const getLineLayer = (indexPatternTitle: string, indexPatternId: string) style: { type: 'VECTOR', properties: { - fillColor: { type: 'STATIC', options: { color: '#e6194b' } }, + fillColor: { + type: 'STATIC', + options: { color: '#1EA593' }, + }, lineColor: { - type: 'DYNAMIC', - options: { - color: 'Green to Red', - field: { label: 'count', name: 'doc_count', origin: 'source' }, - useCustomColorRamp: false, - }, + type: 'STATIC', + options: { color: '#3185FC' }, }, lineWidth: { type: 'DYNAMIC', options: { + field: { + label: 'count', + name: 'doc_count', + origin: 'source', + }, minSize: 1, - maxSize: 4, - field: { label: 'count', name: 'doc_count', origin: 'source' }, + maxSize: 8, }, }, iconSize: { type: 'STATIC', options: { size: 10 } }, - iconOrientation: { type: 'STATIC', options: { orientation: 0 } }, - symbol: { options: { symbolizeAs: 'circle', symbolId: 'airfield' } }, + iconOrientation: { + type: 'STATIC', + options: { orientation: 0 }, + }, + symbol: { + options: { symbolizeAs: 'circle', symbolId: 'airfield' }, + }, }, }, id: uuid.v4(), label: `${indexPatternTitle} | ${i18n.LINE_LAYER}`, minZoom: 0, maxZoom: 24, - alpha: 1, + alpha: 0.5, visible: true, applyGlobalQuery: true, type: 'VECTOR', diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.test.tsx index 87a3d4c77ad70..a73e6dabc68ae 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/map_tool_tip.test.tsx @@ -10,6 +10,12 @@ import * as React from 'react'; import { MapToolTip } from './map_tool_tip'; import { MapFeature } from '../types'; +jest.mock('../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); + describe('MapToolTip', () => { test('placeholder component renders correctly against snapshot', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx index e281c26831f3e..296935be34dfe 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx @@ -13,6 +13,12 @@ import { TestProviders } from '../../../mock'; import { getEmptyStringTag } from '../../empty_value'; import { HostDetailsLink, IPDetailsLink } from '../../links'; +jest.mock('../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); + describe('PointToolTipContent', () => { const mockFeatureProps: FeatureProperty[] = [ { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index e1045bcfe0194..7ab991aa7a8ae 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -5,6 +5,7 @@ */ import { Filter as ESFilterType } from '@kbn/es-query'; +import { Query } from 'src/plugins/data/common'; import { TimeRange } from 'src/plugins/data/public'; import { EmbeddableInput, @@ -15,10 +16,7 @@ import { inputsModel } from '../../store/inputs'; export interface MapEmbeddableInput extends EmbeddableInput { filters: ESFilterType[]; - query: { - query: string; - language: string; - }; + query: Query; refreshConfig: { isPaused: boolean; interval: number; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx index d85231b564da8..96de698a6393e 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx @@ -7,10 +7,11 @@ import { mount } from 'enzyme'; import React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; +import { npSetup } from 'ui/new_platform'; import { TestProviders } from '../../mock'; +import { MockNpSetUp, mockUiSettings } from '../../mock/ui_settings'; import { wait } from '../../lib/helpers'; -import '../../mock/ui_settings'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; @@ -18,6 +19,10 @@ import { defaultHeaders } from './default_headers'; jest.mock('../../lib/settings/use_kibana_ui_setting'); +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; + const from = 1566943856794; const to = 1566857456791; // Suppress warnings about "act" until async/await syntax is supported: https://github.com/facebook/react/issues/14769 @@ -35,12 +40,7 @@ describe('EventsViewer', () => { const wrapper = mount( - + ); @@ -60,12 +60,7 @@ describe('EventsViewer', () => { const wrapper = mount( - + ); @@ -85,12 +80,7 @@ describe('EventsViewer', () => { const wrapper = mount( - + ); @@ -111,12 +101,7 @@ describe('EventsViewer', () => { const wrapper = mount( - + ); diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 4299657e36dab..d8c8996b9af42 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -5,10 +5,12 @@ */ import { EuiPanel } from '@elastic/eui'; -import { getOr, isEmpty } from 'lodash/fp'; +import { Filter } from '@kbn/es-query'; +import { getOr, isEmpty, isEqual } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; import { StaticIndexPattern } from 'ui/index_patterns'; +import { Query } from 'src/plugins/data/common'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; @@ -41,6 +43,7 @@ interface Props { columns: ColumnHeader[]; dataProviders: DataProvider[]; end: number; + filters: Filter[]; height?: number; id: string; indexPattern: StaticIndexPattern; @@ -48,8 +51,8 @@ interface Props { itemsPerPage: number; itemsPerPageOptions: number[]; kqlMode: KqlMode; - kqlQueryExpression: string; onChangeItemsPerPage: OnChangeItemsPerPage; + query: Query; showInspect: boolean; start: number; sort: Sort; @@ -62,6 +65,7 @@ export const EventsViewer = React.memo( columns, dataProviders, end, + filters, height = DEFAULT_EVENTS_VIEWER_HEIGHT, id, indexPattern, @@ -69,8 +73,8 @@ export const EventsViewer = React.memo( itemsPerPage, itemsPerPageOptions, kqlMode, - kqlQueryExpression, onChangeItemsPerPage, + query, showInspect, start, sort, @@ -78,16 +82,17 @@ export const EventsViewer = React.memo( }) => { const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; - const combinedQueries = combineQueries( + const combinedQueries = combineQueries({ dataProviders, indexPattern, browserFields, - kqlQueryExpression, + filters, + kqlQuery: query, kqlMode, start, end, - true - ); + isEventViewer: true, + }); return ( @@ -190,6 +195,7 @@ export const EventsViewer = React.memo( prevProps.columns === nextProps.columns && prevProps.dataProviders === nextProps.dataProviders && prevProps.end === nextProps.end && + isEqual(prevProps.filters, nextProps.filters) && prevProps.height === nextProps.height && prevProps.id === nextProps.id && prevProps.indexPattern === nextProps.indexPattern && @@ -197,7 +203,7 @@ export const EventsViewer = React.memo( prevProps.itemsPerPage === nextProps.itemsPerPage && prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions && prevProps.kqlMode === nextProps.kqlMode && - prevProps.kqlQueryExpression === nextProps.kqlQueryExpression && + isEqual(prevProps.query, nextProps.query) && prevProps.showInspect === nextProps.showInspect && prevProps.start === nextProps.start && prevProps.sort === nextProps.sort diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx index dc0e1288f40f8..d6b65874a02e8 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx @@ -35,12 +35,7 @@ describe('StatefulEventsViewer', () => { const wrapper = mount( - + ); @@ -60,12 +55,7 @@ describe('StatefulEventsViewer', () => { const wrapper = mount( - + ); @@ -85,12 +75,7 @@ describe('StatefulEventsViewer', () => { const wrapper = mount( - + ); diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx index d572d6dd4913b..0ecfb15a67f3b 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { isEqual } from 'lodash/fp'; import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; +import { Query } from 'src/plugins/data/common'; import { WithSource } from '../../containers/source'; import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store'; @@ -24,7 +26,6 @@ import { InputsModelId } from '../../store/inputs/constants'; export interface OwnProps { end: number; id: string; - kqlQueryExpression: string; start: number; } @@ -32,10 +33,12 @@ interface StateReduxProps { activePage?: number; columns: ColumnHeader[]; dataProviders?: DataProvider[]; + filters: Filter[]; isLive: boolean; itemsPerPage?: number; itemsPerPageOptions?: number[]; kqlMode: KqlMode; + query: Query; pageCount?: number; sort?: Sort; } @@ -75,12 +78,13 @@ const StatefulEventsViewerComponent = React.memo( dataProviders, deleteEventQuery, end, + filters, id, isLive, itemsPerPage, itemsPerPageOptions, kqlMode, - kqlQueryExpression, + query, removeColumn, start, sort, @@ -130,13 +134,14 @@ const StatefulEventsViewerComponent = React.memo( id={id} dataProviders={dataProviders!} end={end} + filters={filters} indexPattern={indexPattern} isLive={isLive} itemsPerPage={itemsPerPage!} itemsPerPageOptions={itemsPerPageOptions!} kqlMode={kqlMode} - kqlQueryExpression={kqlQueryExpression} onChangeItemsPerPage={onChangeItemsPerPage} + query={query} showInspect={showInspect} start={start} sort={sort!} @@ -153,11 +158,12 @@ const StatefulEventsViewerComponent = React.memo( isEqual(prevProps.columns, nextProps.columns) && isEqual(prevProps.dataProviders, nextProps.dataProviders) && prevProps.end === nextProps.end && + isEqual(prevProps.filters, nextProps.filters) && prevProps.isLive === nextProps.isLive && prevProps.itemsPerPage === nextProps.itemsPerPage && isEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) && prevProps.kqlMode === nextProps.kqlMode && - prevProps.kqlQueryExpression === nextProps.kqlQueryExpression && + isEqual(prevProps.query, nextProps.query) && prevProps.pageCount === nextProps.pageCount && isEqual(prevProps.sort, nextProps.sort) && prevProps.start === nextProps.start @@ -167,6 +173,8 @@ StatefulEventsViewerComponent.displayName = 'StatefulEventsViewerComponent'; const makeMapStateToProps = () => { const getInputsTimeline = inputsSelectors.getTimelineSelector(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getEvents = timelineSelectors.getEventsByIdSelector(); const mapStateToProps = (state: State, { id }: OwnProps) => { const input: inputsModel.InputsRange = getInputsTimeline(state); @@ -176,11 +184,13 @@ const makeMapStateToProps = () => { return { columns, dataProviders, + filters: getGlobalFiltersQuerySelector(state), id, isLive: input.policy.kind === 'interval', itemsPerPage, itemsPerPageOptions, kqlMode, + query: getGlobalQuerySelector(state), sort, }; }; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/mock.ts b/x-pack/legacy/plugins/siem/public/components/events_viewer/mock.ts index 10e3d9c38cc31..8512d9535cc69 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/mock.ts +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/mock.ts @@ -27,7 +27,7 @@ export const mockEventViewerResponse = [ 'destination.ip', ], filterQuery: - '{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1566943856794}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1566857456791}}}],"minimum_should_match":1}}]}}', + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1566943856794}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1566857456791}}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', sourceId: 'default', pagination: { limit: 25, cursor: null, tiebreaker: null }, sortField: { sortFieldId: '@timestamp', direction: 'desc' }, diff --git a/x-pack/legacy/plugins/siem/public/components/filters_global/filters_global.tsx b/x-pack/legacy/plugins/siem/public/components/filters_global/filters_global.tsx index 0e052fd419611..ddcfa86e42b05 100644 --- a/x-pack/legacy/plugins/siem/public/components/filters_global/filters_global.tsx +++ b/x-pack/legacy/plugins/siem/public/components/filters_global/filters_global.tsx @@ -4,15 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import React from 'react'; import { Sticky } from 'react-sticky'; import { pure } from 'recompose'; import styled, { css } from 'styled-components'; -import { SuperDatePicker } from '../super_date_picker'; - const offsetChrome = 49; const gutterTimeline = '70px'; // Temporary until timeline is moved - MichaelMarcialis @@ -45,16 +42,6 @@ const Aside = styled.aside<{ isSticky?: boolean }>` Aside.displayName = 'Aside'; -// Temporary fix for EuiSuperDatePicker whitespace bug and auto width - Michael Marcialis -const FlexItemWithDatePickerFix = styled(EuiFlexItem)` - .euiSuperDatePicker__flexWrapper { - max-width: none; - width: auto; - } -`; - -FlexItemWithDatePickerFix.displayName = 'FlexItemWithDatePickerFix'; - export interface FiltersGlobalProps { children: React.ReactNode; } @@ -63,13 +50,7 @@ export const FiltersGlobal = pure(({ children }) => ( {({ style, isSticky }) => ( )} diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.test.tsx index 21992d54be086..3a2e516fffb7e 100644 --- a/x-pack/legacy/plugins/siem/public/components/inspect/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.test.tsx @@ -154,7 +154,7 @@ describe('Inspect Button', () => { wrapper.update(); - expect(store.getState().inputs.global.query[0].isInspected).toBe(true); + expect(store.getState().inputs.global.queries[0].isInspected).toBe(true); expect( wrapper .find('button[data-test-subj="modal-inspect-close"]') @@ -185,7 +185,7 @@ describe('Inspect Button', () => { wrapper.update(); - expect(store.getState().inputs.global.query[0].isInspected).toBe(false); + expect(store.getState().inputs.global.queries[0].isInspected).toBe(false); expect( wrapper .find('button[data-test-subj="modal-inspect-close"]') @@ -200,7 +200,7 @@ describe('Inspect Button', () => { ); - store.getState().inputs.global.query[0].loading = true; + store.getState().inputs.global.queries[0].loading = true; wrapper .find('button[data-test-subj="inspect-icon-button"]') .first() @@ -208,7 +208,7 @@ describe('Inspect Button', () => { wrapper.update(); - expect(store.getState().inputs.global.query[0].isInspected).toBe(true); + expect(store.getState().inputs.global.queries[0].isInspected).toBe(true); expect( wrapper .find('button[data-test-subj="modal-inspect-close"]') diff --git a/x-pack/legacy/plugins/siem/public/components/ml/api/throw_if_not_ok.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/api/throw_if_not_ok.test.ts index 374fbd830f920..cb1ed1bfe8e84 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/api/throw_if_not_ok.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/api/throw_if_not_ok.test.ts @@ -230,7 +230,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-suspicious_login_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -245,7 +245,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-rare_process_linux_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -320,7 +320,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-suspicious_login_activity_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&kqlQuery=(filterQuery:!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually high number of authentication attempts (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"high number of authentication attempts","function":"high_non_zero_count","partition_field_name":"host.name"}],"influencers":["host.name","user.name","source.ip"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"IP Address Details","url_value":"siem#/ml-network/ip/$source.ip$?_g=()&query=!n,queryLocation:network.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', @@ -335,7 +335,7 @@ describe('throw_if_not_ok', () => { path: '/_ml/anomaly_detectors/siem-api-rare_process_linux_ecs', query: {}, body: - '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.details,type:details)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'process.name%20:%20%22$process.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&kqlQuery=(filterQuery:(expression:\'user.name%20:%20%22$user.name$%22\',kind:kuery),queryLocation:hosts.page,type:page)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', + '{"job_type":"anomaly_detector","description":"SIEM Auditbeat: Detect unusually rare processes on Linux (beta)","groups":["siem"],"analysis_config":{"bucket_span":"15m","detectors":[{"detector_description":"rare process executions on Linux","function":"rare","by_field_name":"process.name","partition_field_name":"host.name"}],"influencers":["host.name","process.name","user.name"]},"analysis_limits":{"model_memory_limit":"256mb"},"data_description":{"time_field":"@timestamp","time_format":"epoch_ms"},"custom_settings":{"created_by":"ml-module-siem-auditbeat","custom_urls":[{"url_name":"Host Details by process name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Host Details by user name","url_value":"siem#/ml-hosts/$host.name$?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by process name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'process.name%20:%20%22$process.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"},{"url_name":"Hosts Overview by user name","url_value":"siem#/ml-hosts?_g=()&query=(query:\'user.name%20:%20%22$user.name$%22\',language:kuery)&timerange=(global:(linkTo:!(timeline),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')),timeline:(linkTo:!(global),timerange:(from:\'$earliest$\',kind:absolute,to:\'$latest$\')))"}]}}', statusCode: 400, response: '{"error":{"root_cause":[{"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index"}],"type":"status_exception","reason":"This job would cause a mapping clash with existing field [multi_bucket_impact] - avoid the clash by assigning a dedicated results index","caused_by":{"type":"illegal_argument_exception","reason":"mapper [multi_bucket_impact] of different type, current_type [keyword], merged_type [double]"}},"status":400}', diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts index bab39a437b0df..52e27e7de1df0 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.test.ts @@ -101,47 +101,39 @@ describe('add_entities_to_kql', () => { describe('#addEntitiesToKql', () => { test('returns same kql if no entity names or values were defined', () => { - const entity = addEntitiesToKql( - [], - [], - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' - ); - expect(entity).toEqual('(filterQuery:(expression:\'process.name : ""\',kind:kuery))'); + const entity = addEntitiesToKql([], [], '(query:\'process.name : ""\',language:kuery)'); + expect(entity).toEqual('(language:kuery,query:\'process.name : ""\')'); }); - test('returns kql with no "and" clause if the KQL expression is not defined ', () => { - const entity = addEntitiesToKql( - ['host.name'], - ['host-1'], - "(filterQuery:(expression:'',kind:kuery))" - ); - expect(entity).toEqual('(filterQuery:(expression:\'(host.name: "host-1")\',kind:kuery))'); + test('returns kql with no "and" clause if the KQL query is not defined ', () => { + const entity = addEntitiesToKql(['host.name'], ['host-1'], "(query:'',language:kuery)"); + expect(entity).toEqual('(language:kuery,query:\'(host.name: "host-1")\')'); }); - test('returns kql with "and" clause separating the two if the KQL expression is defined', () => { + test('returns kql with "and" clause separating the two if the KQL query is defined', () => { const entity = addEntitiesToKql( ['host.name'], ['host-1'], - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' + '(query:\'process.name : ""\',language:kuery)' ); expect(entity).toEqual( - '(filterQuery:(expression:\'(host.name: "host-1") and (process.name : "")\',kind:kuery))' + '(language:kuery,query:\'(host.name: "host-1") and (process.name : "")\')' ); }); test('returns KQL that is not a Rison Object "as is" with no changes', () => { const entity = addEntitiesToKql(['host.name'], ['host-1'], 'I am some invalid value'); - expect(entity).toEqual('I am some invalid value'); + expect(entity).toEqual('(language:kuery,query:\'(host.name: "host-1")\')'); }); test('returns kql with "and" clause separating the two with multiple entity names and a single value', () => { const entity = addEntitiesToKql( ['source.ip', 'destination.ip'], ['127.0.0.1'], - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' + '(query:\'process.name : ""\',language:kuery)' ); expect(entity).toEqual( - '(filterQuery:(expression:\'((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1")) and (process.name : "")\',kind:kuery))' + '(language:kuery,query:\'((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1")) and (process.name : "")\')' ); }); @@ -149,10 +141,10 @@ describe('add_entities_to_kql', () => { const entity = addEntitiesToKql( ['source.ip', 'destination.ip'], ['127.0.0.1', '255.255.255.255'], - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' + '(query:\'process.name : ""\',language:kuery)' ); expect(entity).toEqual( - '(filterQuery:(expression:\'((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "255.255.255.255" or destination.ip: "255.255.255.255")) and (process.name : "")\',kind:kuery))' + '(language:kuery,query:\'((source.ip: "127.0.0.1" or destination.ip: "127.0.0.1") or (source.ip: "255.255.255.255" or destination.ip: "255.255.255.255")) and (process.name : "")\')' ); }); @@ -160,16 +152,16 @@ describe('add_entities_to_kql', () => { const entity = addEntitiesToKql( ['host.name'], ['host-name-1', 'host-name-2'], - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' + '(query:\'process.name : ""\',language:kuery)' ); expect(entity).toEqual( - '(filterQuery:(expression:\'(host.name: "host-name-1" or host.name: "host-name-2") and (process.name : "")\',kind:kuery))' + '(language:kuery,query:\'(host.name: "host-name-1" or host.name: "host-name-2") and (process.name : "")\')' ); }); - test('returns kql expression with a null filterQuery', () => { - const entity = addEntitiesToKql(['host.name'], ['host-1'], '(filterQuery:!n)'); - expect(entity).toEqual('(filterQuery:(expression:\'(host.name: "host-1")\'))'); + test('returns kql query with a null appQuery', () => { + const entity = addEntitiesToKql(['host.name'], ['host-1'], '!n'); + expect(entity).toEqual('(language:kuery,query:\'(host.name: "host-1")\')'); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts index 490bb8fd324c3..f933ff5b1a1a9 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/add_entities_to_kql.ts @@ -41,22 +41,21 @@ export const addEntitiesToKql = ( ): string => { const value: RisonValue = decodeRison(kqlQuery); if (isRisonObject(value)) { - const filterQuery = value.filterQuery; - if (isRisonObject(filterQuery)) { - if (isRegularString(filterQuery.expression)) { + const appQuery = value; + if (isRisonObject(appQuery)) { + if (isRegularString(appQuery.query)) { const entitiesKql = entitiesToKql(entityNames, entities); - if (filterQuery.expression !== '' && entitiesKql !== '') { - filterQuery.expression = `(${entitiesKql}) and (${filterQuery.expression})`; - } else if (filterQuery.expression === '' && entitiesKql !== '') { - filterQuery.expression = `(${entitiesKql})`; + if (appQuery.query !== '' && entitiesKql !== '') { + appQuery.query = `(${entitiesKql}) and (${appQuery.query})`; + } else if (appQuery.query === '' && entitiesKql !== '') { + appQuery.query = `(${entitiesKql})`; } return encode(value); } - } else if (value.filterQuery == null) { - const entitiesKql = entitiesToKql(entityNames, entities); - value.filterQuery = { expression: `(${entitiesKql})` }; - return encode(value); } + } else if (value == null) { + const entitiesKql = entitiesToKql(entityNames, entities); + return encode({ query: `(${entitiesKql})`, language: 'kuery' }); } return kqlQuery; }; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx index f2e60a5fb86e9..1ff89d7ffe625 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -11,11 +11,10 @@ import { QueryString } from 'ui/utils/query_string'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; -import { replaceKqlQueryLocationForHostPage } from './replace_kql_query_location_for_host_page'; interface QueryStringType { '?_g': string; - kqlQuery: string | null; + query: string | null; timerange: string | null; } @@ -31,8 +30,8 @@ export const MlHostConditionalContainer = React.memo(({ const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) ); - if (queryStringDecoded.kqlQuery != null) { - queryStringDecoded.kqlQuery = replaceKQLParts(queryStringDecoded.kqlQuery); + if (queryStringDecoded.query != null) { + queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } const reEncoded = QueryString.encode(queryStringDecoded); return ; @@ -49,29 +48,19 @@ export const MlHostConditionalContainer = React.memo(({ const queryStringDecoded: QueryStringType = QueryString.decode( location.search.substring(1) ); - if (queryStringDecoded.kqlQuery != null) { - queryStringDecoded.kqlQuery = replaceKQLParts(queryStringDecoded.kqlQuery); + if (queryStringDecoded.query != null) { + queryStringDecoded.query = replaceKQLParts(queryStringDecoded.query); } if (emptyEntity(hostName)) { - if (queryStringDecoded.kqlQuery != null) { - queryStringDecoded.kqlQuery = replaceKqlQueryLocationForHostPage( - queryStringDecoded.kqlQuery - ); - } const reEncoded = QueryString.encode(queryStringDecoded); return ; } else if (multipleEntities(hostName)) { const hosts: string[] = getMultipleEntities(hostName); - if (queryStringDecoded.kqlQuery != null) { - queryStringDecoded.kqlQuery = addEntitiesToKql( - ['host.name'], - hosts, - queryStringDecoded.kqlQuery - ); - queryStringDecoded.kqlQuery = replaceKqlQueryLocationForHostPage( - queryStringDecoded.kqlQuery - ); - } + queryStringDecoded.query = addEntitiesToKql( + ['host.name'], + hosts, + queryStringDecoded.query || '' + ); const reEncoded = QueryString.encode(queryStringDecoded); return ; } else { diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx index 200a00b6dd562..cf9abfe5c46b6 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -11,11 +11,10 @@ import { QueryString } from 'ui/utils/query_string'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, getMultipleEntities, multipleEntities } from './entity_helpers'; -import { replaceKqlQueryLocationForNetworkPage } from './replace_kql_query_location_for_network_page'; interface QueryStringType { '?_g': string; - kqlQuery: string | null; + query: string | null; timerange: string | null; } @@ -31,8 +30,8 @@ export const MlNetworkConditionalContainer = React.memo; @@ -49,29 +48,19 @@ export const MlNetworkConditionalContainer = React.memo; } else if (multipleEntities(ip)) { const ips: string[] = getMultipleEntities(ip); - if (queryStringDecoded.kqlQuery != null) { - queryStringDecoded.kqlQuery = addEntitiesToKql( - ['source.ip', 'destination.ip'], - ips, - queryStringDecoded.kqlQuery - ); - queryStringDecoded.kqlQuery = replaceKqlQueryLocationForNetworkPage( - queryStringDecoded.kqlQuery - ); - } + queryStringDecoded.query = addEntitiesToKql( + ['source.ip', 'destination.ip'], + ips, + queryStringDecoded.query || '' + ); const reEncoded = QueryString.encode(queryStringDecoded); return ; } else { diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.test.ts index 35c1e5f099ba5..3ec75a2279ff2 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.test.ts @@ -42,76 +42,68 @@ describe('remove_kql_variables', () => { }); }); test('should not replace a single empty string value', () => { - const replacement = removeKqlVariables( - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' - ); - expect(replacement).toEqual('(filterQuery:(expression:\'process.name : ""\',kind:kuery))'); + const replacement = removeKqlVariables('(query:\'process.name : ""\',language:kuery)'); + expect(replacement).toEqual('(language:kuery,query:\'process.name : ""\')'); }); test('should not replace a complex string when no variables are present', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(query:\'user.name : "user-1" and process.name : "process-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(language:kuery,query:\'user.name : "user-1" and process.name : "process-1"\')' ); }); test('replacing a string with a variable $user.name$ into an empty string', () => { - const replacement = removeKqlVariables( - '(filterQuery:(expression:\'user.name : "$user.name$"\',kind:kuery))' - ); - expect(replacement).toEqual("(filterQuery:(expression:'',kind:kuery))"); + const replacement = removeKqlVariables('(query:\'user.name : "$user.name$"\',language:kuery)'); + expect(replacement).toEqual("(language:kuery,query:'')"); }); test('replacing a string with a variable $user.name$ and an "and" clause that does not have a variable', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "process-name"\',kind:kuery))' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "process-name"\',language:kuery)' ); + expect(replacement).toEqual('(language:kuery,query:\'process.name : "process-name"\')'); }); test('replacing a string with an "and" clause and a variable $user.name$', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$"\',kind:kuery))' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$"\',language:kuery)' ); + expect(replacement).toEqual('(language:kuery,query:\'process.name : "process-name"\')'); }); test('replacing a string with an "and" clause, a variable $user.name$, and then another "and" clause', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name" and host.name : "host-1"\',kind:kuery))' + '(language:kuery,query:\'process.name : "process-name" and host.name : "host-1"\')' ); }); test('replacing a string with an "and" clause, a variable $user.name$, and then another "and" clause and then another variable', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1" and process.title : "$process.title$"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1" and process.title : "$process.title$"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name" and host.name : "host-1"\',kind:kuery))' + '(language:kuery,query:\'process.name : "process-name" and host.name : "host-1"\')' ); }); test('replacing a string with two variables of $user.name$ and $process.name$ into an empty string', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "$process.name$"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "$process.name$"\',language:kuery)' ); - expect(replacement).toEqual("(filterQuery:(expression:'',kind:kuery))"); + expect(replacement).toEqual("(language:kuery,query:'')"); }); test('replacing a string with two variables of $user.name$ and $process.name$ and an "and" clause', () => { const replacement = removeKqlVariables( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "$process.name$" and host.name="host-1"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "$process.name$" and host.name="host-1"\',language:kuery)' ); - expect(replacement).toEqual('(filterQuery:(expression:host.name="host-1",kind:kuery))'); + expect(replacement).toEqual('(language:kuery,query:host.name="host-1")'); }); test('empty string should return an empty string', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.ts index c31f1eb75e96d..8c6f4b72a19d9 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/remove_kql_variables.ts @@ -34,10 +34,10 @@ export const replacer = (match: string, ...parts: Array { const value: RisonValue = decodeRison(kqlQuery); if (isRisonObject(value)) { - const filterQuery = value.filterQuery; - if (isRisonObject(filterQuery)) { - if (isRegularString(filterQuery.expression)) { - filterQuery.expression = removeKqlVariablesUsingRegex(filterQuery.expression); + const appQuery = value; + if (isRisonObject(appQuery)) { + if (isRegularString(appQuery.query)) { + appQuery.query = removeKqlVariablesUsingRegex(appQuery.query); return encode(value); } } diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.test.ts index 76689f63c17b6..7d5769d7affd0 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.test.ts @@ -9,46 +9,46 @@ import { replaceKqlCommasWithOr } from './replace_kql_commas_with_or'; describe('replace_kql_commas_with_or', () => { test('replaces two comma separated values using an or clause', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'user.name : "becky,evan"\',kind:kuery))' + '(query:\'user.name : "becky,evan"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan")\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan")\')' ); }); test('replaces three comma separated values using an or clause', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'user.name : "becky,evan,braden"\',kind:kuery))' + '(query:\'user.name : "becky,evan,braden"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan" or user.name: "braden")\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan" or user.name: "braden")\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause next to it', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'user.name : "becky,evan,braden" and process.name:"process-name"\',kind:kuery))' + '(query:\'user.name : "becky,evan,braden" and process.name:"process-name"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan" or user.name: "braden") and process.name:"process-name"\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan" or user.name: "braden") and process.name:"process-name"\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause in front of it', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'process.name:"process-name" and user.name : "becky,evan,braden"\',kind:kuery))' + '(query:\'process.name:"process-name" and user.name : "becky,evan,braden"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden")\',kind:kuery))' + '(language:kuery,query:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden")\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause in front and behind it', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'process.name:"process-name" and user.name : "becky,evan,braden" and host.name:"host-name-1"\',kind:kuery))' + '(query:\'process.name:"process-name" and user.name : "becky,evan,braden" and host.name:"host-name-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden") and host.name:"host-name-1"\',kind:kuery))' + '(language:kuery,query:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden") and host.name:"host-name-1"\')' ); }); @@ -58,18 +58,16 @@ describe('replace_kql_commas_with_or', () => { }); test('should not replace a single empty string value', () => { - const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' - ); - expect(replacement).toEqual('(filterQuery:(expression:\'process.name : ""\',kind:kuery))'); + const replacement = replaceKqlCommasWithOr('(query:\'process.name : ""\',language:kuery)'); + expect(replacement).toEqual('(language:kuery,query:\'process.name : ""\')'); }); test('should not replace a complex string when no variables are present and no commas are present', () => { const replacement = replaceKqlCommasWithOr( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(query:\'user.name : "user-1" and process.name : "process-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(language:kuery,query:\'user.name : "user-1" and process.name : "process-1"\')' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.ts index 043c877bba035..20e3c3da50755 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_commas_with_or.ts @@ -27,10 +27,10 @@ export const replaceKqlCommasWithOrUsingRegex = (expression: string) => { export const replaceKqlCommasWithOr = (kqlQuery: string): string => { const value: RisonValue = decodeRison(kqlQuery); if (isRisonObject(value)) { - const filterQuery = value.filterQuery; - if (isRisonObject(filterQuery)) { - if (isRegularString(filterQuery.expression)) { - filterQuery.expression = replaceKqlCommasWithOrUsingRegex(filterQuery.expression); + const appQuery = value; + if (isRisonObject(appQuery)) { + if (isRegularString(appQuery.query)) { + appQuery.query = replaceKqlCommasWithOrUsingRegex(appQuery.query); return encode(value); } } diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_parts.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_parts.test.ts index e47a859ed28d2..c760fd8cf9aa7 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_parts.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_parts.test.ts @@ -9,115 +9,107 @@ import { replaceKQLParts } from './replace_kql_parts'; describe('replace_kql_parts', () => { describe('variables only testing', () => { test('replacing a string with a variable $user.name$ into an empty string', () => { - const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "$user.name$"\',kind:kuery))' - ); - expect(replacement).toEqual("(filterQuery:(expression:'',kind:kuery))"); + const replacement = replaceKQLParts('(query:\'user.name : "$user.name$"\',language:kuery)'); + expect(replacement).toEqual("(language:kuery,query:'')"); }); test('replacing a string with a variable $user.name$ and an "and" clause that does not have a variable', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "process-name"\',kind:kuery))' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "process-name"\',language:kuery)' ); + expect(replacement).toEqual('(language:kuery,query:\'process.name : "process-name"\')'); }); test('replacing a string with an "and" clause and a variable $user.name$', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$"\',kind:kuery))' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$"\',language:kuery)' ); + expect(replacement).toEqual('(language:kuery,query:\'process.name : "process-name"\')'); }); test('replacing a string with an "and" clause, a variable $user.name$, and then another "and" clause', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name" and host.name : "host-1"\',kind:kuery))' + '(language:kuery,query:\'process.name : "process-name" and host.name : "host-1"\')' ); }); test('replacing a string with an "and" clause, a variable $user.name$, and then another "and" clause and then another variable', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1" and process.title : "$process.title$"\',kind:kuery))' + '(query:\'process.name : "process-name" and user.name : "$user.name$" and host.name : "host-1" and process.title : "$process.title$"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name : "process-name" and host.name : "host-1"\',kind:kuery))' + '(language:kuery,query:\'process.name : "process-name" and host.name : "host-1"\')' ); }); test('replacing a string with two variables of $user.name$ and $process.name$ into an empty string', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "$process.name$"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "$process.name$"\',language:kuery)' ); - expect(replacement).toEqual("(filterQuery:(expression:'',kind:kuery))"); + expect(replacement).toEqual("(language:kuery,query:'')"); }); test('replacing a string with two variables of $user.name$ and $process.name$ and an "and" clause', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "$process.name$" and host.name="host-1"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "$process.name$" and host.name="host-1"\',language:kuery)' ); - expect(replacement).toEqual('(filterQuery:(expression:host.name="host-1",kind:kuery))'); + expect(replacement).toEqual('(language:kuery,query:host.name="host-1")'); }); }); describe('comma testing only', () => { test('replaces two comma separated values using an or clause', () => { - const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "becky,evan"\',kind:kuery))' - ); + const replacement = replaceKQLParts('(query:\'user.name : "becky,evan"\',language:kuery)'); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan")\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan")\')' ); }); test('replaces three comma separated values using an or clause', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "becky,evan,braden"\',kind:kuery))' + '(query:\'user.name : "becky,evan,braden"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan" or user.name: "braden")\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan" or user.name: "braden")\')' ); }); test('replaces three comma separated values using an or clause with hypens for names', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "username-1,username-2,username-3"\',kind:kuery))' + '(query:\'user.name : "username-1,username-2,username-3"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "username-1" or user.name: "username-2" or user.name: "username-3")\',kind:kuery))' + '(language:kuery,query:\'(user.name: "username-1" or user.name: "username-2" or user.name: "username-3")\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause next to it', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "becky,evan,braden" and process.name:"process-name"\',kind:kuery))' + '(query:\'user.name : "becky,evan,braden" and process.name:"process-name"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(user.name: "becky" or user.name: "evan" or user.name: "braden") and process.name:"process-name"\',kind:kuery))' + '(language:kuery,query:\'(user.name: "becky" or user.name: "evan" or user.name: "braden") and process.name:"process-name"\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause in front of it', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name:"process-name" and user.name : "becky,evan,braden"\',kind:kuery))' + '(query:\'process.name:"process-name" and user.name : "becky,evan,braden"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden")\',kind:kuery))' + '(language:kuery,query:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden")\')' ); }); test('replaces three comma separated values using an or clause with an additional "and" clause in front and behind it', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name:"process-name" and user.name : "becky,evan,braden" and host.name:"host-name-1"\',kind:kuery))' + '(query:\'process.name:"process-name" and user.name : "becky,evan,braden" and host.name:"host-name-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden") and host.name:"host-name-1"\',kind:kuery))' + '(language:kuery,query:\'process.name:"process-name" and (user.name: "becky" or user.name: "evan" or user.name: "braden") and host.name:"host-name-1"\')' ); }); }); @@ -129,36 +121,34 @@ describe('replace_kql_parts', () => { }); test('should not replace a single empty string value', () => { - const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name : ""\',kind:kuery))' - ); - expect(replacement).toEqual('(filterQuery:(expression:\'process.name : ""\',kind:kuery))'); + const replacement = replaceKQLParts('(query:\'process.name : ""\',language:kuery)'); + expect(replacement).toEqual('(language:kuery,query:\'process.name : ""\')'); }); test('should not replace a complex string when no variables are present and no commas are present', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(query:\'user.name : "user-1" and process.name : "process-1"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'user.name : "user-1" and process.name : "process-1"\',kind:kuery))' + '(language:kuery,query:\'user.name : "user-1" and process.name : "process-1"\')' ); }); test('replacing a string with a variable $user.name$ into an empty string and expand a comma separated list of items', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'user.name : "$user.name$" and process.name : "process-name-1,process-name-2"\',kind:kuery))' + '(query:\'user.name : "$user.name$" and process.name : "process-name-1,process-name-2"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(process.name: "process-name-1" or process.name: "process-name-2")\',kind:kuery))' + '(language:kuery,query:\'(process.name: "process-name-1" or process.name: "process-name-2")\')' ); }); test('replacing a string with an "and" clause, a variable $user.name$, and then another "and" clause while expanding multiple process names and host names', () => { const replacement = replaceKQLParts( - '(filterQuery:(expression:\'process.name : "process-name-1,process-name-2,process-name-3" and user.name : "$user.name$" and host.name : "host-1,host-2,host-3,host-4" and process.title : "$process.title$"\',kind:kuery))' + '(query:\'process.name : "process-name-1,process-name-2,process-name-3" and user.name : "$user.name$" and host.name : "host-1,host-2,host-3,host-4" and process.title : "$process.title$"\',language:kuery)' ); expect(replacement).toEqual( - '(filterQuery:(expression:\'(process.name: "process-name-1" or process.name: "process-name-2" or process.name: "process-name-3") and (host.name: "host-1" or host.name: "host-2" or host.name: "host-3" or host.name: "host-4")\',kind:kuery))' + '(language:kuery,query:\'(process.name: "process-name-1" or process.name: "process-name-2" or process.name: "process-name-3") and (host.name: "host-1" or host.name: "host-2" or host.name: "host-3" or host.name: "host-4")\')' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.test.ts deleted file mode 100644 index 48e6e1c942cc4..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.test.ts +++ /dev/null @@ -1,33 +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 { replaceKqlQueryLocationForHostPage } from './replace_kql_query_location_for_host_page'; - -// Suppress warnings about invalid RISON as this is what we are testing -/* eslint-disable no-console */ -const originalError = console.log; -describe('replace_kql_query_location_for_host_page', () => { - beforeAll(() => { - console.log = jest.fn(); - }); - - afterAll(() => { - console.log = originalError; - }); - test('replaces host details and type details for a page', () => { - const replacement = replaceKqlQueryLocationForHostPage( - '(filterQuery:(expression:\'process.name: "some-name"\',kind:kuery),queryLocation:hosts.details,type:details)' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name: "some-name"\',kind:kuery),queryLocation:hosts.page,type:page)' - ); - }); - - test('does not do anything if the RISON is not valid', () => { - const replacement = replaceKqlQueryLocationForHostPage('invalid rison here'); - expect(replacement).toEqual('invalid rison here'); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.ts deleted file mode 100644 index 3ee02ac1cf1b4..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.ts +++ /dev/null @@ -1,21 +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 { RisonValue, encode } from 'rison-node'; -import { decodeRison, isRisonObject } from './rison_helpers'; -import { CONSTANTS } from '../../url_state/constants'; -import { HostsType } from '../../../store/hosts/model'; - -export const replaceKqlQueryLocationForHostPage = (kqlQuery: string): string => { - const value: RisonValue = decodeRison(kqlQuery); - if (isRisonObject(value)) { - value.queryLocation = CONSTANTS.hostsPage; - value.type = HostsType.page; - return encode(value); - } else { - return kqlQuery; - } -}; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.test.ts deleted file mode 100644 index 5a31bbf2cb676..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.test.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 { replaceKqlQueryLocationForNetworkPage } from './replace_kql_query_location_for_network_page'; -// Suppress warnings about invalid RISON as this is what we are testing -/* eslint-disable no-console */ -const originalError = console.log; -describe('replace_kql_query_location_for_host_page', () => { - beforeAll(() => { - console.log = jest.fn(); - }); - - afterAll(() => { - console.log = originalError; - }); - test('replaces host details and type details for a page', () => { - const replacement = replaceKqlQueryLocationForNetworkPage( - '(filterQuery:(expression:\'process.name: "some-name"\',kind:kuery),queryLocation:network.details,type:details)' - ); - expect(replacement).toEqual( - '(filterQuery:(expression:\'process.name: "some-name"\',kind:kuery),queryLocation:network.page,type:page)' - ); - }); - - test('does not do anything if the RISON is not valid', () => { - const replacement = replaceKqlQueryLocationForNetworkPage('invalid rison here'); - expect(replacement).toEqual('invalid rison here'); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.ts deleted file mode 100644 index c249ca7872f45..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.ts +++ /dev/null @@ -1,21 +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 { RisonValue, encode } from 'rison-node'; -import { decodeRison, isRisonObject } from './rison_helpers'; -import { CONSTANTS } from '../../url_state/constants'; -import { NetworkType } from '../../../store/network/model'; - -export const replaceKqlQueryLocationForNetworkPage = (kqlQuery: string): string => { - const value: RisonValue = decodeRison(kqlQuery); - if (isRisonObject(value)) { - value.queryLocation = CONSTANTS.networkPage; - value.type = NetworkType.page; - return encode(value); - } else { - return kqlQuery; - } -}; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/rison_helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/rison_helpers.test.ts index 738ebe89519be..da7f5eca55527 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/rison_helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/conditional_links/rison_helpers.test.ts @@ -5,8 +5,6 @@ */ import { decodeRison, isRisonObject, isRegularString } from './rison_helpers'; -import { HostsType } from '../../../store/hosts/model'; -import { CONSTANTS } from '../../url_state/constants'; describe('rison_helpers', () => { // Suppress warnings about invalid RISON as this is what we are testing @@ -26,14 +24,8 @@ describe('rison_helpers', () => { }); test('returns a RISON value decoded if sent in an object', () => { - const expected = decodeRison( - '(filterQuery:(expression:\'process.name: "process-name-1"\',kind:kuery),queryLocation:hosts.details,type:details)' - ); - expect(expected).toEqual({ - filterQuery: { expression: 'process.name: "process-name-1"', kind: 'kuery' }, - queryLocation: CONSTANTS.hostsDetails, - type: HostsType.details, - }); + const expected = decodeRison('(query:\'process.name: "process-name-1"\',language:kuery)'); + expect(expected).toEqual({ query: 'process.name: "process-name-1"', language: 'kuery' }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts index 39a4c8efc4001..02135348957ff 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts @@ -24,14 +24,18 @@ jest.mock('ui/chrome', () => ({ }), })); +jest.mock('../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); + const getMockObject = ( pageName: string, pathName: string, detailName: string | undefined ): RouteSpyState & TabNavigationProps => ({ detailName, - hostDetails: { filterQuery: null, queryLocation: null }, - hosts: { filterQuery: null, queryLocation: null }, navTabs: { hosts: { disabled: false, @@ -62,12 +66,16 @@ const getMockObject = ( urlKey: 'timeline', }, }, - network: { filterQuery: null, queryLocation: null }, pageName, pathName, search: '', tabName: HostsTableType.authentications, - timelineId: '', + query: { query: '', language: 'kuery' }, + filters: [], + timeline: { + id: '', + isOpen: false, + }, timerange: { global: { linkTo: ['timeline'], diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts index e934398ccf57f..68aa115c965d6 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; +import { isEmpty } from 'lodash/fp'; import { Location } from 'history'; +import { Query } from 'src/plugins/data/common'; import { UrlInputsModel } from '../../store/inputs/model'; import { CONSTANTS } from '../url_state/constants'; -import { KqlQuery, URL_STATE_KEYS, KeyUrlState } from '../url_state/types'; +import { URL_STATE_KEYS, KeyUrlState, Timeline } from '../url_state/types'; import { replaceQueryStringInLocation, replaceStateKeyInQueryString, @@ -22,13 +25,29 @@ export const getSearch = (tab: SearchNavTab, urlState: TabNavigationProps): stri if (tab && tab.urlKey != null && URL_STATE_KEYS[tab.urlKey] != null) { return URL_STATE_KEYS[tab.urlKey].reduce( (myLocation: Location, urlKey: KeyUrlState) => { - let urlStateToReplace: UrlInputsModel | KqlQuery | string = urlState[CONSTANTS.timelineId]; - if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'host') { - urlStateToReplace = tab.isDetailPage ? urlState.hostDetails : urlState.hosts; - } else if (urlKey === CONSTANTS.kqlQuery && tab.urlKey === 'network') { - urlStateToReplace = urlState.network; + let urlStateToReplace: UrlInputsModel | Query | Filter[] | Timeline | string = ''; + + if (urlKey === CONSTANTS.appQuery && urlState.query != null) { + if (urlState.query.query === '') { + urlStateToReplace = ''; + } else { + urlStateToReplace = urlState.query; + } + } else if (urlKey === CONSTANTS.filters && urlState.filters != null) { + if (isEmpty(urlState.filters)) { + urlStateToReplace = ''; + } else { + urlStateToReplace = urlState.filters; + } } else if (urlKey === CONSTANTS.timerange) { urlStateToReplace = urlState[CONSTANTS.timerange]; + } else if (urlKey === CONSTANTS.timeline && urlState[CONSTANTS.timeline] != null) { + const timeline = urlState[CONSTANTS.timeline]; + if (timeline.id === '') { + urlStateToReplace = ''; + } else { + urlStateToReplace = timeline; + } } return replaceQueryStringInLocation( myLocation, diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index 96cb85b246a49..cf519da617183 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -49,26 +49,17 @@ describe('SIEM Navigation', () => { linkTo: ['global'], }, }, - hosts: { - filterQuery: null, - queryLocation: null, + [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, + [CONSTANTS.filters]: [], + [CONSTANTS.timeline]: { + id: '', + isOpen: false, }, - hostDetails: { - filterQuery: null, - queryLocation: null, - }, - network: { - filterQuery: null, - queryLocation: null, - }, - [CONSTANTS.timelineId]: '', }; const wrapper = mount(); test('it calls setBreadcrumbs with correct path on mount', () => { expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { detailName: undefined, - hostDetails: { filterQuery: null, queryLocation: null }, - hosts: { filterQuery: null, queryLocation: null }, navTabs: { hosts: { disabled: false, @@ -99,12 +90,17 @@ describe('SIEM Navigation', () => { urlKey: 'timeline', }, }, - network: { filterQuery: null, queryLocation: null }, pageName: 'hosts', pathName: '/hosts', search: '', tabName: 'authentications', - timelineId: '', + query: { query: '', language: 'kuery' }, + filters: [], + savedQuery: undefined, + timeline: { + id: '', + isOpen: false, + }, timerange: { global: { linkTo: ['timeline'], @@ -138,8 +134,6 @@ describe('SIEM Navigation', () => { wrapper.update(); expect(setBreadcrumbs).toHaveBeenNthCalledWith(2, { detailName: undefined, - hostDetails: { filterQuery: null, queryLocation: null }, - hosts: { filterQuery: null, queryLocation: null }, navTabs: { hosts: { disabled: false, @@ -170,12 +164,17 @@ describe('SIEM Navigation', () => { urlKey: 'timeline', }, }, - network: { filterQuery: null, queryLocation: null }, pageName: 'network', pathName: '/network', search: '', tabName: undefined, - timelineId: '', + query: { query: '', language: 'kuery' }, + filters: [], + savedQuery: undefined, + timeline: { + id: '', + isOpen: false, + }, timerange: { global: { linkTo: ['timeline'], diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx index 06f7a2ffb0566..ae8d09eeff112 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx @@ -11,68 +11,59 @@ import { connect } from 'react-redux'; import { RouteSpyState } from '../../utils/route/types'; import { useRouteSpy } from '../../utils/route/use_route_spy'; -import { CONSTANTS } from '../url_state/constants'; -import { - inputsSelectors, - hostsSelectors, - networkSelectors, - timelineSelectors, - State, - hostsModel, - networkModel, -} from '../../store'; import { setBreadcrumbs } from './breadcrumbs'; import { TabNavigation } from './tab_navigation'; import { TabNavigationProps } from './tab_navigation/types'; import { SiemNavigationComponentProps } from './types'; +import { makeMapStateToProps } from '../url_state/helpers'; export const SiemNavigationComponent = React.memo( ({ + query, detailName, display, - hostDetails, - hosts, + filters, navTabs, - network, pageName, pathName, + savedQuery, search, showBorder, tabName, - timelineId, + timeline, timerange, }) => { useEffect(() => { if (pathName) { setBreadcrumbs({ + query, detailName, - hosts, - hostDetails, + filters, navTabs, - network, pageName, pathName, + savedQuery, search, tabName, timerange, - timelineId, + timeline, }); } - }, [pathName, search, hosts, hostDetails, network, navTabs, timerange, timelineId]); + }, [query, pathName, search, filters, navTabs, savedQuery, timerange, timeline]); return ( ); @@ -81,64 +72,18 @@ export const SiemNavigationComponent = React.memo { - const getInputsSelector = inputsSelectors.inputsSelector(); - const getHostsFilterQueryAsKuery = hostsSelectors.hostsFilterQueryAsKuery(); - const getNetworkFilterQueryAsKuery = networkSelectors.networkFilterQueryAsKuery(); - const getTimelines = timelineSelectors.getTimelines(); - const mapStateToProps = (state: State) => { - const inputState = getInputsSelector(state); - const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; - const { linkTo: timelineLinkTo, timerange: timelineTimerange } = inputState.timeline; - - const openTimelineId = Object.entries(getTimelines(state)).reduce( - (useTimelineId, [timelineId, timelineObj]) => - timelineObj.savedObjectId != null ? timelineObj.savedObjectId : useTimelineId, - '' - ); - - return { - hosts: { - filterQuery: getHostsFilterQueryAsKuery(state, hostsModel.HostsType.page), - queryLocation: CONSTANTS.hostsPage, - }, - hostDetails: { - filterQuery: getHostsFilterQueryAsKuery(state, hostsModel.HostsType.details), - queryLocation: CONSTANTS.hostsDetails, - }, - network: { - filterQuery: getNetworkFilterQueryAsKuery(state, networkModel.NetworkType.page), - queryLocation: CONSTANTS.networkPage, - }, - [CONSTANTS.timerange]: { - global: { - [CONSTANTS.timerange]: globalTimerange, - linkTo: globalLinkTo, - }, - timeline: { - [CONSTANTS.timerange]: timelineTimerange, - linkTo: timelineLinkTo, - }, - }, - [CONSTANTS.timelineId]: openTimelineId, - }; - }; - - return mapStateToProps; -}; - export const SiemNavigationRedux = compose< React.ComponentClass >(connect(makeMapStateToProps))(SiemNavigationComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx index 61a0ec9c06c2d..ac4b78c5b61f5 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx @@ -51,22 +51,12 @@ describe('Tab Navigation', () => { linkTo: ['global'], }, }, - hosts: { - filterQuery: null, - queryLocation: null, + [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, + [CONSTANTS.filters]: [], + [CONSTANTS.timeline]: { + id: '', + isOpen: false, }, - hostDetails: { - filterQuery: null, - queryLocation: null, - }, - network: { - filterQuery: { - expression: 'host.name:"siem-es"', - kind: 'kuery', - }, - queryLocation: CONSTANTS.hostsPage, - }, - [CONSTANTS.timelineId]: '', }; test('it mounts with correct tab highlighted', () => { const wrapper = shallow(); @@ -89,7 +79,7 @@ describe('Tab Navigation', () => { const wrapper = shallow(); const firstTab = wrapper.find('[data-test-subj="navigation-link-network"]'); expect(firstTab.props().href).toBe( - "#/link-to/network?kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" + "#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); }); }); @@ -125,22 +115,12 @@ describe('Tab Navigation', () => { linkTo: ['global'], }, }, - network: { - filterQuery: null, - queryLocation: null, - }, - hosts: { - filterQuery: null, - queryLocation: null, - }, - hostDetails: { - filterQuery: { - expression: 'host.name:"siem-es"', - kind: 'kuery', - }, - queryLocation: CONSTANTS.hostsPage, + [CONSTANTS.appQuery]: { query: 'host.name:"siem-es"', language: 'kuery' }, + [CONSTANTS.filters]: [], + [CONSTANTS.timeline]: { + id: '', + isOpen: false, }, - [CONSTANTS.timelineId]: '', }; test('it mounts with correct tab highlighted', () => { const wrapper = shallow(); @@ -169,7 +149,7 @@ describe('Tab Navigation', () => { `[data-test-subj="navigation-link-${HostsTableType.authentications}"]` ); expect(firstTab.props().href).toBe( - `#/${pageName}/${hostName}/${HostsTableType.authentications}?kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:hosts.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` + `#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts index 38970e31332cd..1d5ebf2097974 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts @@ -4,9 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; +import { Query } from 'src/plugins/data/common'; import { UrlInputsModel } from '../../../store/inputs/model'; import { CONSTANTS } from '../../url_state/constants'; -import { KqlQuery } from '../../url_state/types'; +import { Timeline } from '../../url_state/types'; import { HostsTableType } from '../../../store/hosts/model'; import { SiemNavigationComponentProps } from '../types'; @@ -15,9 +17,9 @@ export interface TabNavigationProps extends SiemNavigationComponentProps { pathName: string; pageName: string; tabName: HostsTableType | undefined; - hosts: KqlQuery; - hostDetails: KqlQuery; - network: KqlQuery; + [CONSTANTS.appQuery]?: Query; + [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.savedQuery]?: string; [CONSTANTS.timerange]: UrlInputsModel; - [CONSTANTS.timelineId]: string; + [CONSTANTS.timeline]: Timeline; } diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts index b035006930bb2..1d2508fcfaf15 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts @@ -147,6 +147,7 @@ export interface QueryTimelineById { apolloClient: ApolloClient | ApolloClient<{}> | undefined; duplicate: boolean; timelineId: string; + openTimeline?: boolean; updateIsLoading: ActionCreator<{ id: string; isLoading: boolean }>; updateTimeline: DispatchUpdateTimeline; } @@ -155,6 +156,7 @@ export const queryTimelineById = ({ apolloClient, duplicate = false, timelineId, + openTimeline = true, updateIsLoading, updateTimeline, }: QueryTimelineById) => { @@ -179,7 +181,10 @@ export const queryTimelineById = ({ from: getOr(getDefaultFromValue(), 'dateRange.start', timeline), id: 'timeline-1', notes, - timeline, + timeline: { + ...timeline, + show: openTimeline, + }, to: getOr(getDefaultToValue(), 'dateRange.end', timeline), })(); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/__snapshots__/index.test.tsx.snap index a3d7e80d35d89..823ed972d4a2f 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/__snapshots__/index.test.tsx.snap @@ -12,9 +12,31 @@ exports[`AddToKql Component Rendering 1`] = ` } } > - siem-kibana - + `; diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/helpers.ts b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/helpers.ts new file mode 100644 index 0000000000000..4be2a6be55fce --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/helpers.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const createFilter = (key: string, value: string) => ({ + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key, + value, + params: { + query: value, + }, + }, + query: { + match: { + [key]: { + query: value, + type: 'phrase', + }, + }, + }, +}); diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx index 9ec4e037e3edf..1f5f20cfc9237 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.test.tsx @@ -4,21 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { mount, shallow } from 'enzyme'; import toJson from 'enzyme-to-json'; import * as React from 'react'; -import { escapeQueryValue } from '../../../lib/keury'; import { apolloClientObservable, mockGlobalState, TestProviders, mockIndexPattern, } from '../../../mock'; -import { createStore, hostsModel, networkModel, State } from '../../../store'; - +import { createStore, State } from '../../../store'; +import { siemFilterManager } from '../../search_bar'; import { AddToKql } from '.'; +interface MockSiemFilterManager { + addFilters: (filters: Filter[]) => void; +} +const mockSiemFilterManager: MockSiemFilterManager = siemFilterManager as MockSiemFilterManager; +jest.mock('../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); +const mockAddFilters = jest.fn(); +mockSiemFilterManager.addFilters = mockAddFilters; + describe('AddToKql Component', () => { const state: State = mockGlobalState; let store = createStore(state, apolloClientObservable); @@ -31,10 +43,29 @@ describe('AddToKql Component', () => { const wrapper = shallow( <>{'siem-kibana'} @@ -48,10 +79,29 @@ describe('AddToKql Component', () => { const wrapper = shallow( <>{'siem-kibana'} @@ -63,14 +113,33 @@ describe('AddToKql Component', () => { expect(wrapper.find('[data-test-subj="hover-actions-container"] svg').first()).toBeTruthy(); }); - test('Functionality with hosts state', async () => { + test('Functionality with inputs state', async () => { const wrapper = mount( <>{'siem-kibana'} @@ -84,103 +153,25 @@ describe('AddToKql Component', () => { .simulate('click'); wrapper.update(); - expect(store.getState().hosts.page).toEqual({ - queries: { - authentications: { - activePage: 0, - limit: 10, - }, - allHosts: { - activePage: 0, - limit: 10, - direction: 'desc', - sortField: 'lastSeen', - }, - events: { - activePage: 0, - limit: 10, - }, - uncommonProcesses: { - activePage: 0, - limit: 10, + expect(mockAddFilters.mock.calls[0][0]).toEqual({ + meta: { + alias: null, + disabled: false, + key: 'host.name', + negate: false, + params: { + query: 'siem-kibana', }, - anomalies: null, + type: 'phrase', + value: 'siem-kibana', }, - filterQuery: { - kuery: { - kind: 'kuery', - expression: 'host.name: "siem-kibana"', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"siem-kibana"}}],"minimum_should_match":1}}', - }, - filterQueryDraft: { - kind: 'kuery', - expression: 'host.name: "siem-kibana"', - }, - }); - }); - - test('Functionality with network state', async () => { - const wrapper = mount( - - - <>{'siem-kibana'} - - - ); - - wrapper - .simulate('mouseenter') - .find('[data-test-subj="hover-actions-container"] .euiToolTipAnchor svg') - .first() - .simulate('click'); - wrapper.update(); - - expect(store.getState().network.page).toEqual({ - queries: { - topNFlowDestination: { - activePage: 0, - limit: 10, - topNFlowSort: { - field: 'bytes_out', - direction: 'desc', - }, - }, - topNFlowSource: { - activePage: 0, - limit: 10, - topNFlowSort: { - field: 'bytes_out', - direction: 'desc', - }, - }, - dns: { - activePage: 0, - limit: 10, - dnsSortField: { - field: 'queryCount', - direction: 'desc', + query: { + match: { + 'host.name': { + query: 'siem-kibana', + type: 'phrase', }, - isPtrIncluded: false, - }, - }, - filterQuery: { - kuery: { - kind: 'kuery', - expression: 'host.name: "siem-kibana"', }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"siem-kibana"}}],"minimum_should_match":1}}', - }, - filterQueryDraft: { - kind: 'kuery', - expression: 'host.name: "siem-kibana"', }, }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.tsx index 86e3a76cdff69..2e3edb2c59a61 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_to_kql/index.tsx @@ -5,33 +5,40 @@ */ import { EuiIcon, EuiPanel, EuiToolTip } from '@elastic/eui'; -import { isEmpty } from 'lodash/fp'; +import { Filter } from '@kbn/es-query'; +import { getOr } from 'lodash/fp'; import React from 'react'; +import { connect } from 'react-redux'; import styled from 'styled-components'; import { StaticIndexPattern } from 'ui/index_patterns'; +import { Query } from 'src/plugins/data/common'; -import { HostsFilter } from '../../../containers/hosts'; -import { NetworkFilter } from '../../../containers/network'; -import { assertUnreachable } from '../../../lib/helpers'; -import { hostsModel, KueryFilterQuery, networkModel } from '../../../store'; import { WithHoverActions } from '../../with_hover_actions'; +import { InputsModelId } from '../../../store/inputs/constants'; +import { siemFilterManager } from '../../search_bar'; + import * as i18n from './translations'; +import { filterQuerySelector } from '../../search_bar/selectors'; +import { State } from '../../../store'; +import { InputsRange } from '../../../store/inputs/model'; + +export * from './helpers'; + +interface AddToKqlRedux { + query: Query; +} -interface Props { - applyFilterQueryFromKueryExpression: (expression: string) => void; +interface OwnProps { + id: InputsModelId; children: JSX.Element; - expression: string; - filterQueryDraft: KueryFilterQuery; + indexPattern: StaticIndexPattern; + filter: Filter; } -const AddToKqlComponent = React.memo( - ({ children, expression, filterQueryDraft, applyFilterQueryFromKueryExpression }) => { +const AddToKqlComponent = React.memo( + ({ children, id, indexPattern, filter, query }) => { const addToKql = () => { - applyFilterQueryFromKueryExpression( - filterQueryDraft && !isEmpty(filterQueryDraft.expression) - ? `${filterQueryDraft.expression} and ${expression}` - : expression - ); + siemFilterManager.addFilters(filter); }; return ( ( - ({ children, expression, type, componentFilterType, indexPattern }) => { - switch (componentFilterType) { - case 'hosts': - return ( - - {({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => ( - - {children} - - )} - - ); - case 'network': - return ( - - {({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => ( - - {children} - - )} - - ); - } - assertUnreachable(componentFilterType, 'Unknown Filter Type in switch statement'); - } -); +const makeMapStateToProps = () => { + const getFilterQuerySelector = filterQuerySelector(); + return (state: State, { id }: OwnProps) => { + const inputsRange: InputsRange = getOr({}, `inputs.${id}`, state); + return { + query: getFilterQuerySelector(inputsRange), + }; + }; +}; -AddToKql.displayName = 'AddToKql'; +export const AddToKql = connect(makeMapStateToProps)(AddToKqlComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/__snapshots__/index.test.tsx.snap index f5339c27ed052..c320ab1be2d84 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/__snapshots__/index.test.tsx.snap @@ -149,6 +149,12 @@ exports[`Hosts Table rendering it renders the default Hosts table 1`] = ` "searchable": true, "type": "string", }, + Object { + "aggregatable": true, + "name": "host.name", + "searchable": true, + "type": "string", + }, ], "title": "filebeat-*,auditbeat-*,packetbeat-*", } diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx index 5789b63bd2bc3..216c76fa92961 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/columns.tsx @@ -9,7 +9,6 @@ import moment from 'moment'; import React from 'react'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { escapeQueryValue } from '../../../../lib/keury'; import { hostsModel } from '../../../../store'; import { DragEffects, DraggableWrapper } from '../../../drag_and_drop/draggable_wrapper'; import { escapeDataProviderId } from '../../../drag_and_drop/helpers'; @@ -19,7 +18,7 @@ import { HostDetailsLink } from '../../../links'; import { LocalizedDateTooltip } from '../../../localized_date_tooltip'; import { IS_OPERATOR } from '../../../timeline/data_providers/data_provider'; import { Provider } from '../../../timeline/data_providers/provider'; -import { AddToKql } from '../../add_to_kql'; +import { AddToKql, createFilter } from '../../add_to_kql'; import { HostsTableColumns } from './'; import * as i18n from './translations'; @@ -56,10 +55,9 @@ export const getHostsColumns = ( ) : ( @@ -106,10 +104,9 @@ export const getHostsColumns = ( if (hostOsName != null) { return ( <>{hostOsName} @@ -128,10 +125,9 @@ export const getHostsColumns = ( if (hostOsVersion != null) { return ( <>{hostOsVersion} diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx index dabfa4acda105..90d7a9c613dc2 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.test.tsx @@ -10,6 +10,7 @@ import { getOr } from 'lodash/fp'; import * as React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; import { Provider as ReduxStoreProvider } from 'react-redux'; +import { npSetup } from 'ui/new_platform'; import { apolloClientObservable, @@ -17,11 +18,15 @@ import { mockGlobalState, TestProviders, } from '../../../../mock'; +import { MockNpSetUp, mockUiSettings } from '../../../../mock/ui_settings'; import { createStore, hostsModel, State } from '../../../../store'; - +import { HostsTableType } from '../../../../store/hosts/model'; import { HostsTable } from './index'; import { mockData } from './mock'; -import { HostsTableType } from '../../../../store/hosts/model'; + +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; describe('Hosts Table', () => { const loadPage = jest.fn(); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap index 498c620312a3a..f80a61836b86e 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/__snapshots__/index.test.tsx.snap @@ -181,6 +181,12 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ "searchable": true, "type": "string", }, + Object { + "aggregatable": true, + "name": "host.name", + "searchable": true, + "type": "string", + }, ], "title": "filebeat-*,auditbeat-*,packetbeat-*", } @@ -375,6 +381,12 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ "searchable": true, "type": "string", }, + Object { + "aggregatable": true, + "name": "host.name", + "searchable": true, + "type": "string", + }, ], "title": "filebeat-*,auditbeat-*,packetbeat-*", } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.test.tsx index 792f66635cc73..216d42425435a 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.test.tsx @@ -18,6 +18,14 @@ import { createStore, networkModel, State } from '../../../../store'; import { UsersTable } from '.'; import { mockUsersData } from './mock'; +jest.mock('../../../../lib/settings/use_kibana_ui_setting'); + +jest.mock('../../../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); + describe('Users Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx new file mode 100644 index 0000000000000..0876ca69f91a7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -0,0 +1,395 @@ +/* + * 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 { Filter } from '@kbn/es-query'; +import { getOr, isEqual, set } from 'lodash/fp'; +import React, { memo, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { Dispatch } from 'redux'; +import { Subscription } from 'rxjs'; +import styled from 'styled-components'; +import { StaticIndexPattern, IndexPattern } from 'ui/index_patterns'; + +import { TimeRange, Query } from 'src/plugins/data/common/types'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; + +import { OnTimeChangeProps } from '@elastic/eui'; +import { start as data } from '../../../../../../../src/legacy/core_plugins/data/public/legacy'; + +import { inputsActions } from '../../store/inputs'; +import { InputsRange } from '../../store/inputs/model'; +import { InputsModelId } from '../../store/inputs/constants'; +import { State, inputsModel } from '../../store'; +import { formatDate } from '../super_date_picker'; +import { + endSelector, + filterQuerySelector, + fromStrSelector, + isLoadingSelector, + kindSelector, + queriesSelector, + savedQuerySelector, + startSelector, + toStrSelector, +} from './selectors'; +import { timelineActions, hostsActions, networkActions } from '../../store/actions'; + +const { + ui: { SearchBar }, + filter, + search, + timefilter, +} = data; + +export const siemFilterManager = filter.filterManager; +export const savedQueryService = search.services.savedQueryService; + +interface SiemSearchBarRedux { + end: number; + fromStr: string; + isLoading: boolean; + queries: inputsModel.GlobalGraphqlQuery[]; + filterQuery: Query; + savedQuery?: SavedQuery; + start: number; + toStr: string; +} + +interface SiemSearchBarDispatch { + updateSearch: DispatchUpdateSearch; + setSavedQuery: ({ + id, + savedQuery, + }: { + id: InputsModelId; + savedQuery: SavedQuery | undefined; + }) => void; + setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => void; +} + +interface SiemSearchBarProps { + id: InputsModelId; + indexPattern: StaticIndexPattern; + timelineId?: string; +} + +const SearchBarContainer = styled.div` + .globalQueryBar { + padding: 0px; + } +`; + +const SearchBarComponent = memo( + ({ + end, + filterQuery, + fromStr, + id, + indexPattern, + isLoading = false, + queries, + savedQuery, + setSavedQuery, + setSearchBarFilter, + start, + timelineId, + toStr, + updateSearch, + }) => { + if (fromStr != null && toStr != null) { + timefilter.timefilter.setTime({ from: fromStr, to: toStr }); + } else if (start != null && end != null) { + timefilter.timefilter.setTime({ + from: new Date(start).toISOString(), + to: new Date(end).toISOString(), + }); + } + + const onQuerySubmit = (payload: { dateRange: TimeRange; query?: Query }) => { + const isQuickSelection = + payload.dateRange.from.includes('now') || payload.dateRange.to.includes('now'); + let updateSearchBar: UpdateReduxSearchBar = { + id, + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection, + updateTime: false, + }; + let isStateUpdated = false; + + if ( + (isQuickSelection && + (fromStr !== payload.dateRange.from || toStr !== payload.dateRange.to)) || + (!isQuickSelection && + (start !== formatDate(payload.dateRange.from) || + end !== formatDate(payload.dateRange.to))) + ) { + isStateUpdated = true; + updateSearchBar.updateTime = true; + updateSearchBar.end = payload.dateRange.to; + updateSearchBar.start = payload.dateRange.from; + } + + if (payload.query != null && !isEqual(payload.query, filterQuery)) { + isStateUpdated = true; + updateSearchBar = set('query', payload.query, updateSearchBar); + } + + if (!isStateUpdated) { + // That mean we are doing a refresh! + if (isQuickSelection) { + updateSearchBar.updateTime = true; + updateSearchBar.end = payload.dateRange.to; + updateSearchBar.start = payload.dateRange.from; + } else { + queries.forEach(q => q.refetch && (q.refetch as inputsModel.Refetch)()); + } + } + + window.setTimeout(() => updateSearch(updateSearchBar), 0); + }; + + const onRefresh = (payload: { dateRange: TimeRange }) => { + if (payload.dateRange.from.includes('now') || payload.dateRange.to.includes('now')) { + updateSearch({ + id, + end: payload.dateRange.to, + start: payload.dateRange.from, + isInvalid: false, + isQuickSelection: true, + updateTime: true, + }); + } else { + queries.forEach(q => q.refetch && (q.refetch as inputsModel.Refetch)()); + } + }; + + const onSaved = (newSavedQuery: SavedQuery) => { + setSavedQuery({ id, savedQuery: newSavedQuery }); + }; + + const onSavedQueryUpdated = (savedQueryUpdated: SavedQuery) => { + const isQuickSelection = savedQueryUpdated.attributes.timefilter + ? savedQueryUpdated.attributes.timefilter.from.includes('now') || + savedQueryUpdated.attributes.timefilter.to.includes('now') + : false; + + let updateSearchBar: UpdateReduxSearchBar = { + id, + filters: savedQueryUpdated.attributes.filters || [], + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection, + updateTime: false, + }; + + if (savedQueryUpdated.attributes.timefilter) { + updateSearchBar.end = savedQueryUpdated.attributes.timefilter + ? savedQueryUpdated.attributes.timefilter.to + : updateSearchBar.end; + updateSearchBar.start = savedQueryUpdated.attributes.timefilter + ? savedQueryUpdated.attributes.timefilter.from + : updateSearchBar.start; + updateSearchBar.updateTime = true; + } + + updateSearchBar = set('query', savedQueryUpdated.attributes.query, updateSearchBar); + updateSearchBar = set('savedQuery', savedQueryUpdated, updateSearchBar); + + updateSearch(updateSearchBar); + }; + + const onClearSavedQuery = () => { + if (savedQuery != null) { + updateSearch({ + id, + filters: [], + end: toStr != null ? toStr : new Date(end).toISOString(), + start: fromStr != null ? fromStr : new Date(start).toISOString(), + isInvalid: false, + isQuickSelection: false, + updateTime: false, + query: { + query: '', + language: savedQuery.attributes.query.language, + }, + resetSavedQuery: true, + savedQuery: undefined, + }); + } + }; + + useEffect(() => { + let isSubscribed = true; + const subscriptions = new Subscription(); + + subscriptions.add( + siemFilterManager.getUpdates$().subscribe({ + next: () => { + if (isSubscribed) { + setSearchBarFilter({ + id, + filters: siemFilterManager.getFilters(), + }); + } + }, + }) + ); + + return () => { + isSubscribed = false; + subscriptions.unsubscribe(); + }; + }, []); + + return ( + + + + ); + } +); + +const makeMapStateToProps = () => { + const getEndSelector = endSelector(); + const getFromStrSelector = fromStrSelector(); + const getIsLoadingSelector = isLoadingSelector(); + const getKindSelector = kindSelector(); + const getQueriesSelector = queriesSelector(); + const getStartSelector = startSelector(); + const getToStrSelector = toStrSelector(); + const getFilterQuerySelector = filterQuerySelector(); + const getSavedQuerySelector = savedQuerySelector(); + return (state: State, { id }: SiemSearchBarProps) => { + const inputsRange: InputsRange = getOr({}, `inputs.${id}`, state); + return { + end: getEndSelector(inputsRange), + fromStr: getFromStrSelector(inputsRange), + filterQuery: getFilterQuerySelector(inputsRange), + isLoading: getIsLoadingSelector(inputsRange), + kind: getKindSelector(inputsRange), + queries: getQueriesSelector(inputsRange), + savedQuery: getSavedQuerySelector(inputsRange), + start: getStartSelector(inputsRange), + toStr: getToStrSelector(inputsRange), + }; + }; +}; + +SearchBarComponent.displayName = 'SiemSearchBar'; + +interface UpdateReduxSearchBar extends OnTimeChangeProps { + id: InputsModelId; + filters?: Filter[]; + query?: Query; + savedQuery?: SavedQuery; + resetSavedQuery?: boolean; + timelineId?: string; + updateTime: boolean; +} + +type DispatchUpdateSearch = ({ + end, + id, + isQuickSelection, + start, + timelineId, +}: UpdateReduxSearchBar) => void; + +export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ + end, + filters, + id, + isQuickSelection, + query, + resetSavedQuery, + savedQuery, + start, + timelineId, + updateTime = false, +}: UpdateReduxSearchBar): void => { + if (updateTime) { + const fromDate = formatDate(start); + let toDate = formatDate(end, { roundUp: true }); + if (isQuickSelection) { + dispatch( + inputsActions.setRelativeRangeDatePicker({ + id, + fromStr: start, + toStr: end, + from: fromDate, + to: toDate, + }) + ); + } else { + toDate = formatDate(end); + dispatch( + inputsActions.setAbsoluteRangeDatePicker({ + id, + from: formatDate(start), + to: formatDate(end), + }) + ); + } + if (timelineId != null) { + dispatch( + timelineActions.updateRange({ + id: timelineId, + start: fromDate, + end: toDate, + }) + ); + } + } + if (query != null) { + dispatch( + inputsActions.setFilterQuery({ + id, + ...query, + }) + ); + } + if (filters != null) { + siemFilterManager.setFilters(filters); + } + if (savedQuery != null || resetSavedQuery) { + dispatch(inputsActions.setSavedQuery({ id, savedQuery })); + } + + dispatch(hostsActions.setHostTablesActivePageToZero()); + dispatch(networkActions.setNetworkTablesActivePageToZero()); +}; + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + updateSearch: dispatchUpdateSearch(dispatch), + setSavedQuery: ({ id, savedQuery }: { id: InputsModelId; savedQuery: SavedQuery | undefined }) => + dispatch(inputsActions.setSavedQuery({ id, savedQuery })), + setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => + dispatch(inputsActions.setSearchBarFilter({ id, filters })), +}); + +export const SiemSearchBar = connect( + makeMapStateToProps, + mapDispatchToProps +)(SearchBarComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts b/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts new file mode 100644 index 0000000000000..cfd7cd840dac8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createSelector } from 'reselect'; +import { Query } from 'src/plugins/data/common'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { InputsRange } from '../../store/inputs/model'; + +export { + endSelector, + fromStrSelector, + isLoadingSelector, + kindSelector, + queriesSelector, + startSelector, + toStrSelector, +} from '../super_date_picker/selectors'; + +export const getFilterQuery = (inputState: InputsRange): Query => inputState.query; + +export const getSavedQuery = (inputState: InputsRange): SavedQuery | undefined => + inputState.savedQuery; + +export const filterQuerySelector = () => + createSelector( + getFilterQuery, + filterQuery => filterQuery + ); + +export const savedQuerySelector = () => + createSelector( + getSavedQuery, + savedQuery => savedQuery + ); diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.test.tsx index 5c2ae38ed4b62..8e5f7a92e348d 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.test.tsx @@ -428,7 +428,7 @@ describe('SIEM Super Date Picker', () => { const mapStateToProps = makeMapStateToProps(); const props1 = mapStateToProps(state, { id: 'global' }); const clone = cloneDeep(state); - clone.inputs.global.query = [ + clone.inputs.global.queries = [ { loading: true, id: '1', @@ -446,7 +446,7 @@ describe('SIEM Super Date Picker', () => { const mapStateToProps = makeMapStateToProps(); const props1 = mapStateToProps(state, { id: 'global' }); const clone = cloneDeep(state); - clone.inputs.global.query = [ + clone.inputs.global.queries = [ { loading: true, id: '1', diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx index fa695d76f9f3e..2d93fde336d3a 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx @@ -18,7 +18,7 @@ import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { inputsModel, State } from '../../store'; -import { inputsActions, timelineActions, hostsActions, networkActions } from '../../store/actions'; +import { inputsActions, timelineActions } from '../../store/actions'; import { InputsModelId } from '../../store/inputs/constants'; import { policySelector, @@ -55,18 +55,13 @@ interface UpdateReduxTime extends OnTimeChangeProps { timelineId?: string; } -interface ReturnUpdateReduxTime { - kqlHasBeenUpdated: boolean; -} - -type DispatchUpdateReduxTime = ({ +export type DispatchUpdateReduxTime = ({ end, id, isQuickSelection, - kql, start, timelineId, -}: UpdateReduxTime) => ReturnUpdateReduxTime; +}: UpdateReduxTime) => void; interface SuperDatePickerDispatchProps { setDuration: ({ id, duration }: { id: InputsModelId; duration: number }) => void; @@ -109,7 +104,7 @@ export const SuperDatePickerComponent = React.memo( [] ); const onRefresh = ({ start: newStart, end: newEnd }: OnRefreshProps): void => { - const { kqlHasBeenUpdated } = updateReduxTime({ + updateReduxTime({ end: newEnd, id, isInvalid: false, @@ -122,10 +117,7 @@ export const SuperDatePickerComponent = React.memo( const currentEnd = isQuickSelection ? formatDate(newEnd, { roundUp: true }) : formatDate(newEnd); - if ( - !kqlHasBeenUpdated && - (!isQuickSelection || (start === currentStart && end === currentEnd)) - ) { + if (!isQuickSelection || (start === currentStart && end === currentEnd)) { refetchQuery(queries); } }; @@ -201,7 +193,7 @@ export const SuperDatePickerComponent = React.memo( } ); -const formatDate = ( +export const formatDate = ( date: string, options?: { roundUp?: boolean; @@ -211,14 +203,13 @@ const formatDate = ( return momentDate != null && momentDate.isValid() ? momentDate.valueOf() : 0; }; -const dispatchUpdateReduxTime = (dispatch: Dispatch) => ({ +export const dispatchUpdateReduxTime = (dispatch: Dispatch) => ({ end, id, isQuickSelection, - kql, start, timelineId, -}: UpdateReduxTime): ReturnUpdateReduxTime => { +}: UpdateReduxTime): void => { const fromDate = formatDate(start); let toDate = formatDate(end, { roundUp: true }); if (isQuickSelection) { @@ -250,21 +241,6 @@ const dispatchUpdateReduxTime = (dispatch: Dispatch) => ({ }) ); } - - let kqlHasBeenUpdated = false; - if (kql) { - // if refetch is successful, it will have a side effect - // to set all the tables on its activePage to zero (meaning pagination) - kqlHasBeenUpdated = kql.refetch(dispatch); - } - if (!kqlHasBeenUpdated) { - // Date picker is global to all the page in the app - // (Hosts/Network are the only one having tables) - dispatch(hostsActions.setHostTablesActivePageToZero()); - dispatch(networkActions.setNetworkTablesActivePageToZero()); - } - - return { kqlHasBeenUpdated }; }; export const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.test.ts b/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.test.ts index 2e42ed791f3d8..1dafa141542bf 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.test.ts @@ -33,8 +33,13 @@ describe('selectors', () => { kind: 'manual', duration: 0, }, - query: [], + queries: [], linkTo: [], + query: { + query: '', + language: 'kuery', + }, + filters: [], }; const getPolicySelector = policySelector(); @@ -62,8 +67,13 @@ describe('selectors', () => { kind: 'manual', duration: 0, }, - query: [], + queries: [], linkTo: [], + query: { + query: '', + language: 'kuery', + }, + filters: [], }; }); @@ -295,7 +305,7 @@ describe('selectors', () => { const result1 = getIsLoadingSelector(inputState); const change: InputsRange = { ...inputState, - query: [ + queries: [ { loading: true, id: '1', @@ -313,7 +323,7 @@ describe('selectors', () => { test('returns false if there are no queries loading', () => { const inputsRange: InputsRange = { ...inputState, - query: [ + queries: [ { loading: false, id: '1', @@ -339,7 +349,7 @@ describe('selectors', () => { test('returns true if at least one query is loading', () => { const inputsRange: InputsRange = { ...inputState, - query: [ + queries: [ { loading: false, id: '1', @@ -381,7 +391,7 @@ describe('selectors', () => { const result1 = getQueriesSelector(inputState); const change: InputsRange = { ...inputState, - query: [ + queries: [ { loading: false, id: '1', diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.ts b/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.ts index 7f2acd17ce799..59f55f33842fe 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/selectors.ts @@ -11,7 +11,7 @@ export const getPolicy = (inputState: InputsRange): Policy => inputState.policy; export const getTimerange = (inputState: InputsRange): TimeRange => inputState.timerange; -export const getQuery = (inputState: InputsRange): GlobalQuery[] => inputState.query; +export const getQueries = (inputState: InputsRange): GlobalQuery[] => inputState.queries; export const policySelector = () => createSelector( @@ -57,18 +57,18 @@ export const toStrSelector = () => export const isLoadingSelector = () => createSelector( - getQuery, - query => query.some(i => i.loading === true) + getQueries, + queries => queries.some(i => i.loading === true) ); export const queriesSelector = () => createSelector( - getQuery, - query => query.filter(q => q.id !== 'kql') + getQueries, + queries => queries.filter(q => q.id !== 'kql') ); export const kqlQuerySelector = () => createSelector( - getQuery, - query => query.find(q => q.id === 'kql') + getQueries, + queries => queries.find(q => q.id === 'kql') ); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap index 47f5bc1b04c3c..a2d20ff0b8d18 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/header/__snapshots__/index.test.tsx.snap @@ -231,6 +231,12 @@ exports[`Header rendering renders correctly against snapshot 1`] = ` "searchable": true, "type": "string", }, + Object { + "aggregatable": true, + "name": "host.name", + "searchable": true, + "type": "string", + }, ], "title": "filebeat-*,auditbeat-*,packetbeat-*", } diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx index 34c9d2b0c6683..8ab694df32c6d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx @@ -5,8 +5,10 @@ */ import { cloneDeep } from 'lodash/fp'; +import { npSetup } from 'ui/new_platform'; import { mockIndexPattern } from '../../mock'; +import { mockUiSettings, MockNpSetUp } from '../../mock/ui_settings'; import { mockDataProviders } from './data_providers/mock/mock_data_providers'; import { buildGlobalQuery, combineQueries } from './helpers'; @@ -16,6 +18,10 @@ const cleanUpKqlQuery = (str: string) => str.replace(/\n/g, '').replace(/\s\s+/g const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf(); const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf(); +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; + describe('Build KQL Query', () => { test('Build KQL query with one data provider', () => { const dataProviders = mockDataProviders.slice(0, 1); @@ -118,42 +124,53 @@ describe('Build KQL Query', () => { describe('Combined Queries', () => { test('No Data Provider & No kqlQuery & and isEventViewer is false', () => { expect( - combineQueries([], mockIndexPattern, mockBrowserFields, '', 'search', startDate, endDate) + combineQueries({ + dataProviders: [], + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + }) ).toBeNull(); }); test('No Data Provider & No kqlQuery & isEventViewer is true', () => { const isEventViewer = true; expect( - combineQueries( - [], - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate, - isEventViewer - ) + combineQueries({ + dataProviders: [], + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + isEventViewer, + }) ).toEqual({ filterQuery: - '{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}', + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}', }); }); test('Only Data Provider', () => { const dataProviders = mockDataProviders.slice(0, 1); - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -161,17 +178,18 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = '@timestamp'; dataProviders[0].queryMatch.value = '2018-03-23T23:36:23.232Z'; - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232}}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -179,17 +197,18 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = '@timestamp'; dataProviders[0].queryMatch.value = 1521848183232; - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232}}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521848183232,"lte":1521848183232,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -197,17 +216,18 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = 'event.end'; dataProviders[0].queryMatch.value = '2018-03-23T23:36:23.232Z'; - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -215,64 +235,68 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 1)); dataProviders[0].queryMatch.field = 'event.end'; dataProviders[0].queryMatch.value = 1521848183232; - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - '', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: '', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match":{"event.end":1521848183232}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); test('Only KQL search/filter query', () => { - const { filterQuery } = combineQueries( - [], - mockIndexPattern, - mockBrowserFields, - 'host.name: "host-1"', - 'search', - startDate, - endDate - )!; + const { filterQuery } = combineQueries({ + dataProviders: [], + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); test('Data Provider & KQL search query', () => { const dataProviders = mockDataProviders.slice(0, 1); - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - 'host.name: "host-1"', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); test('Data Provider & KQL filter query', () => { const dataProviders = mockDataProviders.slice(0, 1); - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - 'host.name: "host-1"', - 'filter', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' }, + kqlMode: 'filter', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -280,17 +304,18 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); dataProviders[0].and = mockDataProviders.slice(2, 4); dataProviders[1].and = mockDataProviders.slice(4, 5); - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - 'host.name: "host-1"', - 'search', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' }, + kqlMode: 'search', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"should":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"should":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); @@ -298,17 +323,18 @@ describe('Combined Queries', () => { const dataProviders = cloneDeep(mockDataProviders.slice(0, 2)); dataProviders[0].and = mockDataProviders.slice(2, 4); dataProviders[1].and = mockDataProviders.slice(4, 5); - const { filterQuery } = combineQueries( + const { filterQuery } = combineQueries({ dataProviders, - mockIndexPattern, - mockBrowserFields, - 'host.name: "host-1"', - 'filter', - startDate, - endDate - )!; + indexPattern: mockIndexPattern, + browserFields: mockBrowserFields, + filters: [], + kqlQuery: { query: 'host.name: "host-1"', language: 'kuery' }, + kqlMode: 'filter', + start: startDate, + end: endDate, + })!; expect(filterQuery).toEqual( - '{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}' + '{"bool":{"must":[],"filter":[{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 3"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 4"}}],"minimum_should_match":1}}]}}]}},{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"name":"Provider 2"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"name":"Provider 5"}}],"minimum_should_match":1}}]}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132,"time_zone":"America/New_York"}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253,"time_zone":"America/New_York"}}}],"minimum_should_match":1}}]}}]}}],"should":[],"must_not":[]}}' ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx index eaf6728a3eef7..b56145d3a058f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { isEmpty, isNumber, get } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { StaticIndexPattern } from 'ui/index_patterns'; +import { Query } from 'src/plugins/data/common'; -import { convertKueryToElasticSearchQuery, escapeQueryValue } from '../../lib/keury'; +import { escapeQueryValue, convertToBuildEsQuery } from '../../lib/keury'; import { DataProvider, DataProvidersAnd, EXISTS_OPERATOR } from './data_providers/data_provider'; import { BrowserFields } from '../../containers/source'; @@ -88,45 +90,56 @@ export const buildGlobalQuery = (dataProviders: DataProvider[], browserFields: B }, '') .trim(); -export const combineQueries = ( - dataProviders: DataProvider[], - indexPattern: StaticIndexPattern, - browserFields: BrowserFields, - kqlQuery: string, - kqlMode: string, - start: number, - end: number, - isEventViewer?: boolean -): { filterQuery: string } | null => { - let kuery: string; - if (isEmpty(dataProviders) && isEmpty(kqlQuery) && !isEventViewer) { +export const combineQueries = ({ + dataProviders, + indexPattern, + browserFields, + filters = [], + kqlQuery, + kqlMode, + start, + end, + isEventViewer, +}: { + dataProviders: DataProvider[]; + indexPattern: StaticIndexPattern; + browserFields: BrowserFields; + filters: Filter[]; + kqlQuery: Query; + kqlMode: string; + start: number; + end: number; + isEventViewer?: boolean; +}): { filterQuery: string } | null => { + const kuery: Query = { query: '', language: kqlQuery.language }; + if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && !isEventViewer) { return null; - } else if (isEmpty(dataProviders) && isEmpty(kqlQuery) && isEventViewer) { - kuery = `@timestamp >= ${start} and @timestamp <= ${end}`; + } else if (isEmpty(dataProviders) && isEmpty(kqlQuery.query) && isEventViewer) { + kuery.query = `@timestamp >= ${start} and @timestamp <= ${end}`; return { - filterQuery: convertKueryToElasticSearchQuery(kuery, indexPattern), + filterQuery: convertToBuildEsQuery({ queries: [kuery], indexPattern, filters }), }; - } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery)) { - kuery = `(${kqlQuery}) and @timestamp >= ${start} and @timestamp <= ${end}`; + } else if (isEmpty(dataProviders) && !isEmpty(kqlQuery.query)) { + kuery.query = `(${kqlQuery.query}) and @timestamp >= ${start} and @timestamp <= ${end}`; return { - filterQuery: convertKueryToElasticSearchQuery(kuery, indexPattern), + filterQuery: convertToBuildEsQuery({ queries: [kuery], indexPattern, filters }), }; } else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) { - kuery = `(${buildGlobalQuery( + kuery.query = `(${buildGlobalQuery( dataProviders, browserFields )}) and @timestamp >= ${start} and @timestamp <= ${end}`; return { - filterQuery: convertKueryToElasticSearchQuery(kuery, indexPattern), + filterQuery: convertToBuildEsQuery({ queries: [kuery], indexPattern, filters }), }; } const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or'; const postpend = (q: string) => `${!isEmpty(q) ? ` ${operatorKqlQuery} (${q})` : ''}`; - kuery = `((${buildGlobalQuery(dataProviders, browserFields)})${postpend( - kqlQuery + kuery.query = `((${buildGlobalQuery(dataProviders, browserFields)})${postpend( + kqlQuery.query as string )}) and @timestamp >= ${start} and @timestamp <= ${end}`; return { - filterQuery: convertKueryToElasticSearchQuery(kuery, indexPattern), + filterQuery: convertToBuildEsQuery({ queries: [kuery], indexPattern, filters }), }; }; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index 5101d55792369..fdf8381685118 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -111,15 +111,16 @@ export const Timeline = React.memo( sort, toggleColumn, }) => { - const combinedQueries = combineQueries( + const combinedQueries = combineQueries({ dataProviders, indexPattern, browserFields, - kqlQueryExpression, + filters: [], + kqlQuery: { query: kqlQueryExpression, language: 'keury' }, kqlMode, start, - end - ); + end, + }); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; return ( diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts index e0ecfc1640bbe..c709a9370ec61 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts @@ -5,15 +5,17 @@ */ export enum CONSTANTS { + appQuery = 'query', + filters = 'filters', + savedQuery = 'savedQuery', hostsDetails = 'hosts.details', hostsPage = 'hosts.page', - kqlQuery = 'kqlQuery', networkDetails = 'network.details', networkPage = 'network.page', overviewPage = 'overview.page', timelinePage = 'timeline.page', timerange = 'timerange', - timelineId = 'timelineId', + timeline = 'timeline', unknown = 'unknown', } diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index 64a53a8402a57..67b712338b81d 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -4,14 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { decode, encode, RisonValue } from 'rison-node'; import { Location } from 'history'; import { QueryString } from 'ui/utils/query_string'; +import { Query } from 'src/plugins/data/common'; +import { inputsSelectors, State, timelineSelectors } from '../../store'; import { SiemPageName } from '../../pages/home/home_navigations'; import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; -import { LocationTypes } from './types'; +import { LocationTypes, UrlStateContainerPropTypes } from './types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const decodeRisonUrlState = (value: string | undefined): RisonValue | any | undefined => { @@ -41,12 +44,7 @@ export const replaceStateKeyInQueryString = ( urlState: UrlState | undefined ) => (queryString: string) => { const previousQueryValues = QueryString.decode(queryString); - if ( - urlState == null || - (typeof urlState === 'string' && urlState === '') || - (urlState && urlState.filterQuery === null) || - (urlState && urlState.filterQuery != null && urlState.filterQuery.expression === '') - ) { + if (urlState == null || (typeof urlState === 'string' && urlState === '')) { delete previousQueryValues[stateKey]; return QueryString.encode({ ...previousQueryValues, @@ -134,3 +132,58 @@ export const isKqlForRoute = ( } return false; }; + +export const makeMapStateToProps = () => { + const getInputsSelector = inputsSelectors.inputsSelector(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); + const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector(); + const getTimelines = timelineSelectors.getTimelines(); + const mapStateToProps = (state: State, { pageName, detailName }: UrlStateContainerPropTypes) => { + const inputState = getInputsSelector(state); + const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; + const { linkTo: timelineLinkTo, timerange: timelineTimerange } = inputState.timeline; + + const timeline = Object.entries(getTimelines(state)).reduce( + (obj, [timelineId, timelineObj]) => ({ + id: timelineObj.savedObjectId != null ? timelineObj.savedObjectId : '', + isOpen: timelineObj.show, + }), + { id: '', isOpen: false } + ); + + let searchAttr: { + [CONSTANTS.appQuery]?: Query; + [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.savedQuery]?: string; + } = { + [CONSTANTS.appQuery]: getGlobalQuerySelector(state), + [CONSTANTS.filters]: getGlobalFiltersQuerySelector(state), + }; + const savedQuery = getGlobalSavedQuerySelector(state); + if (savedQuery != null && savedQuery.id !== '') { + searchAttr = { + [CONSTANTS.savedQuery]: savedQuery.id, + }; + } + + return { + urlState: { + ...searchAttr, + [CONSTANTS.timerange]: { + global: { + [CONSTANTS.timerange]: globalTimerange, + linkTo: globalLinkTo, + }, + timeline: { + [CONSTANTS.timerange]: timelineTimerange, + linkTo: timelineLinkTo, + }, + }, + [CONSTANTS.timeline]: timeline, + }, + }; + }; + + return mapStateToProps; +}; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx index 0d31f17fb0756..183d4f0351410 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx @@ -8,20 +8,20 @@ import { mount } from 'enzyme'; import * as React from 'react'; import { HookWrapper } from '../../mock'; +import { SiemPageName } from '../../pages/home/home_navigations'; +import { RouteSpyState } from '../../utils/route/types'; + +import { CONSTANTS } from './constants'; import { getMockPropsObj, mockHistory, + mockSetFilterQuery, mockSetAbsoluteRangeDatePicker, mockSetRelativeRangeDatePicker, testCases, - mockApplyHostsFilterQuery, - mockApplyNetworkFilterQuery, } from './test_dependencies'; import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; -import { CONSTANTS } from './constants'; -import { RouteSpyState } from '../../utils/route/types'; -import { SiemPageName } from '../../pages/home/home_navigations'; let mockProps: UrlStateContainerPropTypes; @@ -37,6 +37,12 @@ jest.mock('../../utils/route/use_route_spy', () => ({ useRouteSpy: () => [mockRouteSpy], })); +jest.mock('../search_bar', () => ({ + siemFilterManager: { + setFilters: jest.fn(), + }, +})); + describe('UrlStateContainer', () => { afterEach(() => { jest.resetAllMocks(); @@ -56,7 +62,6 @@ describe('UrlStateContainer', () => { }).relativeTimeSearch.undefinedQuery; mount( useUrlStateHooks(args)} />); - // @ts-ignore property mock does not exists expect(mockSetRelativeRangeDatePicker.mock.calls[1][0]).toEqual({ from: 1558591200000, fromStr: 'now-1d/d', @@ -65,7 +70,7 @@ describe('UrlStateContainer', () => { toStr: 'now-1d/d', id: 'global', }); - // @ts-ignore property mock does not exists + expect(mockSetRelativeRangeDatePicker.mock.calls[0][0]).toEqual({ from: 1558732849370, fromStr: 'now-15m', @@ -86,14 +91,13 @@ describe('UrlStateContainer', () => { .absoluteTimeSearch.undefinedQuery; mount( useUrlStateHooks(args)} />); - // @ts-ignore property mock does not exists expect(mockSetAbsoluteRangeDatePicker.mock.calls[1][0]).toEqual({ from: 1556736012685, kind: 'absolute', to: 1556822416082, id: 'global', }); - // @ts-ignore property mock does not exists + expect(mockSetAbsoluteRangeDatePicker.mock.calls[0][0]).toEqual({ from: 1556736012685, kind: 'absolute', @@ -104,59 +108,26 @@ describe('UrlStateContainer', () => { ); }); - describe('kqlQuery action is called with correct data on component mount', () => { - const serializedFilterQuery = { - kuery: { - expression: 'host.name:"siem-es"', - kind: 'kuery', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"siem-es"}}],"minimum_should_match":1}}', - }; + describe('appQuery action is called with correct data on component mount', () => { test.each(testCases.slice(0, 4))( ' %o', (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { mockProps = getMockPropsObj({ page, examplePath, namespaceLower, pageName, detailName }) .relativeTimeSearch.undefinedQuery; mount( useUrlStateHooks(args)} />); - const functionName = - namespaceUpper === 'Network' - ? mockApplyNetworkFilterQuery - : mockApplyHostsFilterQuery; - // @ts-ignore property mock does not exists - expect(functionName.mock.calls[0][0]).toEqual({ - filterQuery: serializedFilterQuery, - [`${namespaceLower}Type`]: type, - }); - } - ); - }); - describe('kqlQuery action is not called called when the queryLocation does not match the router location', () => { - test.each(testCases)( - '%o', - (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { - mockProps = getMockPropsObj({ - page, - examplePath, - namespaceLower, - pageName, - detailName, - }).oppositeQueryLocationSearch.undefinedQuery; - mount( useUrlStateHooks(args)} />); - const functionName = - namespaceUpper === 'Network' - ? mockApplyNetworkFilterQuery - : mockApplyHostsFilterQuery; - // @ts-ignore property mock does not exists - expect(functionName.mock.calls.length).toEqual(0); + expect(mockSetFilterQuery.mock.calls[0][0]).toEqual({ + id: 'global', + language: 'kuery', + query: 'host.name:"siem-es"', + }); } ); }); }); describe('Redux updates URL state', () => { - describe('kqlQuery url state is set from redux data on component mount', () => { + describe('appQuery url state is set from redux data on component mount', () => { test.each(testCases)( '%o', (page, namespaceLower, namespaceUpper, examplePath, type, pageName, detailName) => { @@ -169,7 +140,6 @@ describe('UrlStateContainer', () => { }).noSearch.definedQuery; mount( useUrlStateHooks(args)} />); - // @ts-ignore property mock does not exists expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0] ).toEqual({ @@ -177,7 +147,7 @@ describe('UrlStateContainer', () => { pathname: examplePath, search: [CONSTANTS.overviewPage, CONSTANTS.timelinePage].includes(page) ? '?_g=()&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))' - : `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page})&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + : `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, state: '', }); } diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx index 1c93ba023ad10..b58b60587c863 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.tsx @@ -4,30 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEqual } from 'lodash/fp'; import React from 'react'; import { compose, Dispatch } from 'redux'; import { connect } from 'react-redux'; -import { isEqual } from 'lodash/fp'; -import { - hostsModel, - hostsSelectors, - inputsSelectors, - networkModel, - networkSelectors, - State, - timelineSelectors, -} from '../../store'; import { timelineActions } from '../../store/actions'; import { RouteSpyState } from '../../utils/route/types'; import { useRouteSpy } from '../../utils/route/use_route_spy'; -import { CONSTANTS } from './constants'; -import { UrlStateContainerPropTypes, UrlStateProps, KqlQuery, LocationTypes } from './types'; +import { UrlStateContainerPropTypes, UrlStateProps } from './types'; import { useUrlStateHooks } from './use_url_state'; import { dispatchUpdateTimeline } from '../open_timeline/helpers'; -import { getCurrentLocation } from './helpers'; import { dispatchSetInitialStateFromUrl } from './initialize_redux_by_url'; +import { makeMapStateToProps } from './helpers'; export const UrlStateContainer = React.memo( (props: UrlStateContainerPropTypes) => { @@ -40,70 +30,6 @@ export const UrlStateContainer = React.memo( UrlStateContainer.displayName = 'UrlStateContainer'; -const makeMapStateToProps = () => { - const getInputsSelector = inputsSelectors.inputsSelector(); - const getHostsFilterQueryAsKuery = hostsSelectors.hostsFilterQueryAsKuery(); - const getNetworkFilterQueryAsKuery = networkSelectors.networkFilterQueryAsKuery(); - const getTimelines = timelineSelectors.getTimelines(); - const mapStateToProps = (state: State, { pageName, detailName }: UrlStateContainerPropTypes) => { - const inputState = getInputsSelector(state); - const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; - const { linkTo: timelineLinkTo, timerange: timelineTimerange } = inputState.timeline; - - const page: LocationTypes | null = getCurrentLocation(pageName, detailName); - const kqlQueryInitialState: KqlQuery = { - filterQuery: null, - queryLocation: page, - }; - if (page === CONSTANTS.hostsPage) { - kqlQueryInitialState.filterQuery = getHostsFilterQueryAsKuery( - state, - hostsModel.HostsType.page - ); - } else if (page === CONSTANTS.hostsDetails) { - kqlQueryInitialState.filterQuery = getHostsFilterQueryAsKuery( - state, - hostsModel.HostsType.details - ); - } else if (page === CONSTANTS.networkPage) { - kqlQueryInitialState.filterQuery = getNetworkFilterQueryAsKuery( - state, - networkModel.NetworkType.page - ); - } else if (page === CONSTANTS.networkDetails) { - kqlQueryInitialState.filterQuery = getNetworkFilterQueryAsKuery( - state, - networkModel.NetworkType.details - ); - } - - const openTimelineId = Object.entries(getTimelines(state)).reduce( - (useTimelineId, [timelineId, timelineObj]) => - timelineObj.savedObjectId != null ? timelineObj.savedObjectId : useTimelineId, - '' - ); - - return { - urlState: { - [CONSTANTS.timerange]: { - global: { - [CONSTANTS.timerange]: globalTimerange, - linkTo: globalLinkTo, - }, - timeline: { - [CONSTANTS.timerange]: timelineTimerange, - linkTo: timelineLinkTo, - }, - }, - [CONSTANTS.kqlQuery]: kqlQueryInitialState, - [CONSTANTS.timelineId]: openTimelineId, - }, - }; - }; - - return mapStateToProps; -}; - const mapDispatchToProps = (dispatch: Dispatch) => ({ setInitialStateFromUrl: dispatchSetInitialStateFromUrl(dispatch), updateTimeline: dispatchUpdateTimeline(dispatch), diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx index 6598bd491f73b..c70b72a407db7 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx @@ -5,7 +5,6 @@ */ import { mount } from 'enzyme'; -import { difference } from 'lodash/fp'; import * as React from 'react'; import { HookWrapper } from '../../mock/hook_wrapper'; @@ -16,18 +15,17 @@ import { getFilterQuery, getMockPropsObj, mockHistory, testCases } from './test_ import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; -jest.mock('lodash/fp'); +jest.mock('../search_bar', () => ({ + siemFilterManager: { + addFilters: jest.fn(), + }, +})); let mockProps: UrlStateContainerPropTypes; describe('UrlStateContainer - lodash.throttle mocked to test update url', () => { - beforeEach(() => { - // @ts-ignore property mockImplementation does not exists - difference.mockImplementation((all, items) => - all.filter((item: string) => !items.includes(item)) - ); - }); afterEach(() => { + jest.clearAllMocks(); jest.resetAllMocks(); }); @@ -78,7 +76,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))", + "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))", state: '', }); }); @@ -96,10 +94,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => ); const newUrlState = { ...mockProps.urlState, - [CONSTANTS.kqlQuery]: { - ...getFilterQuery(CONSTANTS.networkPage), - queryLocation: CONSTANTS.networkPage, - }, + [CONSTANTS.appQuery]: getFilterQuery(), }; wrapper.setProps({ hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false }, @@ -112,7 +107,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - "?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))", + "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))", state: '', }); }); @@ -125,13 +120,15 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => pageName: SiemPageName.network, detailName: undefined, }).noSearch.undefinedQuery; + const wrapper = mount( useUrlStateHooks(args)} /> ); const newUrlState = { ...mockProps.urlState, - timelineId: 'hello_timeline_id', + timeline: { id: 'hello_timeline_id', isOpen: true }, }; + wrapper.setProps({ hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false }, }); @@ -143,7 +140,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => hash: '', pathname: '/network', search: - '?_g=()&timelineId=hello_timeline_id&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?_g=()&timeline=(id:hello_timeline_id,isOpen:!t)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', state: '', }); }); @@ -209,7 +206,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => expect( mockHistory.replace.mock.calls[mockHistory.replace.mock.calls.length - 1][0].search ).toEqual( - "?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:network.page)&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" + "?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx index b773770966991..fa3b278866704 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { get, isEmpty } from 'lodash/fp'; import { Dispatch } from 'redux'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; +import { Query } from 'src/plugins/data/common'; -import { hostsActions, inputsActions, networkActions } from '../../store/actions'; +import { inputsActions } from '../../store/actions'; import { InputsModelId, TimeRangeKinds } from '../../store/inputs/constants'; import { UrlInputsModel, @@ -15,14 +18,12 @@ import { AbsoluteTimeRange, RelativeTimeRange, } from '../../store/inputs/model'; +import { savedQueryService, siemFilterManager } from '../search_bar'; import { CONSTANTS } from './constants'; -import { decodeRisonUrlState, isKqlForRoute, getCurrentLocation } from './helpers'; +import { decodeRisonUrlState } from './helpers'; import { normalizeTimeRange } from './normalize_time_range'; -import { DispatchSetInitialStateFromUrl, KqlQuery, SetInitialStateFromUrl } from './types'; -import { convertKueryToElasticSearchQuery } from '../../lib/keury'; -import { HostsType } from '../../store/hosts/model'; -import { NetworkType } from '../../store/network/model'; +import { DispatchSetInitialStateFromUrl, SetInitialStateFromUrl } from './types'; import { queryTimelineById } from '../open_timeline/helpers'; export const dispatchSetInitialStateFromUrl = ( @@ -110,42 +111,48 @@ export const dispatchSetInitialStateFromUrl = ( } } } - if (urlKey === CONSTANTS.kqlQuery && indexPattern != null) { - const kqlQueryStateData: KqlQuery = decodeRisonUrlState(newUrlStateString); - if (isKqlForRoute(pageName, detailName, kqlQueryStateData.queryLocation)) { - const filterQuery = { - kuery: kqlQueryStateData.filterQuery, - serializedQuery: convertKueryToElasticSearchQuery( - kqlQueryStateData.filterQuery ? kqlQueryStateData.filterQuery.expression : '', - indexPattern - ), - }; - const page = getCurrentLocation(pageName, detailName); - if ([CONSTANTS.hostsPage, CONSTANTS.hostsDetails].includes(page)) { - dispatch( - hostsActions.applyHostsFilterQuery({ - filterQuery, - hostsType: page === CONSTANTS.hostsPage ? HostsType.page : HostsType.details, - }) - ); - } else if ([CONSTANTS.networkPage, CONSTANTS.networkDetails].includes(page)) { + if (urlKey === CONSTANTS.appQuery && indexPattern != null) { + const appQuery: Query = decodeRisonUrlState(newUrlStateString); + if (appQuery != null) { + dispatch( + inputsActions.setFilterQuery({ + id: 'global', + query: appQuery.query, + language: appQuery.language, + }) + ); + } + } + + if (urlKey === CONSTANTS.filters) { + const filters: Filter[] = decodeRisonUrlState(newUrlStateString); + siemFilterManager.setFilters(filters || []); + } + + if (urlKey === CONSTANTS.savedQuery) { + const savedQueryId: string = decodeRisonUrlState(newUrlStateString); + if (savedQueryId !== '') { + savedQueryService.getSavedQuery(savedQueryId).then((savedQueryData: SavedQuery) => { + siemFilterManager.setFilters(savedQueryData.attributes.filters || []); dispatch( - networkActions.applyNetworkFilterQuery({ - filterQuery, - networkType: page === CONSTANTS.networkPage ? NetworkType.page : NetworkType.details, + inputsActions.setFilterQuery({ + id: 'global', + ...savedQueryData.attributes.query, }) ); - } + dispatch(inputsActions.setSavedQuery({ id: 'global', savedQuery: savedQueryData })); + }); } } - if (urlKey === CONSTANTS.timelineId) { - const timelineId = decodeRisonUrlState(newUrlStateString); - if (timelineId != null) { + if (urlKey === CONSTANTS.timeline) { + const timeline = decodeRisonUrlState(newUrlStateString); + if (timeline != null && timeline.id !== '') { queryTimelineById({ apolloClient, duplicate: false, - timelineId, + timelineId: timeline.id, + openTimeline: timeline.isOpen, updateIsLoading: updateTimelineIsLoading, updateTimeline, }); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts index ad1e1186aa41a..4a764397be081 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts @@ -5,28 +5,27 @@ */ import { ActionCreator } from 'typescript-fsa'; -import { hostsModel, networkModel } from '../../store'; -import { UrlStateContainerPropTypes, LocationTypes, KqlQuery } from './types'; -import { CONSTANTS } from './constants'; +import { Query } from 'src/plugins/data/common'; + import { DispatchUpdateTimeline } from '../open_timeline/types'; import { navTabs, SiemPageName } from '../../pages/home/home_navigations'; -import { hostsActions, inputsActions, networkActions } from '../../store/actions'; +import { hostsModel, networkModel } from '../../store'; +import { inputsActions } from '../../store/actions'; import { HostsTableType } from '../../store/hosts/model'; + +import { CONSTANTS } from './constants'; import { dispatchSetInitialStateFromUrl } from './initialize_redux_by_url'; +import { UrlStateContainerPropTypes, LocationTypes } from './types'; type Action = 'PUSH' | 'POP' | 'REPLACE'; const pop: Action = 'POP'; -export const getFilterQuery = (queryLocation: LocationTypes): KqlQuery => ({ - filterQuery: { - expression: 'host.name:"siem-es"', - kind: 'kuery', - }, - queryLocation, +export const getFilterQuery = (): Query => ({ + query: 'host.name:"siem-es"', + language: 'kuery', }); -export const mockApplyHostsFilterQuery: jest.Mock = (hostsActions.applyHostsFilterQuery as unknown) as jest.Mock; -export const mockApplyNetworkFilterQuery: jest.Mock = (networkActions.applyNetworkFilterQuery as unknown) as jest.Mock; +export const mockSetFilterQuery: jest.Mock = (inputsActions.setFilterQuery as unknown) as jest.Mock; export const mockAddGlobalLinkTo: jest.Mock = (inputsActions.addGlobalLinkTo as unknown) as jest.Mock; export const mockAddTimelineLinkTo: jest.Mock = (inputsActions.addTimelineLinkTo as unknown) as jest.Mock; export const mockRemoveGlobalLinkTo: jest.Mock = (inputsActions.removeGlobalLinkTo as unknown) as jest.Mock; @@ -35,12 +34,6 @@ export const mockSetAbsoluteRangeDatePicker: jest.Mock = (inputsActions.setAbsol export const mockSetRelativeRangeDatePicker: jest.Mock = (inputsActions.setRelativeRangeDatePicker as unknown) as jest.Mock; jest.mock('../../store/actions', () => ({ - hostsActions: { - applyHostsFilterQuery: jest.fn(), - }, - networkActions: { - applyNetworkFilterQuery: jest.fn(), - }, inputsActions: { addGlobalLinkTo: jest.fn(), addTimelineLinkTo: jest.fn(), @@ -48,6 +41,7 @@ jest.mock('../../store/actions', () => ({ removeTimelineLinkTo: jest.fn(), setAbsoluteRangeDatePicker: jest.fn(), setRelativeRangeDatePicker: jest.fn(), + setFilterQuery: jest.fn(), }, })); @@ -116,11 +110,12 @@ export const defaultProps: UrlStateContainerPropTypes = { linkTo: ['global'], }, }, - [CONSTANTS.kqlQuery]: { - filterQuery: null, - queryLocation: null, + [CONSTANTS.appQuery]: { query: '', language: 'kuery' }, + [CONSTANTS.filters]: [], + [CONSTANTS.timeline]: { + id: '', + isOpen: false, }, - [CONSTANTS.timelineId]: '', }, setInitialStateFromUrl: dispatchSetInitialStateFromUrl(mockDispatch), updateTimeline: (jest.fn() as unknown) as DispatchUpdateTimeline, @@ -137,14 +132,14 @@ export const defaultProps: UrlStateContainerPropTypes = { export const getMockProps = ( location = defaultLocation, kqlQueryKey = CONSTANTS.networkPage, - kqlQueryValue: KqlQuery | null, + kqlQueryValue: Query | null, pageName: string, detailName: string | undefined ): UrlStateContainerPropTypes => ({ ...defaultProps, urlState: { ...defaultProps.urlState, - [CONSTANTS.kqlQuery]: kqlQueryValue || { filterQuery: null, queryLocation: null }, + [CONSTANTS.appQuery]: kqlQueryValue || { query: '', language: 'kuery' }, }, history: { ...mockHistory, @@ -192,7 +187,7 @@ export const getMockPropsObj = ({ state: '', }, page, - getFilterQuery(page), + getFilterQuery(), pageName, detailName ), @@ -202,7 +197,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page})&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, @@ -214,11 +209,11 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${page})&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?_g=()&query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, - getFilterQuery(page), + getFilterQuery(), pageName, detailName ), @@ -246,7 +241,7 @@ export const getMockPropsObj = ({ state: '', }, page, - getFilterQuery(page), + getFilterQuery(), pageName, detailName ), @@ -256,9 +251,7 @@ export const getMockPropsObj = ({ { hash: '', pathname: examplePath, - search: `?_g=()&kqlQuery=(filterQuery:(expression:'host.name:%22siem-es%22',kind:kuery),queryLocation:${ - namespaceLower === 'hosts' ? 'network' : 'hosts' - }.page)&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, + search: `?_g=()&query=(query:'host.name:%22siem-es%22',language:kuery)&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, state: '', }, page, diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts index 58c261fd4c27a..f858ffc32ddbc 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; +import ApolloClient from 'apollo-client'; import { ActionCreator } from 'typescript-fsa'; import { StaticIndexPattern } from 'ui/index_patterns'; +import { Query } from 'src/plugins/data/common'; -import ApolloClient from 'apollo-client'; -import { KueryFilterQuery } from '../../store'; import { UrlInputsModel } from '../../store/inputs/model'; import { RouteSpyState } from '../../utils/route/types'; import { DispatchUpdateTimeline } from '../open_timeline/types'; @@ -17,16 +18,30 @@ import { NavTab } from '../navigation/types'; import { CONSTANTS, UrlStateType } from './constants'; export const ALL_URL_STATE_KEYS: KeyUrlState[] = [ - CONSTANTS.kqlQuery, + CONSTANTS.appQuery, + CONSTANTS.filters, + CONSTANTS.savedQuery, CONSTANTS.timerange, - CONSTANTS.timelineId, + CONSTANTS.timeline, ]; export const URL_STATE_KEYS: Record = { - host: [CONSTANTS.kqlQuery, CONSTANTS.timerange, CONSTANTS.timelineId], - network: [CONSTANTS.kqlQuery, CONSTANTS.timerange, CONSTANTS.timelineId], - timeline: [CONSTANTS.timelineId, CONSTANTS.timerange], - overview: [CONSTANTS.timelineId, CONSTANTS.timerange], + host: [ + CONSTANTS.appQuery, + CONSTANTS.filters, + CONSTANTS.savedQuery, + CONSTANTS.timerange, + CONSTANTS.timeline, + ], + network: [ + CONSTANTS.appQuery, + CONSTANTS.filters, + CONSTANTS.savedQuery, + CONSTANTS.timerange, + CONSTANTS.timeline, + ], + timeline: [CONSTANTS.timeline, CONSTANTS.timerange], + overview: [CONSTANTS.timeline, CONSTANTS.timerange], }; export type LocationTypes = @@ -38,15 +53,17 @@ export type LocationTypes = | CONSTANTS.timelinePage | CONSTANTS.unknown; -export interface KqlQuery { - filterQuery: KueryFilterQuery | null; - queryLocation: LocationTypes | null; +export interface Timeline { + id: string; + isOpen: boolean; } export interface UrlState { - [CONSTANTS.kqlQuery]: KqlQuery; + [CONSTANTS.appQuery]?: Query; + [CONSTANTS.filters]?: Filter[]; + [CONSTANTS.savedQuery]?: string; [CONSTANTS.timerange]: UrlInputsModel; - [CONSTANTS.timelineId]: string; + [CONSTANTS.timeline]: Timeline; } export type KeyUrlState = keyof UrlState; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx index 26826ace6fcfd..5b9511f169744 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx @@ -3,10 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { Filter } from '@kbn/es-query'; import { Location } from 'history'; -import { isEqual, difference } from 'lodash/fp'; +import { isEqual, difference, isEmpty } from 'lodash/fp'; import { useEffect, useRef, useState } from 'react'; +import { Query } from 'src/plugins/data/common'; import { UrlInputsModel } from '../../store/inputs/model'; import { useApolloClient } from '../../utils/apollo_context'; @@ -18,7 +19,6 @@ import { replaceStateKeyInQueryString, getParamFromQueryString, decodeRisonUrlState, - isKqlForRoute, getUrlType, getTitle, } from './helpers'; @@ -27,9 +27,9 @@ import { PreviousLocationUrlState, URL_STATE_KEYS, KeyUrlState, - KqlQuery, ALL_URL_STATE_KEYS, UrlStateToRedux, + Timeline, } from './types'; function usePrevious(value: PreviousLocationUrlState) { @@ -59,7 +59,7 @@ export const useUrlStateHooks = ({ const prevProps = usePrevious({ pathName, urlState }); const replaceStateInLocation = ( - urlStateToReplace: UrlInputsModel | KqlQuery | string, + urlStateToReplace: UrlInputsModel | Query | Filter[] | Timeline | string, urlStateKey: string, latestLocation: Location = { hash: '', @@ -94,26 +94,41 @@ export const useUrlStateHooks = ({ urlKey ); if (newUrlStateString) { - const kqlQueryStateData: KqlQuery = decodeRisonUrlState(newUrlStateString); + const queryState: Query | Timeline | Filter[] = decodeRisonUrlState(newUrlStateString); if ( - urlKey === CONSTANTS.kqlQuery && - !isKqlForRoute(pageName, detailName, kqlQueryStateData.queryLocation) && - urlState[urlKey].queryLocation === kqlQueryStateData.queryLocation + urlKey === CONSTANTS.appQuery && + queryState != null && + (queryState as Query).query === '' + ) { + myLocation = replaceStateInLocation('', urlKey, myLocation); + } else if (urlKey === CONSTANTS.filters && isEmpty(queryState)) { + myLocation = replaceStateInLocation('', urlKey, myLocation); + } else if ( + urlKey === CONSTANTS.timeline && + queryState != null && + (queryState as Timeline).id === '' ) { - myLocation = replaceStateInLocation( - { - filterQuery: null, - queryLocation: null, - }, - urlKey, - myLocation - ); + myLocation = replaceStateInLocation('', urlKey, myLocation); } if (isInitializing) { urlStateToUpdate = [...urlStateToUpdate, { urlKey, newUrlStateString }]; } + } else if ( + urlKey === CONSTANTS.appQuery && + urlState[urlKey] != null && + (urlState[urlKey] as Query).query === '' + ) { + myLocation = replaceStateInLocation('', urlKey, myLocation); + } else if (urlKey === CONSTANTS.filters && isEmpty(urlState[urlKey])) { + myLocation = replaceStateInLocation('', urlKey, myLocation); + } else if ( + urlKey === CONSTANTS.timeline && + urlState[urlKey] != null && + (urlState[urlKey] as Timeline).id === '' + ) { + myLocation = replaceStateInLocation('', urlKey, myLocation); } else { - myLocation = replaceStateInLocation(urlState[urlKey], urlKey, myLocation); + myLocation = replaceStateInLocation(urlState[urlKey] || '', urlKey, myLocation); } }); difference(ALL_URL_STATE_KEYS, URL_STATE_KEYS[type]).forEach((urlKey: KeyUrlState) => { @@ -146,7 +161,23 @@ export const useUrlStateHooks = ({ } else if (!isEqual(urlState, prevProps.urlState) && !isInitializing) { let newLocation: Location = location; URL_STATE_KEYS[type].forEach((urlKey: KeyUrlState) => { - newLocation = replaceStateInLocation(urlState[urlKey], urlKey, newLocation); + if ( + urlKey === CONSTANTS.appQuery && + urlState[urlKey] != null && + (urlState[urlKey] as Query).query === '' + ) { + newLocation = replaceStateInLocation('', urlKey, newLocation); + } else if (urlKey === CONSTANTS.filters && isEmpty(urlState[urlKey])) { + newLocation = replaceStateInLocation('', urlKey, newLocation); + } else if ( + urlKey === CONSTANTS.timeline && + urlState[urlKey] != null && + (urlState[urlKey] as Timeline).id === '' + ) { + newLocation = replaceStateInLocation('', urlKey, newLocation); + } else { + newLocation = replaceStateInLocation(urlState[urlKey] || '', urlKey, newLocation); + } }); } else if (pathName !== prevProps.pathName) { handleInitialize(location, type); diff --git a/x-pack/legacy/plugins/siem/public/containers/hosts/filter.tsx b/x-pack/legacy/plugins/siem/public/containers/hosts/filter.tsx deleted file mode 100644 index 4bacb2f87c458..0000000000000 --- a/x-pack/legacy/plugins/siem/public/containers/hosts/filter.tsx +++ /dev/null @@ -1,150 +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 } from 'react'; -import memoizeOne from 'memoize-one'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { convertKueryToElasticSearchQuery } from '../../lib/keury'; -import { - hostsModel, - hostsSelectors, - KueryFilterQuery, - SerializedFilterQuery, - State, - inputsModel, -} from '../../store'; -import { hostsActions } from '../../store/actions'; -import { useUpdateKql } from '../../utils/kql/use_update_kql'; - -export interface HostsFilterArgs { - applyFilterQueryFromKueryExpression: (expression: string) => void; - filterQueryDraft: KueryFilterQuery; - isFilterQueryDraftValid: boolean; - setFilterQueryDraftFromKueryExpression: (expression: string) => void; -} - -interface OwnProps { - children: (args: HostsFilterArgs) => React.ReactNode; - indexPattern: StaticIndexPattern; - type: hostsModel.HostsType; - setQuery?: (params: { - id: string; - inspect: null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql; - }) => void; -} - -interface HostsFilterReduxProps { - hostsFilterQueryDraft: KueryFilterQuery; - isHostFilterQueryDraftValid: boolean; - kueryFilterQuery: KueryFilterQuery; -} - -interface HostsFilterDispatchProps { - applyHostsFilterQuery: ActionCreator<{ - filterQuery: SerializedFilterQuery; - hostsType: hostsModel.HostsType; - }>; - setHostsFilterQueryDraft: ActionCreator<{ - filterQueryDraft: KueryFilterQuery; - hostsType: hostsModel.HostsType; - }>; -} - -export type HostsFilterProps = OwnProps & HostsFilterReduxProps & HostsFilterDispatchProps; - -const HostsFilterComponent = React.memo( - ({ - applyHostsFilterQuery, - children, - hostsFilterQueryDraft, - indexPattern, - isHostFilterQueryDraftValid, - kueryFilterQuery, - setHostsFilterQueryDraft, - setQuery, - type, - }) => { - const applyFilterQueryFromKueryExpression = (expression: string) => - applyHostsFilterQuery({ - filterQuery: { - kuery: { - kind: 'kuery', - expression, - }, - serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern), - }, - hostsType: type, - }); - - const setFilterQueryDraftFromKueryExpression = (expression: string) => - setHostsFilterQueryDraft({ - filterQueryDraft: { - kind: 'kuery', - expression, - }, - hostsType: type, - }); - - const memoizedApplyFilter = memoizeOne(applyFilterQueryFromKueryExpression); - const memoizedSetFilter = memoizeOne(setFilterQueryDraftFromKueryExpression); - useEffect(() => { - if (setQuery) { - setQuery({ - id: 'kql', - inspect: null, - loading: false, - refetch: useUpdateKql({ - indexPattern, - kueryFilterQuery, - kueryFilterQueryDraft: hostsFilterQueryDraft, - storeType: 'hostsType', - type, - }), - }); - } - }, [hostsFilterQueryDraft, kueryFilterQuery, type]); - - return ( - <> - {children({ - applyFilterQueryFromKueryExpression: memoizedApplyFilter, - filterQueryDraft: hostsFilterQueryDraft, - isFilterQueryDraftValid: isHostFilterQueryDraftValid, - setFilterQueryDraftFromKueryExpression: memoizedSetFilter, - })} - - ); - } -); - -HostsFilterComponent.displayName = 'HostsFilterComponent'; - -const makeMapStateToProps = () => { - const getHostsFilterQueryDraft = hostsSelectors.hostsFilterQueryDraft(); - const getIsHostFilterQueryDraftValid = hostsSelectors.isHostFilterQueryDraftValid(); - const getHostsKueryFilterQuery = hostsSelectors.hostsFilterQueryAsKuery(); - const mapStateToProps = (state: State, { type }: OwnProps) => { - return { - hostsFilterQueryDraft: getHostsFilterQueryDraft(state, type), - isHostFilterQueryDraftValid: getIsHostFilterQueryDraftValid(state, type), - kueryFilterQuery: getHostsKueryFilterQuery(state, type), - }; - }; - return mapStateToProps; -}; - -export const HostsFilter = connect( - makeMapStateToProps, - { - applyHostsFilterQuery: hostsActions.applyHostsFilterQuery, - setHostsFilterQueryDraft: hostsActions.setHostsFilterQueryDraft, - } -)(HostsFilterComponent); diff --git a/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx b/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx index 09af9c288f534..d2be29e3e9e29 100644 --- a/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/hosts/index.tsx @@ -26,8 +26,6 @@ import { QueryTemplatePaginated, QueryTemplatePaginatedProps } from '../query_te import { HostsTableQuery } from './hosts_table.gql_query'; import { generateTablePaginationOptions } from '../../components/paginated_table/helpers'; -export { HostsFilter } from './filter'; - const ID = 'hostsQuery'; export interface HostsArgs { diff --git a/x-pack/legacy/plugins/siem/public/containers/network/filter.tsx b/x-pack/legacy/plugins/siem/public/containers/network/filter.tsx deleted file mode 100644 index 6ae2f3ef777e8..0000000000000 --- a/x-pack/legacy/plugins/siem/public/containers/network/filter.tsx +++ /dev/null @@ -1,149 +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 } from 'react'; -import memoizeOne from 'memoize-one'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { convertKueryToElasticSearchQuery } from '../../lib/keury'; -import { - KueryFilterQuery, - networkModel, - networkSelectors, - SerializedFilterQuery, - State, - inputsModel, -} from '../../store'; -import { networkActions } from '../../store/actions'; -import { useUpdateKql } from '../../utils/kql/use_update_kql'; - -export interface NetworkFilterArgs { - applyFilterQueryFromKueryExpression: (expression: string) => void; - filterQueryDraft: KueryFilterQuery; - isFilterQueryDraftValid: boolean; - setFilterQueryDraftFromKueryExpression: (expression: string) => void; -} - -interface OwnProps { - children: (args: NetworkFilterArgs) => React.ReactNode; - indexPattern: StaticIndexPattern; - setQuery?: (params: { - id: string; - inspect: null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql; - }) => void; - type: networkModel.NetworkType; -} - -interface NetworkFilterReduxProps { - isNetworkFilterQueryDraftValid: boolean; - kueryFilterQuery: KueryFilterQuery; - networkFilterQueryDraft: KueryFilterQuery; -} - -interface NetworkFilterDispatchProps { - applyNetworkFilterQuery: ActionCreator<{ - filterQuery: SerializedFilterQuery; - networkType: networkModel.NetworkType; - }>; - setNetworkFilterQueryDraft: ActionCreator<{ - filterQueryDraft: KueryFilterQuery; - networkType: networkModel.NetworkType; - }>; -} - -export type NetworkFilterProps = OwnProps & NetworkFilterReduxProps & NetworkFilterDispatchProps; - -const NetworkFilterComponent = React.memo( - ({ - applyNetworkFilterQuery, - children, - indexPattern, - isNetworkFilterQueryDraftValid, - kueryFilterQuery, - networkFilterQueryDraft, - setNetworkFilterQueryDraft, - setQuery, - type, - }) => { - const applyFilterQueryFromKueryExpression = (expression: string) => - applyNetworkFilterQuery({ - filterQuery: { - kuery: { - kind: 'kuery', - expression, - }, - serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern), - }, - networkType: type, - }); - - const setFilterQueryDraftFromKueryExpression = (expression: string) => - setNetworkFilterQueryDraft({ - filterQueryDraft: { - kind: 'kuery', - expression, - }, - networkType: type, - }); - const memoizedApplyFilter = memoizeOne(applyFilterQueryFromKueryExpression); - const memoizedSetFilter = memoizeOne(setFilterQueryDraftFromKueryExpression); - - useEffect(() => { - if (setQuery) { - setQuery({ - id: 'kql', - inspect: null, - loading: false, - refetch: useUpdateKql({ - indexPattern, - kueryFilterQuery, - kueryFilterQueryDraft: networkFilterQueryDraft, - storeType: 'networkType', - type, - }), - }); - } - }, [networkFilterQueryDraft, kueryFilterQuery, type]); - return ( - <> - {children({ - applyFilterQueryFromKueryExpression: memoizedApplyFilter, - filterQueryDraft: networkFilterQueryDraft, - isFilterQueryDraftValid: isNetworkFilterQueryDraftValid, - setFilterQueryDraftFromKueryExpression: memoizedSetFilter, - })} - - ); - } -); - -NetworkFilterComponent.displayName = 'NetworkFilterComponent'; - -const makeMapStateToProps = () => { - const getNetworkFilterQueryDraft = networkSelectors.networkFilterQueryDraft(); - const getIsNetworkFilterQueryDraftValid = networkSelectors.isNetworkFilterQueryDraftValid(); - const getNetworkKueryFilterQuery = networkSelectors.networkFilterQueryAsKuery(); - const mapStateToProps = (state: State, { type }: OwnProps) => { - return { - isNetworkFilterQueryDraftValid: getIsNetworkFilterQueryDraftValid(state, type), - kueryFilterQuery: getNetworkKueryFilterQuery(state, type), - networkFilterQueryDraft: getNetworkFilterQueryDraft(state, type), - }; - }; - return mapStateToProps; -}; - -export const NetworkFilter = connect( - makeMapStateToProps, - { - applyNetworkFilterQuery: networkActions.applyNetworkFilterQuery, - setNetworkFilterQueryDraft: networkActions.setNetworkFilterQueryDraft, - } -)(NetworkFilterComponent); diff --git a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts index 9cbe17ee6eb37..2ded8923c4afa 100644 --- a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts +++ b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts @@ -4,9 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { + buildEsQuery, + getEsQueryConfig, + Filter, + fromKueryExpression, + toElasticsearchQuery, +} from '@kbn/es-query'; import { isEmpty, isString, flow } from 'lodash/fp'; import { StaticIndexPattern } from 'ui/index_patterns'; +import { npSetup } from 'ui/new_platform'; +import { Query } from 'src/plugins/data/common'; import { KueryFilterQuery } from '../../store'; @@ -65,3 +73,26 @@ export const escapeKuery = flow( escapeNot, escapeWhitespace ); + +export const convertToBuildEsQuery = ({ + indexPattern, + queries, + filters, +}: { + indexPattern: StaticIndexPattern; + queries: Query[]; + filters: Filter[]; +}) => { + try { + return JSON.stringify( + buildEsQuery( + indexPattern, + queries, + filters.filter(f => f.meta.disabled === false), + getEsQueryConfig(npSetup.core.uiSettings) + ) + ); + } catch (exp) { + return ''; + } +}; diff --git a/x-pack/legacy/plugins/siem/public/mock/global_state.ts b/x-pack/legacy/plugins/siem/public/mock/global_state.ts index 02f098cb7b9a9..78315e0d3ddfb 100644 --- a/x-pack/legacy/plugins/siem/public/mock/global_state.ts +++ b/x-pack/legacy/plugins/siem/public/mock/global_state.ts @@ -46,8 +46,6 @@ export const mockGlobalState: State = { uncommonProcesses: { activePage: 0, limit: 10 }, anomalies: null, }, - filterQuery: null, - filterQueryDraft: null, }, details: { queries: { @@ -62,8 +60,6 @@ export const mockGlobalState: State = { uncommonProcesses: { activePage: 0, limit: 10 }, anomalies: null, }, - filterQuery: null, - filterQueryDraft: null, }, }, network: { @@ -86,12 +82,8 @@ export const mockGlobalState: State = { isPtrIncluded: false, }, }, - filterQuery: null, - filterQueryDraft: null, }, details: { - filterQuery: null, - filterQueryDraft: null, flowTarget: FlowTarget.source, queries: { [networkModel.IpDetailsTableType.topNFlowSource]: { @@ -121,14 +113,24 @@ export const mockGlobalState: State = { global: { timerange: { kind: 'relative', fromStr: DEFAULT_FROM, toStr: DEFAULT_TO, from: 0, to: 1 }, linkTo: ['timeline'], - query: [], + queries: [], policy: { kind: DEFAULT_INTERVAL_TYPE, duration: DEFAULT_INTERVAL_VALUE }, + query: { + query: '', + language: 'kuery', + }, + filters: [], }, timeline: { timerange: { kind: 'relative', fromStr: DEFAULT_FROM, toStr: DEFAULT_TO, from: 0, to: 1 }, linkTo: ['global'], - query: [], + queries: [], policy: { kind: DEFAULT_INTERVAL_TYPE, duration: DEFAULT_INTERVAL_VALUE }, + query: { + query: '', + language: 'kuery', + }, + filters: [], }, }, dragAndDrop: { dataProviders: {} }, diff --git a/x-pack/legacy/plugins/siem/public/mock/index_pattern.ts b/x-pack/legacy/plugins/siem/public/mock/index_pattern.ts index 3ec2e083b9393..826057560f942 100644 --- a/x-pack/legacy/plugins/siem/public/mock/index_pattern.ts +++ b/x-pack/legacy/plugins/siem/public/mock/index_pattern.ts @@ -84,6 +84,12 @@ export const mockIndexPattern = { type: 'string', aggregatable: true, }, + { + name: 'host.name', + searchable: true, + type: 'string', + aggregatable: true, + }, ], title: 'filebeat-*,auditbeat-*,packetbeat-*', }; diff --git a/x-pack/legacy/plugins/siem/public/mock/ui_settings.ts b/x-pack/legacy/plugins/siem/public/mock/ui_settings.ts index ac9b7ee6d039f..c7803e99f3857 100644 --- a/x-pack/legacy/plugins/siem/public/mock/ui_settings.ts +++ b/x-pack/legacy/plugins/siem/public/mock/ui_settings.ts @@ -46,3 +46,24 @@ chrome.getUiSettingsClient().get.mockImplementation((key: string) => { throw new Error(`Unexpected config key: ${key}`); } }); + +export interface MockNpSetUp { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + core: { uiSettings: any }; +} + +type Config = + | 'query:allowLeadingWildcards' + | 'query:queryString:options' + | 'courier:ignoreFilterIfFieldNotInIndex' + | 'dateFormat:tz'; + +export const mockUiSettings = { + get: (item: Config) => { + return mockUiSettings[item]; + }, + 'query:allowLeadingWildcards': true, + 'query:queryString:options': {}, + 'courier:ignoreFilterIfFieldNotInIndex': true, + 'dateFormat:tz': 'Browser', +}; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx index afae384ef9eea..5b4fadbbbc9a5 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.test.tsx @@ -5,13 +5,32 @@ */ import { shallow, mount } from 'enzyme'; -import { HostDetailsBody } from './body'; +import toJson from 'enzyme-to-json'; import React from 'react'; +import { StaticIndexPattern } from 'ui/index_patterns'; +import { npSetup } from 'ui/new_platform'; -import '../../../mock/ui_settings'; -import { CommonChildren } from '../navigation/types'; -import toJson from 'enzyme-to-json'; +import { mockIndexPattern } from '../../../mock/index_pattern'; import { TestProviders } from '../../../mock/test_providers'; +import { mockUiSettings, MockNpSetUp } from '../../../mock/ui_settings'; +import { CommonChildren } from '../navigation/types'; +import { HostDetailsBody } from './body'; + +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; + +jest.mock('../../../containers/source', () => ({ + indicesExistOrDataTemporarilyUnavailable: () => true, + WithSource: ({ + children, + }: { + children: (args: { + indicesExist: boolean; + indexPattern: StaticIndexPattern; + }) => React.ReactNode; + }) => children({ indicesExist: true, indexPattern: mockIndexPattern }), +})); describe('body', () => { test('render snapshot', () => { @@ -45,20 +64,34 @@ describe('body', () => { /> ); - // match against everything but the functions to ensure they are there as expected expect(child.mock.calls[0][0]).toMatchObject({ endDate: 0, - filterQuery: { term: { 'host.name': 'host-1' } }, - hostName: 'host-1', - indexPattern: { - fields: [], - title: 'auditbeat-*,endgame-*,filebeat-*,packetbeat-*,winlogbeat-*', - }, - kqlQueryExpression: 'host.name: "host-1"', + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}},{"match_phrase":{"host.name":{"query":"host-1"}}}],"should":[],"must_not":[]}}', skip: false, startDate: 0, type: 'details', + indexPattern: { + fields: [ + { name: '@timestamp', searchable: true, type: 'date', aggregatable: true }, + { name: '@version', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.ephemeral_id', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.hostname', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.id', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test1', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test2', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test3', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test4', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test5', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test6', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test7', searchable: true, type: 'string', aggregatable: true }, + { name: 'agent.test8', searchable: true, type: 'string', aggregatable: true }, + { name: 'host.name', searchable: true, type: 'string', aggregatable: true }, + ], + title: 'filebeat-*,auditbeat-*,packetbeat-*', + }, + hostName: 'host-1', }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx index a115224dd24db..d0f8d8037d028 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/body.tsx @@ -11,36 +11,61 @@ import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../c import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; import { Anomaly } from '../../../components/ml/types'; -import { getHostDetailsEventsKqlQueryExpression } from './helpers'; +import { convertToBuildEsQuery } from '../../../lib/keury'; import { HostDetailsBodyComponentProps } from './types'; -import { getFilterQuery, type, makeMapStateToProps } from './utils'; +import { type, makeMapStateToProps } from './utils'; const HostDetailsBodyComponent = React.memo( ({ children, deleteQuery, - filterQueryExpression, + detailName, + filters, from, isInitializing, - detailName, + query, setAbsoluteRangeDatePicker, setQuery, to, }) => { return ( - {({ indicesExist, indexPattern }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + {({ indicesExist, indexPattern }) => { + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters: [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'host.name', + value: detailName, + params: { + query: detailName, + }, + }, + query: { + match: { + 'host.name': { + query: detailName, + type: 'phrase', + }, + }, + }, + }, + ...filters, + ], + }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( <> {children({ deleteQuery, endDate: to, - filterQuery: getFilterQuery(detailName, filterQueryExpression, indexPattern), - kqlQueryExpression: getHostDetailsEventsKqlQueryExpression({ - filterQueryExpression, - hostName: detailName, - }), + filterQuery, skip: isInitializing, setQuery, startDate: from, @@ -60,8 +85,8 @@ const HostDetailsBodyComponent = React.memo( }, })} - ) : null - } + ) : null; + }} ); } diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index 192b692253316..fec7109ef71d2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -12,43 +12,43 @@ import { StickyContainer } from 'react-sticky'; import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; -import { LastEventTime } from '../../../components/last_event_time'; - -import { HostOverviewByNameQuery } from '../../../containers/hosts/overview'; -import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; -import { LastEventIndexKey } from '../../../graphql/types'; - -import { HostsEmptyPage } from '../hosts_empty_page'; -import { HostsKql } from '../kql'; -import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; -import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details'; +import { LastEventTime } from '../../../components/last_event_time'; import { hostToCriteria } from '../../../components/ml/criteria/host_to_criteria'; -import { SiemNavigation } from '../../../components/navigation'; -import { HostsQueryProps } from '../hosts'; -import { SpyRoute } from '../../../utils/route/spy_routes'; +import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider'; +import { hasMlUserPermissions } from '../../../components/ml/permissions/has_ml_user_permissions'; import { AnomalyTableProvider } from '../../../components/ml/anomaly/anomaly_table_provider'; +import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; +import { SiemNavigation } from '../../../components/navigation'; import { manageQuery } from '../../../components/page/manage_query'; import { HostOverview } from '../../../components/page/hosts/host_overview'; import { KpiHostsComponent } from '../../../components/page/hosts'; +import { SiemSearchBar } from '../../../components/search_bar'; +import { HostOverviewByNameQuery } from '../../../containers/hosts/overview'; +import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; +import { LastEventIndexKey } from '../../../graphql/types'; +import { convertToBuildEsQuery } from '../../../lib/keury'; +import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; +import { SpyRoute } from '../../../utils/route/spy_routes'; -import { HostDetailsComponentProps } from './types'; -import { getFilterQuery, type, makeMapStateToProps } from './utils'; -import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider'; -import { hasMlUserPermissions } from '../../../components/ml/permissions/has_ml_user_permissions'; +import { HostsQueryProps } from '../hosts'; +import { HostsEmptyPage } from '../hosts_empty_page'; export { HostDetailsBody } from './body'; import { navTabsHostDetails } from './nav_tabs'; +import { HostDetailsComponentProps } from './types'; +import { makeMapStateToProps } from './utils'; const HostOverviewManage = manageQuery(HostOverview); const KpiHostDetailsManage = manageQuery(KpiHostsComponent); const HostDetailsComponent = React.memo( ({ + detailName, isInitializing, - filterQueryExpression, + filters, from, - detailName, + query, setQuery, setAbsoluteRangeDatePicker, to, @@ -57,11 +57,39 @@ const HostDetailsComponent = React.memo( return ( <> - {({ indicesExist, indexPattern }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + {({ indicesExist, indexPattern }) => { + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters: [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'host.name', + value: detailName, + params: { + query: detailName, + }, + }, + query: { + match: { + 'host.name': { + query: detailName, + type: 'phrase', + }, + }, + }, + }, + ...filters, + ], + }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - + ( ( - ) - } + ); + }} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts index 886338b8435eb..0f25c2815609c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/types.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { ActionCreator } from 'typescript-fsa'; +import { Query } from 'src/plugins/data/common'; import { InputsModelId } from '../../../store/inputs/constants'; import { HostComponentProps } from '../../../components/link_to/redirect_to_hosts'; @@ -18,7 +20,8 @@ import { } from '../navigation/types'; interface HostDetailsComponentReduxProps { - filterQueryExpression: string; + query: Query; + filters: Filter[]; } interface HostDetailsComponentDispatchProps { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts index f1c393dec04c7..52efe93c0c8dc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts @@ -4,27 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { isEmpty } from 'lodash/fp'; import { Breadcrumb } from 'ui/chrome'; -import { StaticIndexPattern } from 'ui/index_patterns'; -import { ESTermQuery } from '../../../../common/typed_json'; - -import { hostsModel, hostsSelectors } from '../../../store/hosts'; +import { hostsModel, inputsSelectors, State } from '../../../store'; import { HostsTableType } from '../../../store/hosts/model'; -import { State } from '../../../store'; import { getHostsUrl, getHostDetailsUrl } from '../../../components/link_to/redirect_to_hosts'; import * as i18n from '../translations'; -import { convertKueryToElasticSearchQuery, escapeQueryValue } from '../../../lib/keury'; import { RouteSpyState } from '../../../utils/route/types'; export const type = hostsModel.HostsType.details; export const makeMapStateToProps = () => { - const getHostsFilterQuery = hostsSelectors.hostsFilterQueryExpression(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); return (state: State) => ({ - filterQueryExpression: getHostsFilterQuery(state, type) || '', + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), }); }; @@ -63,19 +59,3 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): Breadcr } return breadcrumb; }; - -export const getFilterQuery = ( - hostName: string | null, - filterQueryExpression: string, - indexPattern: StaticIndexPattern -): ESTermQuery | string => - isEmpty(filterQueryExpression) - ? hostName - ? { term: { 'host.name': hostName } } - : '' - : convertKueryToElasticSearchQuery( - `${filterQueryExpression} ${ - hostName ? `and host.name: ${escapeQueryValue(hostName)}` : '' - }`, - indexPattern - ); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx index 98698d50da096..a04638a50941f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx @@ -5,22 +5,22 @@ */ import { mount } from 'enzyme'; +import { cloneDeep } from 'lodash/fp'; import * as React from 'react'; import { Router } from 'react-router-dom'; +import { MockedProvider } from 'react-apollo/test-utils'; import { ActionCreator } from 'typescript-fsa'; +import { npSetup } from 'ui/new_platform'; import '../../mock/match_media'; -import '../../mock/ui_settings'; -import { Hosts, HostsComponentProps } from './hosts'; -import { mocksSource } from '../../containers/source/mock'; -import { TestProviders } from '../../mock'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { cloneDeep } from 'lodash/fp'; import { SiemNavigation } from '../../components/navigation'; +import { mocksSource } from '../../containers/source/mock'; import { wait } from '../../lib/helpers'; - +import { TestProviders } from '../../mock'; +import { MockNpSetUp, mockUiSettings } from '../../mock/ui_settings'; import { InputsModelId } from '../../store/inputs/constants'; +import { Hosts, HostsComponentProps } from './hosts'; jest.mock('../../lib/settings/use_kibana_ui_setting'); @@ -30,6 +30,16 @@ jest.mock('ui/documentation_links', () => ({ }, })); +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; + +// Test will fail because we will to need to mock some core services to make the test work +// For now let's forget about SiemSearchBar +jest.mock('../../components/search_bar', () => ({ + SiemSearchBar: () => null, +})); + let localSource: Array<{ request: {}; result: { @@ -83,7 +93,8 @@ describe('Hosts - rendering', () => { id: InputsModelId; to: number; }>, - filterQuery: '', + query: { query: '', language: 'kuery' }, + filters: [], }; beforeAll(() => { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index bd0252877524e..9197cb0e9a581 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -5,39 +5,42 @@ */ import { EuiSpacer } from '@elastic/eui'; +import { Filter } from '@kbn/es-query'; import * as React from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; - import { ActionCreator } from 'typescript-fsa'; +import { Query } from 'src/plugins/data/common'; + +import { FiltersGlobal } from '../../components/filters_global'; +import { GlobalTimeArgs } from '../../containers/global_time'; import { HeaderPage } from '../../components/header_page'; +import { KpiHostsQuery } from '../../containers/kpi_hosts'; import { LastEventTime } from '../../components/last_event_time'; +import { SiemNavigation } from '../../components/navigation'; import { KpiHostsComponent } from '../../components/page/hosts'; import { manageQuery } from '../../components/page/manage_query'; -import { GlobalTimeArgs } from '../../containers/global_time'; -import { KpiHostsQuery } from '../../containers/kpi_hosts'; +import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions'; +import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; +import { SiemSearchBar } from '../../components/search_bar'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; -import { hostsModel, hostsSelectors, State } from '../../store'; - -import { HostsEmptyPage } from './hosts_empty_page'; -import { HostsKql } from './kql'; +import { convertToBuildEsQuery } from '../../lib/keury'; +import { inputsSelectors, State } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { InputsModelId } from '../../store/inputs/constants'; -import { SiemNavigation } from '../../components/navigation'; import { SpyRoute } from '../../utils/route/spy_routes'; -import { FiltersGlobal } from '../../components/filters_global'; -import * as i18n from './translations'; +import { HostsEmptyPage } from './hosts_empty_page'; import { navTabsHosts } from './nav_tabs'; -import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions'; -import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; +import * as i18n from './translations'; const KpiHostsComponentManage = manageQuery(KpiHostsComponent); interface HostsComponentReduxProps { - filterQuery: string; + query: Query; + filters: Filter[]; } interface HostsComponentDispatchProps { @@ -55,20 +58,21 @@ export type HostsComponentProps = HostsComponentReduxProps & HostsQueryProps; const HostsComponent = React.memo( - ({ isInitializing, filterQuery, from, setAbsoluteRangeDatePicker, setQuery, to }) => { + ({ isInitializing, filters, from, query, setAbsoluteRangeDatePicker, setQuery, to }) => { const capabilities = React.useContext(MlCapabilitiesContext); return ( <> - {({ indicesExist, indexPattern }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + {({ indicesExist, indexPattern }) => { + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters, + }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - + ( - ) - } + ); + }} @@ -125,10 +129,13 @@ const HostsComponent = React.memo( HostsComponent.displayName = 'HostsComponent'; const makeMapStateToProps = () => { - const getHostsFilterQueryAsJson = hostsSelectors.hostsFilterQueryAsJson(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const mapStateToProps = (state: State): HostsComponentReduxProps => ({ - filterQuery: getHostsFilterQueryAsJson(state, hostsModel.HostsType.page) || '', + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), }); + return mapStateToProps; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx index 0e381f96cd747..d8cd2b0c30fee 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_body.tsx @@ -7,43 +7,46 @@ import React, { memo } from 'react'; import { connect } from 'react-redux'; +import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; +import { Anomaly } from '../../components/ml/types'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; - -import { hostsModel, hostsSelectors, State } from '../../store'; +import { convertToBuildEsQuery } from '../../lib/keury'; +import { hostsModel, inputsSelectors, State } from '../../store'; +import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { HostsComponentProps } from './hosts'; import { CommonChildren, AnomaliesChildren } from './navigation/types'; -import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; -import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; -import { Anomaly } from '../../components/ml/types'; interface HostsBodyComponentProps extends HostsComponentProps { - kqlQueryExpression: string; children: CommonChildren | AnomaliesChildren; } const HostsBodyComponent = memo( ({ - deleteQuery, - filterQuery, - kqlQueryExpression, - setAbsoluteRangeDatePicker, children, - to, + deleteQuery, + filters, from, - setQuery, isInitializing, + query, + setAbsoluteRangeDatePicker, + setQuery, + to, }) => { return ( - {({ indicesExist, indexPattern }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + {({ indicesExist, indexPattern }) => { + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters, + }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( <> {children({ deleteQuery, endDate: to, filterQuery, - kqlQueryExpression, skip: isInitializing, setQuery, startDate: from, @@ -62,8 +65,8 @@ const HostsBodyComponent = memo( }, })} - ) : null - } + ) : null; + }} ); } @@ -72,11 +75,11 @@ const HostsBodyComponent = memo( HostsBodyComponent.displayName = 'HostsBodyComponent'; const makeMapStateToProps = () => { - const getHostsFilterQueryAsJson = hostsSelectors.hostsFilterQueryAsJson(); - const hostsFilterQueryExpression = hostsSelectors.hostsFilterQueryExpression(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const mapStateToProps = (state: State) => ({ - filterQuery: getHostsFilterQueryAsJson(state, hostsModel.HostsType.page) || '', - kqlQueryExpression: hostsFilterQueryExpression(state, hostsModel.HostsType.page) || '', + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), }); return mapStateToProps; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/kql.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/kql.tsx deleted file mode 100644 index 23e9bc4ab3425..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/kql.tsx +++ /dev/null @@ -1,56 +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 from 'react'; -import { pure } from 'recompose'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { AutocompleteField } from '../../components/autocomplete_field'; -import { HostsFilter } from '../../containers/hosts'; -import { KueryAutocompletion } from '../../containers/kuery_autocompletion'; -import { hostsModel, inputsModel } from '../../store'; - -import * as i18n from './translations'; - -interface HostsKqlProps { - indexPattern: StaticIndexPattern; - type: hostsModel.HostsType; - setQuery: (params: { - id: string; - inspect: null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql; - }) => void; -} - -export const HostsKql = pure(({ indexPattern, setQuery, type }) => ( - - {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( - - {({ - applyFilterQueryFromKueryExpression, - filterQueryDraft, - isFilterQueryDraftValid, - setFilterQueryDraftFromKueryExpression, - }) => ( - - )} - - )} - -)); - -HostsKql.displayName = 'HostsKql'; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx index c7b7f1c0eb643..a4f43f752cac9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx @@ -18,7 +18,6 @@ const EventsOverTimeManage = manageQuery(EventsOverTimeHistogram); export const EventsQueryTabBody = ({ endDate, - kqlQueryExpression, startDate, setQuery, filterQuery, @@ -49,12 +48,7 @@ export const EventsQueryTabBody = ({ )} - + ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/types.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/types.ts index 4554fa1351f5f..d567038a05bd8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/types.ts @@ -30,7 +30,6 @@ interface QueryTabBodyProps { startDate: number; endDate: number; filterQuery?: string | ESTermQuery; - kqlQueryExpression: string; } export type AnomaliesQueryTabBodyProps = QueryTabBodyProps & { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap b/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap index f4ee5ab03897b..baba4fc36ef8f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/pages/network/__snapshots__/ip_details.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Ip Details it matches the snapshot 1`] = ` ({ + SiemSearchBar: () => null, +})); + let localSource: Array<{ request: {}; result: { @@ -68,7 +81,8 @@ const getMockProps = (ip: string) => ({ from, isInitializing: false, setQuery: jest.fn(), - filterQuery: 'coolQueryhuh?', + query: { query: 'coolQueryhuh?', language: 'keury' }, + filters: [], flowTarget: FlowTarget.source, history: getMockHistory(ip), location: { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx index 80fcce20a4982..6fd27e0ab178c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details.tsx @@ -17,31 +17,32 @@ import { HeaderPage } from '../../components/header_page'; import { LastEventTime } from '../../components/last_event_time'; import { getNetworkUrl } from '../../components/link_to/redirect_to_network'; import { manageQuery } from '../../components/page/manage_query'; +import { AnomalyTableProvider } from '../../components/ml/anomaly/anomaly_table_provider'; +import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; +import { AnomaliesNetworkTable } from '../../components/ml/tables/anomalies_network_table'; +import { networkToCriteria } from '../../components/ml/criteria/network_to_criteria'; import { FlowTargetSelectConnected } from '../../components/page/network/flow_target_select_connected'; import { IpOverview } from '../../components/page/network/ip_overview'; import { UsersTable } from '../../components/page/network/users_table'; import { TlsTable } from '../../components/page/network/tls_table'; +import { SiemSearchBar } from '../../components/search_bar'; import { IpOverviewQuery } from '../../containers/ip_overview'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { TlsQuery } from '../../containers/tls'; import { UsersQuery } from '../../containers/users'; import { FlowTargetSourceDest, LastEventIndexKey } from '../../graphql/types'; import { decodeIpv6 } from '../../lib/helpers'; -import { networkModel, networkSelectors, State } from '../../store'; +import { convertToBuildEsQuery } from '../../lib/keury'; +import { ConditionalFlexGroup } from '../../pages/network/navigation/conditional_flex_group'; +import { networkModel, networkSelectors, State, inputsSelectors } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../store/inputs/actions'; +import { SpyRoute } from '../../utils/route/spy_routes'; -import { NetworkKql } from './kql'; import { NetworkEmptyPage } from './network_empty_page'; import { NetworkTopNFlowQuery } from '../../containers/network_top_n_flow'; import { NetworkTopNFlowTable } from '../../components/page/network/network_top_n_flow_table'; import * as i18n from './translations'; import { IPDetailsComponentProps } from './types'; -import { AnomalyTableProvider } from '../../components/ml/anomaly/anomaly_table_provider'; -import { scoreIntervalToDateTime } from '../../components/ml/score/score_interval_to_datetime'; -import { AnomaliesNetworkTable } from '../../components/ml/tables/anomalies_network_table'; -import { networkToCriteria } from '../../components/ml/criteria/network_to_criteria'; -import { SpyRoute } from '../../utils/route/spy_routes'; -import { ConditionalFlexGroup } from '../../pages/network/navigation/conditional_flex_group'; const TlsTableManage = manageQuery(TlsTable); const UsersTableManage = manageQuery(UsersTable); @@ -51,11 +52,12 @@ const NetworkTopNFlowTableManage = manageQuery(NetworkTopNFlowTable); export const IPDetailsComponent = pure( ({ detailName, - filterQuery, + filters, flowTarget, setAbsoluteRangeDatePicker, to, from, + query, setQuery, isInitializing, }) => ( @@ -63,258 +65,257 @@ export const IPDetailsComponent = pure( {({ indicesExist, indexPattern }) => { const ip = decodeIpv6(detailName); + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters, + }); return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - - - + + + - } - title={ip} - draggableArguments={{ field: `${flowTarget}.ip`, value: ip }} - > - - + } + title={ip} + draggableArguments={{ field: `${flowTarget}.ip`, value: ip }} + > + + - - {({ id, inspect, ipOverviewData, loading, refetch }) => ( - - {({ isLoadingAnomaliesData, anomaliesData }) => ( - { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} - /> - )} - - )} - + + {({ id, inspect, ipOverviewData, loading, refetch }) => ( + + {({ isLoadingAnomaliesData, anomaliesData }) => ( + { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }} + /> + )} + + )} + - + - - - - {({ - id, - inspect, - isInspected, - loading, - loadPage, - networkTopNFlow, - pageInfo, - refetch, - totalCount, - }) => ( - - )} - - + + + + {({ + id, + inspect, + isInspected, + loading, + loadPage, + networkTopNFlow, + pageInfo, + refetch, + totalCount, + }) => ( + + )} + + - - - {({ - id, - inspect, - isInspected, - loading, - loadPage, - networkTopNFlow, - pageInfo, - refetch, - totalCount, - }) => ( - - )} - - - + + + {({ + id, + inspect, + isInspected, + loading, + loadPage, + networkTopNFlow, + pageInfo, + refetch, + totalCount, + }) => ( + + )} + + + - + - - {({ - id, - inspect, - isInspected, - users, - totalCount, - pageInfo, - loading, - loadPage, - refetch, - }) => ( - - )} - + + {({ + id, + inspect, + isInspected, + users, + totalCount, + pageInfo, + loading, + loadPage, + refetch, + }) => ( + + )} + - + - - {({ - id, - inspect, - isInspected, - tls, - totalCount, - pageInfo, - loading, - loadPage, - refetch, - }) => ( - - )} - + + {({ + id, + inspect, + isInspected, + tls, + totalCount, + pageInfo, + loading, + loadPage, + refetch, + }) => ( + + )} + - + - { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} - /> - + { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }} + /> ) : ( <> @@ -333,10 +334,12 @@ export const IPDetailsComponent = pure( IPDetailsComponent.displayName = 'IPDetailsComponent'; const makeMapStateToProps = () => { - const getNetworkFilterQuery = networkSelectors.networkFilterQueryAsJson(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getIpDetailsFlowTargetSelector = networkSelectors.ipDetailsFlowTargetSelector(); return (state: State) => ({ - filterQuery: getNetworkFilterQuery(state, networkModel.NetworkType.details) || '', + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), flowTarget: getIpDetailsFlowTargetSelector(state), }); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/kql.tsx b/x-pack/legacy/plugins/siem/public/pages/network/kql.tsx deleted file mode 100644 index e1879734f186c..0000000000000 --- a/x-pack/legacy/plugins/siem/public/pages/network/kql.tsx +++ /dev/null @@ -1,56 +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 from 'react'; -import { pure } from 'recompose'; -import { StaticIndexPattern } from 'ui/index_patterns'; - -import { AutocompleteField } from '../../components/autocomplete_field'; -import { KueryAutocompletion } from '../../containers/kuery_autocompletion'; -import { NetworkFilter } from '../../containers/network'; -import { networkModel, inputsModel } from '../../store'; - -import * as i18n from './translations'; - -interface NetworkKqlProps { - indexPattern: StaticIndexPattern; - type: networkModel.NetworkType; - setQuery: (params: { - id: string; - inspect: null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql; - }) => void; -} - -export const NetworkKql = pure(({ indexPattern, setQuery, type }) => ( - - {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( - - {({ - applyFilterQueryFromKueryExpression, - filterQueryDraft, - isFilterQueryDraftValid, - setFilterQueryDraftFromKueryExpression, - }) => ( - - )} - - )} - -)); - -NetworkKql.displayName = 'NetworkKql'; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx index 6ca7a7dfa476d..9ab1bacf56259 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.test.tsx @@ -5,17 +5,18 @@ */ import { mount } from 'enzyme'; +import { cloneDeep } from 'lodash/fp'; import * as React from 'react'; import { Router } from 'react-router-dom'; +import { MockedProvider } from 'react-apollo/test-utils'; +import { npSetup } from 'ui/new_platform'; import '../../mock/match_media'; -import '../../mock/ui_settings'; -import { Network } from './network'; import { mocksSource } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; -import { MockedProvider } from 'react-apollo/test-utils'; -import { cloneDeep } from 'lodash/fp'; +import { mockUiSettings, MockNpSetUp } from '../../mock/ui_settings'; +import { Network } from './network'; jest.mock('ui/new_platform'); jest.mock('../../lib/settings/use_kibana_ui_setting'); @@ -26,6 +27,16 @@ jest.mock('ui/documentation_links', () => ({ }, })); +const mockNpSetup: MockNpSetUp = (npSetup as unknown) as MockNpSetUp; +jest.mock('ui/new_platform'); +mockNpSetup.core.uiSettings = mockUiSettings; + +// Test will fail because we will to need to mock some core services to make the test work +// For now let's forget about SiemSearchBar +jest.mock('../../components/search_bar', () => ({ + SiemSearchBar: () => null, +})); + let localSource: Array<{ request: {}; result: { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index d0035fc2948f1..f5e1892b6ec6e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -15,18 +15,18 @@ import { HeaderPage } from '../../components/header_page'; import { LastEventTime } from '../../components/last_event_time'; import { manageQuery } from '../../components/page/manage_query'; import { KpiNetworkComponent } from '../../components/page/network'; -import { KpiNetworkQuery } from '../../containers/kpi_network'; -import { NetworkFilter } from '../../containers/network'; import { SiemNavigation } from '../../components/navigation'; +import { SiemSearchBar } from '../../components/search_bar'; +import { KpiNetworkQuery } from '../../containers/kpi_network'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; -import { networkModel, networkSelectors, State } from '../../store'; +import { networkModel, State, inputsSelectors } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { SpyRoute } from '../../utils/route/spy_routes'; -import { NetworkKql } from './kql'; import { NetworkEmptyPage } from './network_empty_page'; import * as i18n from './translations'; +import { convertToBuildEsQuery } from '../../lib/keury'; import { navTabsNetwork, NetworkRoutes, NetworkRoutesLoading } from './navigation'; @@ -37,8 +37,8 @@ const sourceId = 'default'; const NetworkComponent = React.memo( ({ - filterQuery, - queryExpression, + filters, + query, setAbsoluteRangeDatePicker, networkPagePath, to, @@ -50,99 +50,92 @@ const NetworkComponent = React.memo( }) => ( <> - {({ indicesExist, indexPattern }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - - <> - - - - - } - title={i18n.PAGE_TITLE} + {({ indicesExist, indexPattern }) => { + const filterQuery = convertToBuildEsQuery({ + indexPattern, + queries: [query], + filters, + }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + + + + + + } + title={i18n.PAGE_TITLE} + /> + + + + + {({ kpiNetwork, loading, id, inspect, refetch }) => ( + { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }} /> + )} + - - {({ applyFilterQueryFromKueryExpression }) => ( - - )} - - - - {({ kpiNetwork, loading, id, inspect, refetch }) => ( - { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} - /> - )} - - - {capabilitiesFetched && !isInitializing ? ( - <> - - - - - - - - - ) : ( - - )} + {capabilitiesFetched && !isInitializing ? ( + <> + + + + + - - + ) : ( + + )} + + + ) : ( <> - ) - } + ); + }} @@ -152,11 +145,11 @@ const NetworkComponent = React.memo( NetworkComponent.displayName = 'NetworkComponent'; const makeMapStateToProps = () => { - const getNetworkFilterQueryAsJson = networkSelectors.networkFilterQueryAsJson(); - const getNetworkFilterExpression = networkSelectors.networkFilterExpression(); + const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); + const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const mapStateToProps = (state: State) => ({ - filterQuery: getNetworkFilterQueryAsJson(state, networkModel.NetworkType.page) || '', - queryExpression: getNetworkFilterExpression(state, networkModel.NetworkType.page) || '', + query: getGlobalQuerySelector(state), + filters: getGlobalFiltersQuerySelector(state), }); return mapStateToProps; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/types.ts index 8e6a4dc617534..4dedb27d93fcb 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/types.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { RouteComponentProps } from 'react-router-dom'; import { ActionCreator } from 'typescript-fsa'; +import { Query } from 'src/plugins/data/common'; import { FlowTarget } from '../../graphql/types'; import { GlobalTimeArgs } from '../../containers/global_time'; @@ -18,8 +20,8 @@ export type SetAbsoluteRangeDatePicker = ActionCreator<{ }>; interface NetworkComponentReduxProps { - filterQuery: string; - queryExpression: string; + filters: Filter[]; + query: Query; setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; } @@ -32,8 +34,9 @@ export type NetworkComponentProps = NetworkComponentReduxProps & }; interface IPDetailsComponentReduxProps { - filterQuery: string; + filters: Filter[]; flowTarget: FlowTarget; + query: Query; setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; } diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/actions.ts b/x-pack/legacy/plugins/siem/public/store/hosts/actions.ts index efe5835459da2..4dcc5b0db9b0b 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/actions.ts @@ -7,7 +7,6 @@ import actionCreatorFactory from 'typescript-fsa'; import { HostsSortField } from '../../graphql/types'; -import { KueryFilterQuery, SerializedFilterQuery } from '../model'; import { HostsTableType, HostsType } from './model'; const actionCreator = actionCreatorFactory('x-pack/siem/local/hosts'); @@ -30,13 +29,3 @@ export const updateHostsSort = actionCreator<{ sort: HostsSortField; hostsType: HostsType; }>('UPDATE_HOSTS_SORT'); - -export const setHostsFilterQueryDraft = actionCreator<{ - filterQueryDraft: KueryFilterQuery; - hostsType: HostsType; -}>('SET_HOSTS_FILTER_QUERY_DRAFT'); - -export const applyHostsFilterQuery = actionCreator<{ - filterQuery: SerializedFilterQuery; - hostsType: HostsType; -}>('APPLY_HOSTS_FILTER_QUERY'); diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/helpers.test.ts b/x-pack/legacy/plugins/siem/public/store/hosts/helpers.test.ts index 4e69fe6593043..8721121295aad 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/helpers.test.ts @@ -32,8 +32,6 @@ export const mockHostsState: HostsModel = { }, [HostsTableType.anomalies]: null, }, - filterQuery: null, - filterQueryDraft: null, }, details: { queries: { @@ -57,8 +55,6 @@ export const mockHostsState: HostsModel = { }, [HostsTableType.anomalies]: null, }, - filterQuery: null, - filterQueryDraft: null, }, }; diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/model.ts b/x-pack/legacy/plugins/siem/public/store/hosts/model.ts index f8a78fc56b1ed..8b21537292207 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/model.ts @@ -5,7 +5,6 @@ */ import { Direction, HostsFields } from '../../graphql/types'; -import { KueryFilterQuery, SerializedFilterQuery } from '../model'; export enum HostsType { page = 'page', @@ -39,8 +38,6 @@ export interface Queries { } export interface GenericHostsModel { - filterQuery: SerializedFilterQuery | null; - filterQueryDraft: KueryFilterQuery | null; queries: Queries; } diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts b/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts index f32b1e25702b5..ef08f0c2c3656 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/reducer.ts @@ -10,8 +10,6 @@ import { Direction, HostsFields } from '../../graphql/types'; import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../constants'; import { - applyHostsFilterQuery, - setHostsFilterQueryDraft, setHostTablesActivePageToZero, updateHostsSort, updateTableActivePage, @@ -20,7 +18,6 @@ import { import { setHostPageQueriesActivePageToZero, setHostDetailsQueriesActivePageToZero, - setHostsQueriesActivePageToZero, } from './helpers'; import { HostsModel, HostsTableType } from './model'; @@ -49,8 +46,6 @@ export const initialHostsState: HostsState = { }, [HostsTableType.anomalies]: null, }, - filterQuery: null, - filterQueryDraft: null, }, details: { queries: { @@ -74,8 +69,6 @@ export const initialHostsState: HostsState = { }, [HostsTableType.anomalies]: null, }, - filterQuery: null, - filterQueryDraft: null, }, }; @@ -131,20 +124,4 @@ export const hostsReducer = reducerWithInitialState(initialHostsState) }, }, })) - .case(setHostsFilterQueryDraft, (state, { filterQueryDraft, hostsType }) => ({ - ...state, - [hostsType]: { - ...state[hostsType], - filterQueryDraft, - }, - })) - .case(applyHostsFilterQuery, (state, { filterQuery, hostsType }) => ({ - ...state, - [hostsType]: { - ...state[hostsType], - queries: setHostsQueriesActivePageToZero(state, hostsType), - filterQueryDraft: filterQuery.kuery, - filterQuery, - }, - })) .build(); diff --git a/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts b/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts index a4cf0715ef6da..7d00569f9a0ea 100644 --- a/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/hosts/selectors.ts @@ -7,7 +7,6 @@ import { get } from 'lodash/fp'; import { createSelector } from 'reselect'; -import { isFromKueryExpressionValid } from '../../lib/keury'; import { State } from '../reducer'; import { GenericHostsModel, HostsType, HostsTableType } from './model'; @@ -38,34 +37,3 @@ export const uncommonProcessesSelector = () => selectHosts, hosts => hosts.queries.uncommonProcesses ); - -export const hostsFilterQueryExpression = () => - createSelector( - selectHosts, - hosts => - hosts.filterQuery && hosts.filterQuery.kuery ? hosts.filterQuery.kuery.expression : null - ); - -export const hostsFilterQueryAsKuery = () => - createSelector( - selectHosts, - hosts => (hosts.filterQuery && hosts.filterQuery.kuery ? hosts.filterQuery.kuery : null) - ); - -export const hostsFilterQueryAsJson = () => - createSelector( - selectHosts, - hosts => (hosts.filterQuery ? hosts.filterQuery.serializedQuery : null) - ); - -export const hostsFilterQueryDraft = () => - createSelector( - selectHosts, - hosts => hosts.filterQueryDraft - ); - -export const isHostFilterQueryDraftValid = () => - createSelector( - selectHosts, - hosts => isFromKueryExpressionValid(hosts.filterQueryDraft) - ); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts index 8ff1dd5510d03..598b2854b96eb 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import actionCreatorFactory from 'typescript-fsa'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { InspectQuery, Refetch } from './model'; import { InputsModelId } from './constants'; @@ -67,3 +69,19 @@ export const addTimelineLinkTo = actionCreator<{ linkToId: InputsModelId }>('ADD export const removeGlobalLinkTo = actionCreator('REMOVE_GLOBAL_LINK_TO'); export const addGlobalLinkTo = actionCreator<{ linkToId: InputsModelId }>('ADD_GLOBAL_LINK_TO'); + +export const setFilterQuery = actionCreator<{ + id: InputsModelId; + query: string | { [key: string]: unknown }; + language: string; +}>('SET_FILTER_QUERY'); + +export const setSavedQuery = actionCreator<{ + id: InputsModelId; + savedQuery: SavedQuery | undefined; +}>('SET_SAVED_QUERY'); + +export const setSearchBarFilter = actionCreator<{ + id: InputsModelId; + filters: Filter[]; +}>('SET_SEARCH_BAR_FILTER'); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.test.ts b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.test.ts index 5c0a708034b1a..d23110b44ad43 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.test.ts @@ -118,7 +118,7 @@ describe('Inputs', () => { }; const newState: InputsModel = upsertQuery(newQuery); - expect(newState.global.query[0]).toEqual({ + expect(newState.global.queries[0]).toEqual({ id: 'myQuery', inspect: null, isInspected: false, @@ -144,7 +144,7 @@ describe('Inputs', () => { newQuery.state = newState; newState = upsertQuery(newQuery); - expect(newState.global.query[0]).toEqual({ + expect(newState.global.queries[0]).toEqual({ id: 'myQuery', inspect: null, isInspected: false, @@ -179,7 +179,7 @@ describe('Inputs', () => { }; const newState: InputsModel = setIsInspected(newQuery); - expect(newState.global.query[0]).toEqual({ + expect(newState.global.queries[0]).toEqual({ id: 'myQuery', inspect: null, isInspected: true, @@ -199,7 +199,7 @@ describe('Inputs', () => { }; const newState: InputsModel = setIsInspected(newQuery); - expect(newState.global.query[0]).toEqual({ + expect(newState.global.queries[0]).toEqual({ id: 'myQuery', inspect: null, isInspected: false, @@ -263,7 +263,7 @@ describe('Inputs', () => { duration: 300000, kind: 'manual', }, - query: [ + queries: [ { id: 'myQuery', inspect: null, @@ -280,6 +280,8 @@ describe('Inputs', () => { to: 1, toStr: 'now', }, + query: { query: '', language: 'kuery' }, + filters: [], }, timeline: { linkTo: ['global'], @@ -287,7 +289,7 @@ describe('Inputs', () => { duration: 300000, kind: 'manual', }, - query: [], + queries: [], timerange: { from: 0, fromStr: 'now-24h', @@ -295,6 +297,8 @@ describe('Inputs', () => { to: 1, toStr: 'now', }, + query: { query: '', language: 'kuery' }, + filters: [], }, }); }); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts index 6114f3b185a2d..194d2e8cd35c3 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts @@ -71,27 +71,27 @@ export const upsertQuery = ({ refetch, state, }: UpdateQueryParams): InputsModel => { - const queryIndex = state[inputId].query.findIndex(q => q.id === id); + const queryIndex = state[inputId].queries.findIndex(q => q.id === id); return { ...state, [inputId]: { ...get(inputId, state), - query: + queries: queryIndex > -1 ? [ - ...state[inputId].query.slice(0, queryIndex), + ...state[inputId].queries.slice(0, queryIndex), { id, inspect, - isInspected: state[inputId].query[queryIndex].isInspected, + isInspected: state[inputId].queries[queryIndex].isInspected, loading, refetch, - selectedInspectIndex: state[inputId].query[queryIndex].selectedInspectIndex, + selectedInspectIndex: state[inputId].queries[queryIndex].selectedInspectIndex, }, - ...state[inputId].query.slice(queryIndex + 1), + ...state[inputId].queries.slice(queryIndex + 1), ] : [ - ...state[inputId].query, + ...state[inputId].queries, { id, inspect, isInspected: false, loading, refetch, selectedInspectIndex: 0 }, ], }, @@ -113,21 +113,21 @@ export const setIsInspected = ({ selectedInspectIndex, state, }: SetIsInspectedParams): InputsModel => { - const myQueryIndex = state[inputId].query.findIndex(q => q.id === id); - const myQuery = myQueryIndex > -1 ? state[inputId].query[myQueryIndex] : null; + const myQueryIndex = state[inputId].queries.findIndex(q => q.id === id); + const myQuery = myQueryIndex > -1 ? state[inputId].queries[myQueryIndex] : null; return { ...state, [inputId]: { ...get(inputId, state), - query: + queries: myQueryIndex > -1 ? [ - ...state[inputId].query.slice(0, myQueryIndex), + ...state[inputId].queries.slice(0, myQueryIndex), { ...myQuery, isInspected, selectedInspectIndex }, - ...state[inputId].query.slice(myQueryIndex + 1), + ...state[inputId].queries.slice(myQueryIndex + 1), ] - : [...state[inputId].query], + : [...state[inputId].queries], }, }; }; @@ -171,18 +171,18 @@ export interface DeleteOneQueryParams { } export const deleteOneQuery = ({ inputId, id, state }: DeleteOneQueryParams): InputsModel => { - const queryIndex = state[inputId].query.findIndex(q => q.id === id); + const queryIndex = state[inputId].queries.findIndex(q => q.id === id); return { ...state, [inputId]: { ...get(inputId, state), - query: + queries: queryIndex > -1 ? [ - ...state[inputId].query.slice(0, queryIndex), - ...state[inputId].query.slice(queryIndex + 1), + ...state[inputId].queries.slice(0, queryIndex), + ...state[inputId].queries.slice(queryIndex + 1), ] - : [...state[inputId].query], + : [...state[inputId].queries], }, }; }; diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts index 623df5c67bc20..a98ea1f5d0812 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Filter } from '@kbn/es-query'; import { Dispatch } from 'redux'; +import { Query } from 'src/plugins/data/common/query'; +import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { Omit } from '../../../common/utility_types'; import { InputsModelId } from './constants'; import { CONSTANTS } from '../../components/url_state/constants'; @@ -78,8 +81,11 @@ export type GlobalQuery = GlobalGraphqlQuery | GlobalKqlQuery; export interface InputsRange { timerange: TimeRange; policy: Policy; - query: GlobalQuery[]; + queries: GlobalQuery[]; linkTo: InputsModelId[]; + query: Query; + filters: Filter[]; + savedQuery?: SavedQuery; } export interface LinkTo { diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts b/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts index 0262d1c3004e3..8f9f8809a1e86 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts @@ -23,6 +23,9 @@ import { addGlobalLinkTo, addTimelineLinkTo, deleteOneQuery, + setFilterQuery, + setSavedQuery, + setSearchBarFilter, } from './actions'; import { setIsInspected, @@ -56,12 +59,17 @@ export const initialInputsState: InputsState = { from: getDefaultFromValue(), to: getDefaultToValue(), }, - query: [], + queries: [], policy: { kind: getDefaultIntervalKind(), duration: getDefaultIntervalDuration(), }, linkTo: ['timeline'], + query: { + query: '', + language: 'kuery', + }, + filters: [], }, timeline: { timerange: { @@ -71,12 +79,17 @@ export const initialInputsState: InputsState = { from: getDefaultFromValue(), to: getDefaultToValue(), }, - query: [], + queries: [], policy: { kind: getDefaultIntervalKind(), duration: getDefaultIntervalDuration(), }, linkTo: ['global'], + query: { + query: '', + language: 'kuery', + }, + filters: [], }, }; @@ -125,7 +138,7 @@ export const inputsReducer = reducerWithInitialState(initialInputsState) ...state, [id]: { ...get(id, state), - query: state.global.query.slice(state.global.query.length), + queries: state.global.queries.slice(state.global.queries.length), }, })) .case(setQuery, (state, { inputId, id, inspect, loading, refetch }) => @@ -170,4 +183,28 @@ export const inputsReducer = reducerWithInitialState(initialInputsState) .case(addGlobalLinkTo, (state, { linkToId }) => addGlobalLink(linkToId, state)) .case(removeTimelineLinkTo, state => removeTimelineLink(state)) .case(addTimelineLinkTo, (state, { linkToId }) => addTimelineLink(linkToId, state)) + .case(setFilterQuery, (state, { id, query, language }) => ({ + ...state, + [id]: { + ...get(id, state), + query: { + query, + language, + }, + }, + })) + .case(setSavedQuery, (state, { id, savedQuery }) => ({ + ...state, + [id]: { + ...get(id, state), + savedQuery, + }, + })) + .case(setSearchBarFilter, (state, { id, filters }) => ({ + ...state, + [id]: { + ...get(id, state), + filters, + }, + })) .build(); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/selectors.ts b/x-pack/legacy/plugins/siem/public/store/inputs/selectors.ts index b1638a3c339f4..7c33c0f787694 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/selectors.ts @@ -17,7 +17,7 @@ const selectGlobal = (state: State): InputsRange => state.inputs.global; const selectTimeline = (state: State): InputsRange => state.inputs.timeline; const selectGlobalQuery = (state: State, id: string): GlobalQuery => - state.inputs.global.query.find(q => q.id === id) || { + state.inputs.global.queries.find(q => q.id === id) || { id: '', inspect: null, isInspected: false, @@ -27,8 +27,8 @@ const selectGlobalQuery = (state: State, id: string): GlobalQuery => }; const selectTimelineQuery = (state: State, id: string): GlobalQuery => - state.inputs.timeline.query.find(q => q.id === id) || - state.inputs.global.query.find(q => q.id === id) || { + state.inputs.timeline.queries.find(q => q.id === id) || + state.inputs.global.queries.find(q => q.id === id) || { id: '', inspect: null, isInspected: false, @@ -60,7 +60,7 @@ export const globalPolicySelector = createSelector( export const globalQuery = createSelector( selectGlobal, - global => global.query + global => global.queries ); export const globalQueryByIdSelector = () => @@ -81,6 +81,28 @@ export const globalSelector = () => global => global ); +export const globalQuerySelector = () => + createSelector( + selectGlobal, + global => + global.query || { + query: '', + language: 'kuery', + } + ); + +export const globalSavedQuerySelector = () => + createSelector( + selectGlobal, + global => global.savedQuery || null + ); + +export const globalFiltersQuerySelector = () => + createSelector( + selectGlobal, + global => global.filters || [] + ); + export const getTimelineSelector = () => createSelector( selectTimeline, diff --git a/x-pack/legacy/plugins/siem/public/store/network/actions.ts b/x-pack/legacy/plugins/siem/public/store/network/actions.ts index c2c71a4643e6a..17d0dd2c5695c 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/actions.ts @@ -13,7 +13,7 @@ import { TlsSortField, UsersSortField, } from '../../graphql/types'; -import { KueryFilterQuery, networkModel, SerializedFilterQuery } from '../model'; +import { networkModel } from '../model'; const actionCreator = actionCreatorFactory('x-pack/siem/local/network'); @@ -62,16 +62,6 @@ export const updateTopNFlowSort = actionCreator<{ tableType: networkModel.TopNTableType; }>('UPDATE_TOP_N_FLOW_SORT'); -export const setNetworkFilterQueryDraft = actionCreator<{ - filterQueryDraft: KueryFilterQuery; - networkType: networkModel.NetworkType; -}>('SET_NETWORK_FILTER_QUERY_DRAFT'); - -export const applyNetworkFilterQuery = actionCreator<{ - filterQuery: SerializedFilterQuery; - networkType: networkModel.NetworkType; -}>('APPLY_NETWORK_FILTER_QUERY'); - // IP Details Actions export const updateIpDetailsFlowTarget = actionCreator<{ flowTarget: FlowTarget; diff --git a/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts b/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts index 13c98eb800916..f76939c5d3e3d 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/helpers.test.ts @@ -45,8 +45,6 @@ export const mockNetworkState: NetworkModel = { isPtrIncluded: false, }, }, - filterQuery: null, - filterQueryDraft: null, }, details: { queries: { @@ -83,8 +81,6 @@ export const mockNetworkState: NetworkModel = { }, }, }, - filterQuery: null, - filterQueryDraft: null, flowTarget: FlowTarget.source, }, }; diff --git a/x-pack/legacy/plugins/siem/public/store/network/model.ts b/x-pack/legacy/plugins/siem/public/store/network/model.ts index 541a2fe1a02e3..deaca981b1e0d 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/model.ts @@ -11,7 +11,6 @@ import { TlsSortField, UsersSortField, } from '../../graphql/types'; -import { KueryFilterQuery, SerializedFilterQuery } from '../model'; export enum NetworkType { page = 'page', @@ -59,8 +58,6 @@ export interface NetworkQueries { } export interface NetworkPageModel { - filterQuery: SerializedFilterQuery | null; - filterQueryDraft: KueryFilterQuery | null; queries: NetworkQueries; } @@ -82,8 +79,6 @@ export interface IpOverviewQueries { } export interface NetworkDetailsModel { - filterQuery: SerializedFilterQuery | null; - filterQueryDraft: KueryFilterQuery | null; flowTarget: FlowTarget; queries: IpOverviewQueries; } diff --git a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts index df7d496714530..74281bc2a4a5a 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/reducer.ts @@ -17,9 +17,6 @@ import { import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../constants'; import { - applyNetworkFilterQuery, - setIpDetailsTablesActivePageToZero, - setNetworkFilterQueryDraft, setNetworkTablesActivePageToZero, updateDnsLimit, updateDnsSort, @@ -34,12 +31,11 @@ import { updateUsersLimit, updateUsersSort, } from './actions'; -import { IpDetailsTableType, NetworkModel, NetworkTableType, NetworkType } from './model'; import { setNetworkDetailsQueriesActivePageToZero, setNetworkPageQueriesActivePageToZero, - setNetworkQueriesActivePageToZero, } from './helpers'; +import { IpDetailsTableType, NetworkModel, NetworkTableType, NetworkType } from './model'; export type NetworkState = NetworkModel; @@ -72,8 +68,6 @@ export const initialNetworkState: NetworkState = { isPtrIncluded: false, }, }, - filterQuery: null, - filterQueryDraft: null, }, details: { queries: { @@ -110,8 +104,6 @@ export const initialNetworkState: NetworkState = { }, }, }, - filterQuery: null, - filterQueryDraft: null, flowTarget: FlowTarget.source, }, }; @@ -128,13 +120,6 @@ export const networkReducer = reducerWithInitialState(initialNetworkState) queries: setNetworkDetailsQueriesActivePageToZero(state), }, })) - .case(setIpDetailsTablesActivePageToZero, state => ({ - ...state, - details: { - ...state.details, - queries: setNetworkDetailsQueriesActivePageToZero(state), - }, - })) .case(updateIpDetailsTableActivePage, (state, { activePage, tableType }) => ({ ...state, [NetworkType.details]: { @@ -278,22 +263,6 @@ export const networkReducer = reducerWithInitialState(initialNetworkState) } return state; }) - .case(setNetworkFilterQueryDraft, (state, { filterQueryDraft, networkType }) => ({ - ...state, - [networkType]: { - ...state[networkType], - filterQueryDraft, - }, - })) - .case(applyNetworkFilterQuery, (state, { filterQuery, networkType }) => ({ - ...state, - [networkType]: { - ...state[networkType], - queries: setNetworkQueriesActivePageToZero(state, networkType), - filterQueryDraft: filterQuery.kuery, - filterQuery, - }, - })) .case(updateIpDetailsFlowTarget, (state, { flowTarget }) => ({ ...state, [NetworkType.details]: { diff --git a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts index c2d1fa2988e1d..9987d4d4044b3 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash/fp'; import { createSelector } from 'reselect'; -import { isFromKueryExpressionValid } from '../../lib/keury'; import { State } from '../reducer'; import { IpDetailsTableType, NetworkDetailsModel, NetworkPageModel, NetworkType } from './model'; @@ -17,9 +15,6 @@ const selectNetworkPage = (state: State): NetworkPageModel => state.network.page const selectNetworkDetails = (state: State): NetworkDetailsModel => state.network.details; -const selectNetworkByType = (state: State, networkType: NetworkType) => - get(networkType, state.network); - // Network Page Selectors export const dnsSelector = () => createSelector( @@ -50,38 +45,6 @@ export const topNFlowSelector = (flowTarget: FlowTargetSourceDest, networkType: ); }; -// Filter Query Selectors -export const networkFilterQueryAsJson = () => - createSelector( - selectNetworkByType, - network => (network.filterQuery ? network.filterQuery.serializedQuery : null) - ); - -export const networkFilterExpression = () => - createSelector( - selectNetworkByType, - network => - network.filterQuery && network.filterQuery.kuery ? network.filterQuery.kuery.expression : null - ); - -export const networkFilterQueryAsKuery = () => - createSelector( - selectNetworkByType, - network => (network.filterQuery && network.filterQuery.kuery ? network.filterQuery.kuery : null) - ); - -export const networkFilterQueryDraft = () => - createSelector( - selectNetworkByType, - network => network.filterQueryDraft - ); - -export const isNetworkFilterQueryDraftValid = () => - createSelector( - selectNetworkByType, - network => isFromKueryExpressionValid(network.filterQueryDraft) - ); - // IP Details Selectors export const ipDetailsFlowTargetSelector = () => createSelector( diff --git a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts index 005753a074dc7..eee467cd9d6d4 100644 --- a/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/store/timeline/helpers.ts @@ -121,7 +121,6 @@ export const addTimelineToStore = ({ ...timelineById, [id]: { ...timeline, - show: true, isLoading: timelineById[id].isLoading, }, }); diff --git a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.test.tsx b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.test.tsx index 9f7245a484737..de6fedb687d97 100644 --- a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.test.tsx +++ b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.test.tsx @@ -4,20 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { applyHostsFilterQuery as dispatchApplyHostsFilterQuery } from '../../store/hosts/actions'; -import { applyNetworkFilterQuery as dispatchApplyNetworkFilterQuery } from '../../store/network/actions'; import { applyKqlFilterQuery as dispatchApplyTimelineFilterQuery } from '../../store/timeline/actions'; import { mockIndexPattern } from '../../mock/index_pattern'; import { useUpdateKql } from './use_update_kql'; -import { HostsType } from '../../store/hosts/model'; -import { NetworkType } from '../../store/network/model'; const mockDispatch = jest.fn(); mockDispatch.mockImplementation(fn => fn); -const applyHostsKqlMock: jest.Mock = (dispatchApplyHostsFilterQuery as unknown) as jest.Mock; -const applyNetworkKqlMock: jest.Mock = (dispatchApplyNetworkFilterQuery as unknown) as jest.Mock; const applyTimelineKqlMock: jest.Mock = (dispatchApplyTimelineFilterQuery as unknown) as jest.Mock; jest.mock('../../store/hosts/actions', () => ({ @@ -33,110 +27,16 @@ jest.mock('../../store/timeline/actions', () => ({ describe('#useUpdateKql', () => { beforeEach(() => { mockDispatch.mockClear(); - applyHostsKqlMock.mockClear(); - applyNetworkKqlMock.mockClear(); applyTimelineKqlMock.mockClear(); }); - test('We should apply host kql on host page', () => { - useUpdateKql({ - indexPattern: mockIndexPattern, - kueryFilterQuery: { expression: '', kind: 'kuery' }, - kueryFilterQueryDraft: { expression: 'host.name: "myLove"', kind: 'kuery' }, - storeType: 'hostsType', - type: HostsType.page, - })(mockDispatch); - expect(applyHostsKqlMock).toHaveBeenCalledWith({ - filterQuery: { - kuery: { - expression: 'host.name: "myLove"', - kind: 'kuery', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"myLove"}}],"minimum_should_match":1}}', - }, - hostsType: 'page', - }); - expect(applyNetworkKqlMock).not.toHaveBeenCalled(); - expect(applyTimelineKqlMock).not.toHaveBeenCalled(); - }); - - test('We should apply host kql on host details page', () => { - useUpdateKql({ - indexPattern: mockIndexPattern, - kueryFilterQuery: { expression: '', kind: 'kuery' }, - kueryFilterQueryDraft: { expression: 'host.name: "myLove"', kind: 'kuery' }, - storeType: 'hostsType', - type: HostsType.details, - })(mockDispatch); - expect(applyHostsKqlMock).toHaveBeenCalledWith({ - filterQuery: { - kuery: { - expression: 'host.name: "myLove"', - kind: 'kuery', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"myLove"}}],"minimum_should_match":1}}', - }, - hostsType: 'details', - }); - expect(applyNetworkKqlMock).not.toHaveBeenCalled(); - expect(applyTimelineKqlMock).not.toHaveBeenCalled(); - }); - - test('We should apply network kql on network page', () => { - useUpdateKql({ - indexPattern: mockIndexPattern, - kueryFilterQuery: { expression: '', kind: 'kuery' }, - kueryFilterQueryDraft: { expression: 'host.name: "myLove"', kind: 'kuery' }, - storeType: 'networkType', - type: NetworkType.page, - })(mockDispatch); - expect(applyNetworkKqlMock).toHaveBeenCalledWith({ - filterQuery: { - kuery: { - expression: 'host.name: "myLove"', - kind: 'kuery', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"myLove"}}],"minimum_should_match":1}}', - }, - networkType: 'page', - }); - expect(applyHostsKqlMock).not.toHaveBeenCalled(); - expect(applyTimelineKqlMock).not.toHaveBeenCalled(); - }); - - test('We should apply network kql on network details page', () => { - useUpdateKql({ - indexPattern: mockIndexPattern, - kueryFilterQuery: { expression: '', kind: 'kuery' }, - kueryFilterQueryDraft: { expression: 'host.name: "myLove"', kind: 'kuery' }, - storeType: 'networkType', - type: NetworkType.details, - })(mockDispatch); - expect(applyNetworkKqlMock).toHaveBeenCalledWith({ - filterQuery: { - kuery: { - expression: 'host.name: "myLove"', - kind: 'kuery', - }, - serializedQuery: - '{"bool":{"should":[{"match_phrase":{"host.name":"myLove"}}],"minimum_should_match":1}}', - }, - networkType: 'details', - }); - expect(applyHostsKqlMock).not.toHaveBeenCalled(); - expect(applyTimelineKqlMock).not.toHaveBeenCalled(); - }); - test('We should apply timeline kql', () => { useUpdateKql({ indexPattern: mockIndexPattern, kueryFilterQuery: { expression: '', kind: 'kuery' }, kueryFilterQueryDraft: { expression: 'host.name: "myLove"', kind: 'kuery' }, storeType: 'timelineType', - type: NetworkType.page, + type: null, timelineId: 'myTimelineId', })(mockDispatch); expect(applyTimelineKqlMock).toHaveBeenCalledWith({ @@ -150,7 +50,5 @@ describe('#useUpdateKql', () => { }, id: 'myTimelineId', }); - expect(applyHostsKqlMock).not.toHaveBeenCalled(); - expect(applyNetworkKqlMock).not.toHaveBeenCalled(); }); }); diff --git a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx index 3282694f9d998..1a4ca656ba0fe 100644 --- a/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx +++ b/x-pack/legacy/plugins/siem/public/utils/kql/use_update_kql.tsx @@ -9,20 +9,16 @@ import { Dispatch } from 'redux'; import { StaticIndexPattern } from 'ui/index_patterns'; import { KueryFilterQuery } from '../../store'; -import { HostsType } from '../../store/hosts/model'; -import { applyHostsFilterQuery as dispatchApplyHostsFilterQuery } from '../../store/hosts/actions'; -import { applyNetworkFilterQuery as dispatchApplyNetworkFilterQuery } from '../../store/network/actions'; import { applyKqlFilterQuery as dispatchApplyTimelineFilterQuery } from '../../store/timeline/actions'; import { convertKueryToElasticSearchQuery } from '../../lib/keury'; import { RefetchKql } from '../../store/inputs/model'; -import { NetworkType } from '../../store/network/model'; interface UseUpdateKqlProps { indexPattern: StaticIndexPattern; kueryFilterQuery: KueryFilterQuery | null; kueryFilterQueryDraft: KueryFilterQuery | null; - storeType: 'networkType' | 'hostsType' | 'timelineType'; - type: HostsType | NetworkType | null; + storeType: 'timelineType'; + type: null; timelineId?: string; } @@ -36,42 +32,7 @@ export const useUpdateKql = ({ }: UseUpdateKqlProps): RefetchKql => { const updateKql: RefetchKql = (dispatch: Dispatch) => { if (kueryFilterQueryDraft != null && !isEqual(kueryFilterQuery, kueryFilterQueryDraft)) { - if (storeType === 'hostsType' && (type === HostsType.details || type === HostsType.page)) { - dispatch( - dispatchApplyHostsFilterQuery({ - filterQuery: { - kuery: { - kind: 'kuery', - expression: kueryFilterQueryDraft.expression, - }, - serializedQuery: convertKueryToElasticSearchQuery( - kueryFilterQueryDraft.expression, - indexPattern - ), - }, - hostsType: type, - }) - ); - } else if ( - storeType === 'networkType' && - (type === NetworkType.details || type === NetworkType.page) - ) { - dispatch( - dispatchApplyNetworkFilterQuery({ - filterQuery: { - kuery: { - kind: 'kuery', - expression: kueryFilterQueryDraft.expression, - }, - serializedQuery: convertKueryToElasticSearchQuery( - kueryFilterQueryDraft.expression, - indexPattern - ), - }, - networkType: type, - }) - ); - } else if (storeType === 'timelineType' && timelineId != null) { + if (storeType === 'timelineType' && timelineId != null) { dispatch( dispatchApplyTimelineFilterQuery({ id: timelineId, diff --git a/x-pack/legacy/plugins/siem/server/lib/hosts/query.hosts.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/hosts/query.hosts.dsl.ts index 7c2e0df535344..0e1ed8f6011c2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/hosts/query.hosts.dsl.ts +++ b/x-pack/legacy/plugins/siem/server/lib/hosts/query.hosts.dsl.ts @@ -61,6 +61,7 @@ export const buildHostsQuery = ({ track_total_hits: false, }, }; + return dslQuery; }; diff --git a/x-pack/package.json b/x-pack/package.json index de996ae30bb9a..b62a9025984ca 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -309,6 +309,7 @@ "nodemailer": "^4.7.0", "object-hash": "^1.3.1", "object-path-immutable": "^3.1.1", + "oboe": "^2.1.4", "oppsy": "^2.0.0", "papaparse": "^4.6.3", "pdfmake": "0.1.57", diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index b0a6cc84a6933..3346f2ff77036 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -96,9 +96,7 @@ export default function({ getService, getPageObjects, ...rest }: FtrProviderCont field: 'ip', }); - await PageObjects.lens.setTitle('Afancilenstest'); - - await PageObjects.lens.save(); + await PageObjects.lens.save('Afancilenstest'); // Ensure the visualization shows up in the visualize list, and takes // us back to the visualization as we configured it. diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 1825876782d15..8153a8713ca2f 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -124,16 +124,20 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont /** * Save the current Lens visualization. */ - save() { - return testSubjects.click('lnsApp_saveButton'); - }, - - setTitle(title: string) { - return testSubjects.setValue('lns_ChartTitle', title); + async save(title: string) { + await testSubjects.click('lnsApp_saveButton'); + await testSubjects.setValue('savedObjectTitle', title); + await testSubjects.click('confirmSaveSavedObjectButton'); + retry.waitForWithTimeout('Save modal to disappear', 1000, () => + testSubjects + .missingOrFail('confirmSaveSavedObjectButton') + .then(() => true) + .catch(() => false) + ); }, getTitle() { - return testSubjects.getAttribute('lns_ChartTitle', 'value'); + return testSubjects.getVisibleText('lns_ChartTitle'); }, }); } diff --git a/yarn.lock b/yarn.lock index 8567d135248f8..d9ec9a667aaed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14985,6 +14985,11 @@ http-errors@1.7.2, http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + http-parser-js@>=0.4.0: version "0.4.11" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529" @@ -20627,6 +20632,13 @@ object.values@^1.0.4, object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +oboe@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.4.tgz#20c88cdb0c15371bb04119257d4fdd34b0aa49f6" + integrity sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY= + dependencies: + http-https "^1.0.0" + obuf@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e"