From a02af13f1678ee5b98ec817fb686fe3fe16dca8f Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 15 Oct 2019 18:22:07 -0400 Subject: [PATCH 1/9] [SIEM] Phase I - Add saved query in SIEM solution (#47306) * Add Search Bar components Integration of the Search Bar component in host and network page Fix state URL with new Search Bar * update unit test * Fix URL state to match Discover + Fix ML to match with new url state + fix cypress test * fix behavior when save as new query * savedQuery - do not try to update date picker when there is no timefilter * fix refresh * some merge issue + fix back to active page to zero * review I * hack to remove lag * fix type --- .../components/query_bar_top_row.tsx | 18 +- .../components/create_search_bar.tsx | 51 +- .../search_bar/components/search_bar.tsx | 5 + .../data/public/search/search_bar/index.tsx | 2 +- .../lib/ml_conditional_links/index.ts | 84 +-- .../integration/lib/url_state/index.ts | 14 +- .../ml_conditional_links.spec.ts | 124 ++--- .../smoke_tests/url_state/url_state.spec.ts | 58 +- .../run_check_circular_deps_cli.js | 7 +- .../actions/apply_siem_filter_action.test.tsx | 92 ++-- .../actions/apply_siem_filter_action.tsx | 44 +- .../embeddables/embedded_map.test.tsx | 14 +- .../components/embeddables/embedded_map.tsx | 24 +- .../embeddables/embedded_map_helpers.test.tsx | 23 +- .../embeddables/embedded_map_helpers.tsx | 20 +- .../map_tool_tip/map_tool_tip.test.tsx | 6 + .../point_tool_tip_content.test.tsx | 6 + .../public/components/embeddables/types.ts | 6 +- .../events_viewer/events_viewer.test.tsx | 35 +- .../events_viewer/events_viewer.tsx | 22 +- .../components/events_viewer/index.test.tsx | 21 +- .../public/components/events_viewer/index.tsx | 18 +- .../public/components/events_viewer/mock.ts | 2 +- .../filters_global/filters_global.tsx | 21 +- .../public/components/inspect/index.test.tsx | 8 +- .../components/ml/api/throw_if_not_ok.test.ts | 8 +- .../add_entities_to_kql.test.ts | 44 +- .../conditional_links/add_entities_to_kql.ts | 21 +- .../ml_host_conditional_container.tsx | 31 +- .../ml_network_conditional_container.tsx | 31 +- .../remove_kql_variables.test.ts | 44 +- .../conditional_links/remove_kql_variables.ts | 8 +- .../replace_kql_commas_with_or.test.ts | 30 +- .../replace_kql_commas_with_or.ts | 8 +- .../replace_kql_parts.test.ts | 78 ++- ...e_kql_query_location_for_host_page.test.ts | 33 -- ...eplace_kql_query_location_for_host_page.ts | 21 - ...ql_query_location_for_network_page.test.ts | 32 -- ...ace_kql_query_location_for_network_page.ts | 21 - .../conditional_links/rison_helpers.test.ts | 12 +- .../navigation/breadcrumbs/index.test.ts | 16 +- .../public/components/navigation/helpers.ts | 31 +- .../components/navigation/index.test.tsx | 39 +- .../public/components/navigation/index.tsx | 91 +--- .../navigation/tab_navigation/index.test.tsx | 44 +- .../navigation/tab_navigation/types.ts | 12 +- .../components/open_timeline/helpers.ts | 7 +- .../__snapshots__/index.test.tsx.snap | 37 +- .../components/page/add_to_kql/helpers.ts | 27 + .../components/page/add_to_kql/index.test.tsx | 203 ++++--- .../components/page/add_to_kql/index.tsx | 95 ++-- .../__snapshots__/index.test.tsx.snap | 6 + .../page/hosts/hosts_table/columns.tsx | 18 +- .../page/hosts/hosts_table/index.test.tsx | 9 +- .../__snapshots__/index.test.tsx.snap | 12 + .../page/network/users_table/index.test.tsx | 8 + .../public/components/search_bar/index.tsx | 395 ++++++++++++++ .../public/components/search_bar/selectors.ts | 37 ++ .../super_date_picker/index.test.tsx | 4 +- .../components/super_date_picker/index.tsx | 40 +- .../super_date_picker/selectors.test.ts | 22 +- .../components/super_date_picker/selectors.ts | 14 +- .../header/__snapshots__/index.test.tsx.snap | 6 + .../components/timeline/helpers.test.tsx | 232 ++++---- .../public/components/timeline/helpers.tsx | 61 ++- .../public/components/timeline/timeline.tsx | 9 +- .../public/components/url_state/constants.ts | 6 +- .../public/components/url_state/helpers.ts | 67 ++- .../components/url_state/index.test.tsx | 72 +-- .../public/components/url_state/index.tsx | 80 +-- .../url_state/index_mocked.test.tsx | 31 +- .../url_state/initialize_redux_by_url.tsx | 73 +-- .../components/url_state/test_dependencies.ts | 57 +- .../siem/public/components/url_state/types.ts | 43 +- .../components/url_state/use_url_state.tsx | 69 ++- .../siem/public/containers/hosts/filter.tsx | 150 ------ .../siem/public/containers/hosts/index.tsx | 2 - .../siem/public/containers/network/filter.tsx | 149 ------ .../siem/public/containers/network/index.tsx | 7 - .../plugins/siem/public/lib/keury/index.ts | 33 +- .../plugins/siem/public/mock/global_state.ts | 22 +- .../plugins/siem/public/mock/index_pattern.ts | 6 + .../plugins/siem/public/mock/ui_settings.ts | 21 + .../public/pages/hosts/details/body.test.tsx | 57 +- .../siem/public/pages/hosts/details/body.tsx | 51 +- .../siem/public/pages/hosts/details/index.tsx | 78 ++- .../siem/public/pages/hosts/details/types.ts | 5 +- .../siem/public/pages/hosts/details/utils.ts | 30 +- .../siem/public/pages/hosts/hosts.test.tsx | 27 +- .../plugins/siem/public/pages/hosts/hosts.tsx | 57 +- .../siem/public/pages/hosts/hosts_body.tsx | 45 +- .../plugins/siem/public/pages/hosts/kql.tsx | 56 -- .../navigation/events_query_tab_body.tsx | 8 +- .../public/pages/hosts/navigation/types.ts | 1 - .../__snapshots__/ip_details.test.tsx.snap | 8 +- .../public/pages/network/ip_details.test.tsx | 28 +- .../siem/public/pages/network/ip_details.tsx | 503 +++++++++--------- .../plugins/siem/public/pages/network/kql.tsx | 56 -- .../public/pages/network/network.test.tsx | 19 +- .../siem/public/pages/network/network.tsx | 179 +++---- .../siem/public/pages/network/types.ts | 9 +- .../siem/public/store/hosts/actions.ts | 11 - .../siem/public/store/hosts/helpers.test.ts | 4 - .../plugins/siem/public/store/hosts/model.ts | 3 - .../siem/public/store/hosts/reducer.ts | 23 - .../siem/public/store/hosts/selectors.ts | 32 -- .../siem/public/store/inputs/actions.ts | 18 + .../siem/public/store/inputs/helpers.test.ts | 16 +- .../siem/public/store/inputs/helpers.ts | 36 +- .../plugins/siem/public/store/inputs/model.ts | 8 +- .../siem/public/store/inputs/reducer.ts | 43 +- .../siem/public/store/inputs/selectors.ts | 30 +- .../siem/public/store/network/actions.ts | 12 +- .../siem/public/store/network/helpers.test.ts | 4 - .../siem/public/store/network/model.ts | 5 - .../siem/public/store/network/reducer.ts | 33 +- .../siem/public/store/network/selectors.ts | 37 -- .../siem/public/store/timeline/helpers.ts | 1 - .../public/utils/kql/use_update_kql.test.tsx | 104 +--- .../siem/public/utils/kql/use_update_kql.tsx | 45 +- .../siem/server/lib/hosts/query.hosts.dsl.ts | 1 + 121 files changed, 2506 insertions(+), 2687 deletions(-) delete mode 100644 x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.test.ts delete mode 100644 x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_host_page.ts delete mode 100644 x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.test.ts delete mode 100644 x-pack/legacy/plugins/siem/public/components/ml/conditional_links/replace_kql_query_location_for_network_page.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/page/add_to_kql/helpers.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts delete mode 100644 x-pack/legacy/plugins/siem/public/containers/hosts/filter.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/containers/network/filter.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/containers/network/index.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/pages/hosts/kql.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/pages/network/kql.tsx 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/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/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_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/containers/network/index.tsx b/x-pack/legacy/plugins/siem/public/containers/network/index.tsx deleted file mode 100644 index ca06a9c21cddb..0000000000000 --- a/x-pack/legacy/plugins/siem/public/containers/network/index.tsx +++ /dev/null @@ -1,7 +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. - */ - -export { NetworkFilter } from './filter'; 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; }; From 3cc0dbf59b15c61d970a261a814e66fd9f7ea19b Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 15 Oct 2019 17:26:07 -0500 Subject: [PATCH 2/9] Refactoring Functions to i18n; adding missing public functions (#48144) * Refactoring and adding new functions * Fixing TS problem * Clean-up * Missed a commit * Fixing TS refactor screw-ups. Ugh. * Tweak: i18n/function -> i18n * Merge left the old file there --- .../canvas_plugin_src/elements/index.ts | 2 +- .../functions/browser/location.ts | 2 +- .../functions/browser/markdown.ts | 2 +- .../functions/browser/urlparam.ts | 2 +- .../functions/common/__tests__/compare.js | 2 +- .../functions/common/__tests__/progress.js | 2 +- .../canvas_plugin_src/functions/common/all.ts | 2 +- .../functions/common/alterColumn.test.js | 2 +- .../functions/common/alterColumn.ts | 2 +- .../canvas_plugin_src/functions/common/any.ts | 2 +- .../canvas_plugin_src/functions/common/as.ts | 2 +- .../functions/common/axisConfig.ts | 2 +- .../functions/common/axis_config.test.js | 2 +- .../functions/common/case.ts | 2 +- .../functions/common/clear.ts | 2 +- .../functions/common/columns.ts | 2 +- .../functions/common/compare.ts | 2 +- .../functions/common/containerStyle.ts | 2 +- .../functions/common/container_style.test.js | 2 +- .../functions/common/context.ts | 2 +- .../functions/common/csv.test.js | 2 +- .../canvas_plugin_src/functions/common/csv.ts | 2 +- .../functions/common/date.test.js | 2 +- .../functions/common/date.ts | 2 +- .../canvas_plugin_src/functions/common/do.ts | 2 +- .../functions/common/dropdownControl.ts | 2 +- .../canvas_plugin_src/functions/common/eq.ts | 2 +- .../functions/common/exactly.ts | 2 +- .../functions/common/filterrows.ts | 2 +- .../functions/common/formatdate.ts | 2 +- .../functions/common/formatnumber.ts | 2 +- .../functions/common/getCell.test.js | 2 +- .../functions/common/getCell.ts | 2 +- .../canvas_plugin_src/functions/common/gt.ts | 2 +- .../canvas_plugin_src/functions/common/gte.ts | 2 +- .../functions/common/head.ts | 2 +- .../canvas_plugin_src/functions/common/if.ts | 2 +- .../functions/common/image.ts | 2 +- .../functions/common/join_rows.test.js | 2 +- .../functions/common/join_rows.ts | 2 +- .../canvas_plugin_src/functions/common/lt.ts | 2 +- .../canvas_plugin_src/functions/common/lte.ts | 2 +- .../functions/common/mapColumn.ts | 2 +- .../functions/common/math.test.js | 2 +- .../functions/common/math.ts | 2 +- .../functions/common/metric.ts | 2 +- .../canvas_plugin_src/functions/common/neq.ts | 2 +- .../functions/common/palette.ts | 2 +- .../canvas_plugin_src/functions/common/pie.ts | 2 +- .../functions/common/plot/index.ts | 2 +- .../functions/common/ply.test.js | 2 +- .../canvas_plugin_src/functions/common/ply.ts | 2 +- .../functions/common/progress.ts | 2 +- .../functions/common/render.ts | 2 +- .../functions/common/repeatImage.ts | 2 +- .../functions/common/replace.ts | 2 +- .../functions/common/revealImage.ts | 2 +- .../functions/common/reveal_image.test.js | 2 +- .../functions/common/rounddate.ts | 2 +- .../functions/common/rowCount.ts | 2 +- .../functions/common/saved_map.ts | 2 +- .../functions/common/saved_search.ts | 2 +- .../functions/common/saved_visualization.ts | 2 +- .../functions/common/seriesStyle.ts | 2 +- .../functions/common/shape.ts | 2 +- .../functions/common/sort.ts | 2 +- .../functions/common/staticColumn.ts | 2 +- .../functions/common/string.ts | 2 +- .../functions/common/switch.ts | 2 +- .../functions/common/table.ts | 2 +- .../functions/common/tail.ts | 2 +- .../functions/common/timefilter.test.js | 2 +- .../functions/common/timefilter.ts | 2 +- .../functions/common/timefilterControl.ts | 2 +- .../server/demodata/get_demo_rows.ts | 2 +- .../functions/server/demodata/index.ts | 2 +- .../functions/server/escount.ts | 2 +- .../functions/server/esdocs.ts | 2 +- .../functions/server/essql.ts | 2 +- .../functions/server/pointseries/index.ts | 2 +- .../strings/functions/function_errors.ts | 41 --- .../strings/functions/function_help.ts | 228 ----------------- .../axis_config/extended_template.tsx | 2 +- .../uis/arguments/axis_config/index.ts | 2 +- .../uis/arguments/datacolumn/index.js | 2 +- .../datacolumn/simple_math_function.js | 2 +- .../uis/arguments/date_format/index.ts | 2 +- .../uis/arguments/filter_group.js | 2 +- .../uis/arguments/image_upload/forms/file.js | 2 +- .../uis/arguments/image_upload/forms/link.js | 2 +- .../uis/arguments/image_upload/index.js | 2 +- .../canvas_plugin_src/uis/arguments/number.js | 2 +- .../uis/arguments/number_format/index.ts | 2 +- .../uis/arguments/palette.js | 2 +- .../uis/arguments/percentage.js | 2 +- .../canvas_plugin_src/uis/arguments/range.js | 2 +- .../canvas_plugin_src/uis/arguments/select.js | 2 +- .../canvas_plugin_src/uis/arguments/shape.js | 2 +- .../canvas_plugin_src/uis/arguments/string.js | 2 +- .../uis/arguments/textarea.js | 2 +- .../canvas_plugin_src/uis/arguments/toggle.js | 2 +- .../uis/datasources/demodata.js | 3 +- .../uis/datasources/essql.js | 2 +- .../uis/datasources/timelion.js | 3 +- .../canvas_plugin_src/uis/models/math.js | 2 +- .../uis/models/point_series.js | 2 +- .../uis/transforms/formatdate.ts | 2 +- .../uis/transforms/formatnumber.ts | 2 +- .../uis/transforms/rounddate.ts | 2 +- .../canvas_plugin_src/uis/transforms/sort.js | 2 +- .../uis/views/dropdownControl.js | 2 +- .../canvas_plugin_src/uis/views/getCell.js | 2 +- .../canvas_plugin_src/uis/views/image.js | 2 +- .../canvas_plugin_src/uis/views/markdown.js | 2 +- .../canvas_plugin_src/uis/views/metric.js | 2 +- .../canvas/canvas_plugin_src/uis/views/pie.js | 2 +- .../canvas_plugin_src/uis/views/plot.js | 2 +- .../canvas_plugin_src/uis/views/progress.js | 2 +- .../canvas_plugin_src/uis/views/render.js | 2 +- .../uis/views/repeatImage.js | 2 +- .../uis/views/revealImage.js | 2 +- .../canvas_plugin_src/uis/views/shape.js | 2 +- .../canvas_plugin_src/uis/views/table.js | 2 +- .../uis/views/timefilterControl.js | 2 +- .../legacy/plugins/canvas/i18n/constants.ts | 1 + .../elements}/apply_strings.ts | 2 +- .../elements}/element_strings.test.ts | 4 +- .../elements}/element_strings.ts | 0 .../strings => i18n/elements}/index.ts | 2 - .../plugins/canvas/i18n/expression_types.ts | 2 +- .../functions => i18n/functions/dict}/all.ts | 6 +- .../functions/dict/alter_column.ts} | 4 +- .../functions => i18n/functions/dict}/any.ts | 6 +- .../functions => i18n/functions/dict}/as.ts | 6 +- .../canvas/i18n/functions/dict/asset.ts | 34 +++ .../functions/dict/axis_config.ts} | 6 +- .../functions => i18n/functions/dict}/case.ts | 6 +- .../functions/dict}/clear.ts | 6 +- .../functions/dict}/columns.ts | 6 +- .../functions/dict}/compare.ts | 6 +- .../functions/dict/container_style.ts} | 6 +- .../functions/dict}/context.ts | 6 +- .../functions => i18n/functions/dict}/csv.ts | 6 +- .../functions => i18n/functions/dict}/date.ts | 6 +- .../functions/dict}/demodata.ts | 6 +- .../functions => i18n/functions/dict}/do.ts | 6 +- .../functions/dict/dropdown_control.ts} | 4 +- .../functions => i18n/functions/dict}/eq.ts | 6 +- .../functions/dict}/escount.ts | 6 +- .../functions/dict}/esdocs.ts | 6 +- .../functions/dict}/essql.ts | 6 +- .../functions/dict}/exactly.ts | 4 +- .../functions/dict}/filterrows.ts | 6 +- .../canvas/i18n/functions/dict/filters.ts | 25 ++ .../functions/dict}/formatdate.ts | 6 +- .../functions/dict}/formatnumber.ts | 6 +- .../functions/dict/get_cell.ts} | 6 +- .../functions => i18n/functions/dict}/gt.ts | 6 +- .../functions => i18n/functions/dict}/gte.ts | 6 +- .../functions => i18n/functions/dict}/head.ts | 6 +- .../functions => i18n/functions/dict}/if.ts | 6 +- .../functions/dict}/image.ts | 6 +- .../functions/dict}/join_rows.ts | 4 +- .../functions/dict}/location.ts | 4 +- .../functions => i18n/functions/dict}/lt.ts | 6 +- .../functions => i18n/functions/dict}/lte.ts | 6 +- .../functions/dict/map_column.ts} | 6 +- .../functions/dict}/markdown.ts | 6 +- .../functions => i18n/functions/dict}/math.ts | 6 +- .../functions/dict}/metric.ts | 6 +- .../functions => i18n/functions/dict}/neq.ts | 6 +- .../functions/dict}/palette.ts | 4 +- .../functions => i18n/functions/dict}/pie.ts | 6 +- .../functions => i18n/functions/dict}/plot.ts | 6 +- .../functions => i18n/functions/dict}/ply.ts | 6 +- .../functions/dict}/pointseries.ts | 6 +- .../functions/dict}/progress.ts | 8 +- .../functions/dict}/render.ts | 6 +- .../functions/dict/repeat_image.ts} | 6 +- .../functions/dict}/replace.ts | 6 +- .../functions/dict/reveal_image.ts} | 6 +- .../functions/dict}/rounddate.ts | 6 +- .../functions/dict/row_count.ts} | 4 +- .../functions/dict}/saved_map.ts | 4 +- .../functions/dict}/saved_search.ts | 4 +- .../functions/dict}/saved_visualization.ts | 4 +- .../functions/dict/series_style.ts} | 4 +- .../functions/dict}/shape.ts | 6 +- .../functions => i18n/functions/dict}/sort.ts | 6 +- .../functions/dict/static_column.ts} | 4 +- .../functions/dict}/string.ts | 4 +- .../functions/dict}/switch.ts | 6 +- .../functions/dict}/table.ts | 6 +- .../functions => i18n/functions/dict}/tail.ts | 6 +- .../functions/dict}/timefilter.ts | 6 +- .../functions/dict/timefilter_control.ts} | 4 +- .../canvas/i18n/functions/dict/timelion.ts | 45 ++++ .../plugins/canvas/i18n/functions/dict/to.ts | 34 +++ .../functions/dict}/urlparam.ts | 6 +- .../canvas/i18n/functions/function_errors.ts | 45 ++++ .../canvas/i18n/functions/function_help.ts | 236 ++++++++++++++++++ .../strings => i18n}/functions/index.ts | 0 x-pack/legacy/plugins/canvas/i18n/index.ts | 3 + .../{canvas_plugin_src/strings => i18n}/ui.ts | 2 +- .../expression_types/datasources/esdocs.js | 4 +- .../plugins/canvas/public/functions/asset.js | 35 --- .../plugins/canvas/public/functions/asset.ts | 47 ++++ .../canvas/public/functions/filters.js | 68 ----- .../canvas/public/functions/filters.ts | 83 ++++++ .../public/functions/{index.js => index.ts} | 0 .../canvas/public/functions/timelion.js | 99 -------- .../canvas/public/functions/timelion.ts | 113 +++++++++ .../plugins/canvas/public/functions/to.js | 30 --- .../plugins/canvas/public/functions/to.ts | 42 ++++ .../canvas/public/state/selectors/assets.ts | 4 +- .../legacy/plugins/canvas/types/functions.ts | 15 +- 216 files changed, 1028 insertions(+), 826 deletions(-) delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_errors.ts delete mode 100644 x-pack/legacy/plugins/canvas/canvas_plugin_src/strings/functions/function_help.ts rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n/elements}/apply_strings.ts (95%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n/elements}/element_strings.test.ts (90%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n/elements}/element_strings.ts (100%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n/elements}/index.ts (85%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/all.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/alterColumn.ts => i18n/functions/dict/alter_column.ts} (93%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/any.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/as.ts (82%) create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/dict/asset.ts rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/axisConfig.ts => i18n/functions/dict/axis_config.ts} (94%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/case.ts (90%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/clear.ts (77%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/columns.ts (86%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/compare.ts (93%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/containerStyle.ts => i18n/functions/dict/container_style.ts} (94%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/context.ts (80%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/csv.ts (88%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/date.ts (90%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/demodata.ts (83%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/do.ts (86%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/dropdownControl.ts => i18n/functions/dict/dropdown_control.ts} (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/eq.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/escount.ts (84%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/esdocs.ts (92%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/essql.ts (87%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/exactly.ts (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/filterrows.ts (87%) create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/dict/filters.ts rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/formatdate.ts (84%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/formatnumber.ts (84%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/getCell.ts => i18n/functions/dict/get_cell.ts} (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/gt.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/gte.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/head.ts (83%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/if.ts (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/image.ts (90%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/join_rows.ts (91%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/location.ts (86%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/lt.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/lte.ts (83%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/mapColumn.ts => i18n/functions/dict/map_column.ts} (86%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/markdown.ts (88%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/math.ts (93%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/metric.ts (92%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/neq.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/palette.ts (90%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/pie.ts (95%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/plot.ts (94%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/ply.ts (92%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/pointseries.ts (91%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/progress.ts (92%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/render.ts (88%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/repeatImage.ts => i18n/functions/dict/repeat_image.ts} (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/replace.ts (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/revealImage.ts => i18n/functions/dict/reveal_image.ts} (91%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/rounddate.ts (85%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/rowCount.ts => i18n/functions/dict/row_count.ts} (84%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/saved_map.ts (82%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/saved_search.ts (81%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/saved_visualization.ts (80%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/seriesStyle.ts => i18n/functions/dict/series_style.ts} (93%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/shape.ts (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/sort.ts (86%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/staticColumn.ts => i18n/functions/dict/static_column.ts} (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/string.ts (85%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/switch.ts (87%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/table.ts (91%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/tail.ts (83%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/timefilter.ts (89%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions/timefilterControl.ts => i18n/functions/dict/timefilter_control.ts} (88%) create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/dict/timelion.ts create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/dict/to.ts rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings/functions => i18n/functions/dict}/urlparam.ts (88%) create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/function_errors.ts create mode 100644 x-pack/legacy/plugins/canvas/i18n/functions/function_help.ts rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n}/functions/index.ts (100%) rename x-pack/legacy/plugins/canvas/{canvas_plugin_src/strings => i18n}/ui.ts (99%) delete mode 100644 x-pack/legacy/plugins/canvas/public/functions/asset.js create mode 100644 x-pack/legacy/plugins/canvas/public/functions/asset.ts delete mode 100644 x-pack/legacy/plugins/canvas/public/functions/filters.js create mode 100644 x-pack/legacy/plugins/canvas/public/functions/filters.ts rename x-pack/legacy/plugins/canvas/public/functions/{index.js => index.ts} (100%) delete mode 100644 x-pack/legacy/plugins/canvas/public/functions/timelion.js create mode 100644 x-pack/legacy/plugins/canvas/public/functions/timelion.ts delete mode 100644 x-pack/legacy/plugins/canvas/public/functions/to.js create mode 100644 x-pack/legacy/plugins/canvas/public/functions/to.ts 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. From dc7bf3dce319070b6c199fff036c821337501f14 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Tue, 15 Oct 2019 16:26:33 -0600 Subject: [PATCH 3/9] [Maps][File upload] Parse geojson files in chunks to avoid thread blocking (#46710) * Add file parse chunking, update component on progress * Clean up clean and validate and redo to process single features * Add oboe dependency * Prevent state updates on cancel * Handle new files added mid-way through parsing another file * Fix issue where subsequent index name is wiped out when previous file cancelled * Remove unneeded oboe abort logic * Dice parsing logic up further for testing * Clean up * Revert "Fix issue where subsequent index name is wiped out when previous file cancelled" (covered in separate PR) This reverts commit 0688e73ffce56c062a8b21ba27bf59d1f5ebfc7f. * Update file parse test to focus on different stream states * Update clean and validate tests to reflect function input/output changes * Bump up file buffer. Simplify ui update logic, not neceesary to throttle with less frequent callbacks * Show features parsed on UI rather than percentage * Remove extra mock reset * Review feedback. Add localized feature tracking callback * Review feedback. Add comment explaining progress update throttling. Also, use debounce to throttle * Remove console log * Consolidate feature handling into one function passed to oboeStream node * Abstract oboe logic to separate class and import for use in file parser * Update file parser test to mock PatternReader import * Prevent file parse active flag from resetting if another file is in progress * Don't pass back result if no features found on complete, throw error with feedback. Add clean-up for prev PatternReader * Use singleton version of jsts reader & writer. Pass back unmodified feature if clean returns nothing * Make fileHandler function async * Return null if no geometry * Handle single features differently. Fixes functional test error * Update jest test to use unique instances & counts of readers * Review feedback * Review feedback * Review feedback. Add error-handling for null geom * Fix i18n error * Clean up handling of cancelled/replaced files to account for changed fileHandler return type --- .../components/json_index_file_picker.js | 104 ++++++++++-- .../file_upload/public/util/file_parser.js | 146 ++++++++++++---- .../public/util/file_parser.test.js | 157 +++++++++++++----- .../util/geo_json_clean_and_validate.js | 66 +++----- .../util/geo_json_clean_and_validate.test.js | 15 +- .../file_upload/public/util/pattern_reader.js | 66 ++++++++ x-pack/package.json | 1 + yarn.lock | 12 ++ 8 files changed, 432 insertions(+), 135 deletions(-) create mode 100644 x-pack/legacy/plugins/file_upload/public/util/pattern_reader.js 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/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/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" From 7bb1a6eb7427efc1ff83ae4d293dc2028ecb3843 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Tue, 15 Oct 2019 16:29:17 -0600 Subject: [PATCH 4/9] Remove beta badge logic for file upload source card (#48291) --- .../source_select/_source_select.scss | 4 ---- .../source_select/source_select.js | 21 +------------------ .../client_file_source/geojson_file_source.js | 1 - 3 files changed, 1 insertion(+), 25 deletions(-) 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 } From 9a13f26b73f600f1b4a987d660cb59f2d1873703 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Tue, 15 Oct 2019 16:33:01 -0600 Subject: [PATCH 5/9] [SIEM] Updates Network Map layer styles (#48284) ## Summary Updates map styles as outlined by design in the below issue. Resolves https://github.com/elastic/kibana/issues/47046 ##### New Styles Light Mode: Screen Shot 2019-10-15 at 14 01 20 ##### New Styles Dark Mode: Screen Shot 2019-10-15 at 14 12 31 ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. - [ ] ~This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~ - [ ] ~Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~ - [ ] ~[Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~ * @benskelker - will we need to update the map screenshots as part of this release? - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios - [ ] ~This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~ ### For maintainers - [ ] ~This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~ - [ ] ~This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~ --- .../components/embeddables/__mocks__/mock.ts | 82 +++++++++++++------ .../components/embeddables/map_config.ts | 82 +++++++++++++------ 2 files changed, 112 insertions(+), 52 deletions(-) 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/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', From 197a63b0f68a657879f864ce357ca5807febf23b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 16 Oct 2019 00:33:37 +0200 Subject: [PATCH 6/9] [Logs UI] Add ML job status callouts to results page (#47642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Amend outer page / panel structure * Remove unused imports * Add anomalies section and overall chart * Add legend * Rename dataSet to partition * Add max bucket anomaly score * Move job and setup status types to common * Move initial job status fetching into page content * Poll for job status on results page * Add table / expanded row charts * Add bucket span text * Add stat section to expanded rows * Amend annotations on overall graph * Add rule to account for EuiFlexItem edge case * Move functions that handle derivations of data to a new file * Tweak data points fetched * Style bars in grey for anomalies charts * Add severity scoring to annotations * Fix default * Remove decimal places from anomaly score representations * Show all partitions and overall anomaly score in annotation tooltip for overall chart * Handle 'unknown' to workaround lack of '' suuport in tables * Add stats section to overall anomalies section * Base x-domain off the series so that certain buckets aren't omitted * Tweak colours and DRY up annotation rendering * Add sorting to table * Add "number of logs" to API results and render in UI stats * Track and render out-of-sync job configurations * Adjust translation labels * Add stopped state callout * Add more callout icons * Fix api integration tests * Use "pretty" numbers for "Number of logs" stats * Improve status message wording * Change recreate job button color back to default * Add toolbar text * Format all y axis values to 3 digits * Remove "Overall anomaly score" and change all wording / calculations to "Max anomaly scores" * Sort anomaly maximum scores for the overall chart tooltip * Remove unused translations * Use white text with badge in toolbar * Factor out a job recreation callout * Replace `filter()[0]` with `find()` call * Amend key * Use Math.round and introduce a formatAnomalyScore helper function * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/log_rate/index.tsx Co-Authored-By: Felix Stürmer * Format y-axis of log entry rate chart the same as anomalies charts * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Remove grow prop * Update x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/common/http_api/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/page_results_content.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/expanded_row.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/anomalies/index.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/public/pages/logs/analysis/sections/helpers/data_formatters.tsx Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/lib/log_analysis/log_analysis.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Update x-pack/legacy/plugins/infra/server/routes/log_analysis/results/log_entry_rate.ts Co-Authored-By: Felix Stürmer * Change path * Amend property name * Use proper default value argument * Use Kibana dateFormat setting for toolbar formatting * Change logic for calculating severity score categories * Add missing translations * Add interface to top of file * Add no-wrap to tooltip * Use more idomatic code * Use static value for series styles * Move the callouts into the right location * Fix linter warning * Add non-functional ML link button * Fix merge mistake --- .../infra/common/log_analysis/log_analysis.ts | 47 +++- .../logging/log_analysis_job_status/index.ts | 7 + .../job_configuration_outdated_callout.tsx | 29 +++ .../job_definition_outdated_callout.tsx | 29 +++ .../job_stopped_callout.tsx | 24 ++ .../log_analysis_job_problem_indicator.tsx | 29 +++ .../recreate_job_callout.tsx | 24 ++ .../logs/log_analysis/api/ml_api_types.ts | 16 ++ .../api/ml_get_jobs_summary_api.ts | 9 +- .../logs/log_analysis/api/ml_get_module.ts | 42 ++++ .../log_analysis/api/ml_setup_module_api.ts | 45 ++-- .../logs/log_analysis/log_analysis_jobs.tsx | 87 +++++-- .../log_analysis/log_analysis_setup_state.tsx | 25 +- .../log_analysis_status_state.tsx | 233 +++++++++++++++--- .../pages/logs/analysis/page_content.tsx | 13 +- .../logs/analysis/page_results_content.tsx | 26 +- .../logs/analysis/page_setup_content.tsx | 18 +- .../analysis/sections/anomalies/index.tsx | 85 ++++--- .../setup/recreate_ml_jobs_button.tsx | 29 +++ .../pages/logs/analysis/setup/steps/index.tsx | 36 ++- .../analysis/setup/steps/setup_process.tsx | 18 +- 21 files changed, 721 insertions(+), 150 deletions(-) create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_configuration_outdated_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_definition_outdated_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/job_stopped_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/log_analysis_job_problem_indicator.tsx create mode 100644 x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/recreate_job_callout.tsx create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_api_types.ts create mode 100644 x-pack/legacy/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts create mode 100644 x-pack/legacy/plugins/infra/public/pages/logs/analysis/setup/recreate_ml_jobs_button.tsx 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/infra/public/components/logging/log_analysis_job_status/index.ts b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts new file mode 100644 index 0000000000000..06229a26afd19 --- /dev/null +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_analysis_job_status/index.ts @@ -0,0 +1,7 @@ +/* + * 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 * 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' ? ( + ) : ( )} From 0ed925b0775e19c2f734770a6ce9f24ddccc9790 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 15 Oct 2019 16:07:31 -0700 Subject: [PATCH 7/9] [DOCS] Fix missing attribute (#48298) --- docs/developer/core/development-elasticsearch.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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]] From 1354369a450318960b78b100cd2e7b20ebbbcc75 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 15 Oct 2019 18:24:16 -0500 Subject: [PATCH 8/9] [Code] Add highlightLine prop to CodeBlock (#48230) * CodeBlock accepts an array of strings, rather than a single string Rather than do the splitting up of lines (for highlighting, numbering) internally, it makes a bit more sense to have the consumer provide an array of strings to be rendered. The biggest win here is the disambiguation of our upcoming `highlightLine` prop: were we to accept an array of line indices, it's unclear whether those should correspond to the monaco index (1-based), internal index (0-based), or formatted (passed through `lineNumber)`. Better to standardize on the lineIndex argument parameter already used for lineNumber, and simply ask the consumer to return a boolean for any given line. * Add highlightLine prop to CodeBlock Allows consumers to declare which lines should be highlighted with the more subtle, full-width coloring. * Refactors decoration generation into private methods * Simplifies both lineNumber and highlightLine to be invoked with _just_ the lineIndex, as consumers will now have the array to index into themselves, if necessary. * Simplify CSS related to line highlighting Because of the way we're currently using Monaco, we need to apply all three of these options to our line decorations. However, all we really need to do is set the background-color. As such, we can remove these redundant/unused css classes and reduce the noise around this functionality. Also, BEM. * Remove errant CSS rule This selector is meant to move the folding button over to account for the extra width taken by the Blame sidebar. However, `.code-line-decoration` is the only class that is applied to the blame view, and the selector in question was in fact incorrectly moving the folding button off the screen on any foldable line that was also highlighted. The bug was fixed in the previous commit that removed this class, but this was the last mention of it. * Update mock data to depend on Typescript-Node-Starter repo So that we don't have to import a non-standard repo to view the full functionality of this page. --- .../components/code_block/code_block.tsx | 101 +++++++----- .../components/editor/references_panel.tsx | 2 +- .../public/components/integrations/data.ts | 152 +++++++++--------- .../public/components/integrations/index.tsx | 3 +- .../components/search_page/code_result.tsx | 3 +- .../code/public/monaco/monaco_helper.ts | 4 +- .../public/monaco/override_monaco_styles.scss | 1 - .../plugins/code/public/style/_monaco.scss | 15 +- 8 files changed, 140 insertions(+), 141 deletions(-) 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; From 2c02458ee872d845c54fc901978d632c77ee9416 Mon Sep 17 00:00:00 2001 From: Chris Davies Date: Tue, 15 Oct 2019 19:40:30 -0400 Subject: [PATCH 9/9] [Lens] Add save modal to Lens (#48013) --- .../lens/public/app_plugin/app.test.tsx | 132 +++++++++++++++--- .../plugins/lens/public/app_plugin/app.tsx | 94 ++++++++----- .../_workspace_panel_wrapper.scss | 11 +- .../editor_frame/editor_frame.tsx | 2 +- .../editor_frame/workspace_panel_wrapper.tsx | 19 +-- .../test/functional/apps/lens/smokescreen.ts | 4 +- .../test/functional/page_objects/lens_page.ts | 18 ++- 7 files changed, 183 insertions(+), 97 deletions(-) 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 ef53f6046f266..640f1fdb99a21 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, @@ -28,9 +28,10 @@ import { NativeRenderer } from '../native_renderer'; interface State { isLoading: boolean; - isDirty: boolean; + isSaveModalVisible: boolean; indexPatternsForTopNav: IndexPatternInstance[]; persistedDoc?: Document; + lastKnownDoc?: Document; // Properties needed to interface with TopNav dateRange: { @@ -67,9 +68,8 @@ export function App({ const [state, setState] = useState({ isLoading: !!docId, - isDirty: false, + isSaveModalVisible: false, indexPatternsForTopNav: [], - query: { query: '', language }, dateRange: { fromDate: timeDefaults.from, @@ -78,7 +78,7 @@ export function App({ filters: [], }); - const lastKnownDocRef = useRef(undefined); + const { lastKnownDoc } = state; useEffect(() => { const subscription = dataShim.filter.filterManager.getUpdates$().subscribe({ @@ -124,6 +124,7 @@ export function App({ ...s, isLoading: false, persistedDoc: doc, + lastKnownDoc: doc, query: doc.state.query, filters: doc.state.filters, indexPatternsForTopNav: indexPatterns, @@ -139,7 +140,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', }) ); @@ -149,10 +150,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 }) => @@ -177,32 +175,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(() => { - 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', @@ -278,10 +256,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 @@ -307,6 +283,48 @@ 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(() => { + 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', + })} + /> + )} ); @@ -321,7 +339,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/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index a262d5b14cbf7..c511fcd997217 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -99,9 +99,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'); }, }); }